【问题标题】:Dynamically add properties to Django model动态添加属性到 Django 模型
【发布时间】:2018-06-27 02:49:42
【问题描述】:

目前我有以下 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'

但是一旦我退出 shell,我就失去了属性。有没有办法永久添加属性。

【问题讨论】:

  • 为什么需要这样做?您还需要动态添加哪些其他属性到您的模型中?
  • 您到底想完成什么?属性的用处有限,因为您无法查询它们(因为它们实际上并不存在于数据库中)——SQLAlchemy 有一个 hybrid_property 可以让您对属性进行查询。使用 Django 实现类似功能的一种方法是通过 annotating 默认查询集通过向模型管理器的 get_queryset 添加注释来实现类似的功能。这可能适合你的情况。性能影响确实适用。
  • @Pattu:你必须更清楚你真正想要完成的事情。这些属性是否都是TestUser 列的简单表达式,或者它们是否可以包含任意 Python 代码?它们是正面的吗?它们会被(可能是恶意的)用户创建/删除吗?为什么这个用户不能只编辑models.py 文件?
  • 但这解决了什么问题?我强烈建议避免这种情况。像这样动态地将属性/属性添加到实例上几乎是无用的,并且可以说对您的应用程序设计是危险的。如果要使用属性,请使用 @property 方法装饰器来实现它们。在很多情况下,您的模型会被序列化或从数据库重新加载,在这种情况下,您对不在数据库中的对象所做的任何修改都会丢失,即使它看起来您可能正在传递直接的对象。
  • @Pattu:为什么不能将它们放入models.py

标签: python django properties metaclass


【解决方案1】:

但是一旦我退出 shell,我就失去了属性。有没有办法永久添加属性。

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

您也表达了在运行时动态执行此操作的愿望。但是,这样做的好处是可疑的,充其量是对您的代码库有害的。很难想象一个可证明的用例可以解决尚未有更好解决方案的实际问题。几乎可以肯定,有更好的方法可以实现您的目标。如果您的目标是保持代码 DRY,请考虑其他模式,例如从抽象模型继承。

您可以在您的模型类上实现常规属性(使用@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 方式”做事而设计的。你可能只会因违背原则而伤害自己。

【讨论】:

    猜你喜欢
    • 2015-08-28
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-03-17
    • 2016-11-16
    • 1970-01-01
    • 2016-01-24
    • 1970-01-01
    相关资源
    最近更新 更多