向Django模型动态添加属性

时间:2018-01-18 05:28:03

标签: python django properties metaclass

目前我有以下Django模型

class TestUser(models.Model):
    name = models.CharField(max_length=250)
    email = models.CharField(max_length=250)
    ...

    class Meta:
        db_table = 'temp_user'

我有以下方法。

def print_name(self):
    return self.name

我想将此方法添加为TempUser模型的属性。我知道这可以通过将方法放在TempUser类和用户@property中来完成。但我想动态地做。

我是从python shell尝试过的。

In [10]: TempUser.print_name = property(print_name)

In [11]: TempUser.print_name
Out[11]: <property at 0x7efc374e7c58>

In [12]: user = TempUser.objects.get(pk=1)

In [13]: user.print_name
Out[13]: u'Test User'

但是一旦我退出贝壳,我就松开了房产。有没有办法永久添加属性。

1 个答案:

答案 0 :(得分:2)

  

但是一旦我退出贝壳,我就松开了房产。有没有办法永久添加属性。

不,不是通过您在运行时设置的属性。与Django模型实例相关的数据通过数据库保留。 持久数据的唯一选择是在数据库中创建用于存储信息的字段。如果要保留类上可用的方法,请编辑源代码。

您已表达了在运行时动态执行此操作的愿望。但是,这样做的好处至多是可疑的,并且可能对您的代码库造成危害。很难想象一个可证明的用例,它解决了一个尚未有更好解决方案的真正问题。几乎可以肯定有更好的方法来实现目标。如果您的目标是保持代码干燥,请考虑其他模式,例如继承抽象模型。

您可以在模型类上实现常规属性(使用@property方法装饰器),可以获取现有字段并查看相关模型字段以即时组合信息,但它仍然不会保留在数据库......

例如,如果您的模型具有start_timeend_time,则可以添加total_time属性,而无需为其创建字段。

class MyModel(models.Model):
    start_time = models.DateTimeField()
    end_time = models.DateTimeField()

    @property
    def total_time(self):
        return self.end_time - self.start_time

但是,这样做不允许您查询此属性。例如MyModel.objects.filter(total_time__lt=delta)单独使用属性是不可能的。

您拥有的其他一些选项包括annotation and aggregation,可以在运行时动态完成,并为您提供查询数据库的好处。

使用与上面相同的示例,而不是使用@property,可以以类似的方式注释查询集,这也允许您在此“虚拟字段”上查询数据库,甚至传递带注释的查询集。你甚至可以进行算术和聚合,如总和,平均等......

from django.db.models import F, ExpressionWrapper, fields

duration = ExpressionWrapper(F('end_time') - F('start_time'), output_field=fields.DurationField())

qs = MyModel.objects.annotate(duration=duration)
# query for objects with a delta of more than five minutes
results = qs.filter(duration__gt=five_minutes)

另见Query expressions

简而言之:不,如果可以的话,这几乎肯定是一个坏主意。坚持既定的做事方法。 Django是一个非常固执己见的框架,它是专为你做'django方式'而设计的。你可能只会因为违背粮食而伤害自己。