【问题标题】:How to make a Django model fields calculated at runtime?如何在运行时计算 Django 模型字段?
【发布时间】:2010-04-02 13:17:26
【问题描述】:

我有一个模型:

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

我想让age 字段表现得像一个属性:

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

    age = property (get_age)

但同时我需要age是一个真正的字段,所以我可以在Person._meta.fields中找到它,并为其分配属性:age.help_text = "Age of the person"

显然我不能只重写Person.save()方法来计算age并将其存储在数据库中,因为它以后不可避免地会出错(实际上它根本不应该存储在数据库中)。

其实我现在不需要setter,但是好的解决方案必须有设置功能。

在 Django 中是否有可能,或者可能有更 Python 和 djangoic 的方法来解决我的问题?

【问题讨论】:

  • 为什么覆盖保存方法不起作用?
  • 因为下次我们获取模型实例时可能会出错。这是一个简单的例子,我实际上有更复杂的字段,其值的变化不可预测,取决于其他几个因素。

标签: python django django-models field


【解决方案1】:

你正在以一种奇怪的方式处理它。你只需要存储生日(你已经在做)。

如果您需要根据年龄进行查询,请输入年龄,然后搜索符合要求的生日。

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

你自己说过“[年龄]根本不应该存储在数据库中”

你是对的。没有理由有年龄字段......只是属性会很好。

【讨论】:

  • 我有一个类似管理员的自动化应用程序,它使用字段以统一的方式显示和编辑有关对象的信息。如果没有我正在寻找的功能,我必须以特定方式处理这些属性,分别存储和处理标签(verbose_name)、help_texts 等。这很尴尬和混乱。
  • 您不应该只编辑age birthday。无论birthday 是什么age 将始终是birthday 的函数(即现在-生日)。这就是为什么我不明白你想说什么。
  • 我有一个类:class PersonReportPrinter(ReportPrinter),字段为model = Personcolumns = ('id', 'name', 'age'),该类使用Person._meta.get_field_by_name 获取verbose_namehelp_text。实际上,实际的应用程序要复杂一些,有很多模型和几个帮助类,所以变通方法太烦人了。
【解决方案2】:

您可以使用django-computed-property 实现此目的。

您首先必须使用以下命令 pip install django-computed-property

pip install django-computed-property

然后在 settings.py 中将 computed_property 添加到 INSTALLED_APPS 列表中:

INSTALLED_APPS = [
    ...
    'computed_property'
]

现在您可以像这样在模型中导入和使用包含的字段类:

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))

Person 模型上的 age 字段每次被调用时都会返回一个随机数,以证明它是在运行时计算的。

您可以像往常一样从年龄字段中读取值,但您不能设置 字段的值。何时访问该字段以及何时访问模型 实例被保存,它将使用 提供可调用(函数/lambda)、属性age 或属性age

【讨论】:

    【解决方案3】:

    另一种可能更简单的方法是在管理模型中使用自定义表单。 像这样的:

    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()
    

    【讨论】:

      【解决方案4】:

      我认为解决方案是为生日字段创建一个自定义小部件。 您只需要更改生日的显示方式和编辑方式。 这正是小部件的用途。

      抱歉,我没有相应的代码,但这是一个很好的起点:

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

      【讨论】:

        猜你喜欢
        • 2020-08-06
        • 1970-01-01
        • 1970-01-01
        • 2018-01-17
        • 2013-07-14
        • 2016-06-28
        • 2021-07-04
        • 2018-03-16
        相关资源
        最近更新 更多