简体   繁体   English

如何在运行时计算Django模型字段?

[英]How to make a Django model fields calculated at runtime?

I have a model: 我有一个模特:

class Person (models.Model):
    name     = models.CharField ()
    birthday = models.DateField ()
    age      = models.IntegerField ()

I want to make age field to behave like a property: 我想让age字段表现得像一个属性:

    def get_age (self):
        return (datetime.datetime.now() - self.birthday).days // 365

    age = property (get_age)

but at the same time I need age to be a true field, so I can find it in Person._meta.fields , and assign attributes to it: age.help_text = "Age of the person" , etc. 但同时我需要age才能成为真正的领域,所以我可以在Person._meta.fields找到它,并为其指定属性: age.help_text = "Age of the person"等。

Obviously I cannot just override Person.save() method to calculate and store age in the database, because it inevitably will become wrong later (in fact, it shouldn't be stored in the database at all). 显然我不能只重写Person.save()方法来计算和存储数据库中的age ,因为它以后不可避免地会出错(事实上,它根本不应该存储在数据库中)。

Actually, I don't need to have setters now, but a nice solution must have setting feature. 实际上,我现在不需要设置setter,但是一个好的解决方案必须具有设置功能。

Is it possible in Django, or probably there is a more pythonic and djangoic approach to my problem? 是否有可能在Django,或者可能有更多的pythonic和djangoic方法来解决我的问题?

you're going about it in a strange way. 你是以一种奇怪的方式去做的。 you only need to store the birthday (which you're already doing). 你只需要存储生日(你已经在做)。

if you need to do queries based on age, take in the age, and then search on the birthdays that fit the requirements. 如果您需要根据年龄进行查询,请考虑年龄,然后搜索符合要求的生日。

from datetime import datetime

# get people over 50
age = 50 # in years
age_ago = datetime.now() - age # use timedelta i don't know the syntax off the top of my head
Person.objects.filter(birthday__lte=age_ago) # people whose birthday is before fifty years ago

you said it yourself "[age] shouldn't be stored in the database at all" 你自己说过“[年龄]根本不应该存储在数据库中”

you're right. 你是对的。 there is no reason to have the age field... just the property will be fine. 没有理由拥有年龄领域...只是财产将没事。

Another way to go about it which might be simpler, is to use custom form in the admin model. 另一种可能更简单的方法是在管理模型中使用自定义表单。 Something like this: 像这样的东西:

class Person (models.Model):
    birthday = models.DateField()

class PersonForm(forms.ModelForm):
    age = forms.IntegerField() # This will not be in the database
    class Meta:
        model = Person
    def __init__(self, *args, **kwargs):
        # verify instance was passed
        instance = kwargs.get('instance')
        if instance:
            self.base_fields['age'].initial = (datetime.datetime.now() - self.birthday).days
        super(PersonForm, self).__init__(*args, **kwargs)

class FooAdmin(admin.ModelAdmin):
    form = PersonForm
    # Save the age in the birthday field
    def save_model(self, request, obj, form, change):
       obj.birthday = datetime.datetime.now() + form.cleaned_data['age']
       obj.save()

I think the solution is to create a custom widget for the birthday field. 我认为解决方案是为生日字段创建自定义窗口小部件。 All you want is to change how the birthday is displayed and how it is edited. 您只想更改生日的显示方式和编辑方式。 that is exactly the widget purpose. 这正是小部件的目的。

Sorry I don't have the code for that, but here is a good place to start: 对不起,我没有相应的代码,但这是一个很好的起点:

https://docs.djangoproject.com/en/dev/ref/forms/widgets/ https://docs.djangoproject.com/en/dev/ref/forms/widgets/

You can achieve this by using django-computed-property . 您可以使用django-computed-property实现此目的。

You first have to pip install django-computed-property with the following command: 首先必须使用以下命令pip install django-computed-property

pip install django-computed-property

You then add computed_property to your list of INSTALLED_APPS in settings.py : 然后,添加computed_property到你的列表INSTALLED_APPSsettings.py:

INSTALLED_APPS = [
    ...
    'computed_property'
]

Now you can import and use the included field classes in your models like so: 现在,您可以在模型中导入和使用包含的字段类,如下所示:

from django.db import models
from computed_property import ComputedTextField

import random


class Person(models.Model):
    age = ComputedTextField(compute_from='calculation')

    @property
    def calculation(self):
        return str(random.randint(1,101))

The age field on the Person model returns a random number each time it is called to demonstrate that it is calculated at runtime. Person模型上的age字段每次调用时都会返回一个随机数,以证明它是在运行时计算的。

You can read values from the age field as usual, but you may not set the field's value. 您可以照常读取年龄字段中的值,但不能设置字段的值。 When the field is accessed and when a model instance is saved, it will compute the field's value using the provided callable (function/lambda), property age , or attribute age 访问该字段并保存模型实例时,它将使用提供的可调用(函数/ lambda),属性age或属性age来计算字段的值

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM