Python 属性对django模型字段有效吗?

Python 属性对django模型字段有效吗?,python,django,properties,model,Python,Django,Properties,Model,我认为问这个问题最好的方法是用一些代码。。。我能做这个吗?(编辑:回答:否) 或者我必须这样做: from django.db.models.functions import Coalesce queryset = MyModel.objects.annotate(bar_otherwise_foo=Coalesce('bar', 'foo')) 是的,你必须这样做: 注意:通过向模型字段传递db_列,可以在数据库中将列名保留为“foo”。当您在现有系统上工作,并且不想无缘无故地执行db迁移

我认为问这个问题最好的方法是用一些代码。。。我能做这个吗?(编辑:回答:否)

或者我必须这样做:

from django.db.models.functions import Coalesce

queryset = MyModel.objects.annotate(bar_otherwise_foo=Coalesce('bar', 'foo'))
是的,你必须这样做:
注意:通过向模型字段传递db_列,可以在数据库中将列名保留为“foo”。当您在现有系统上工作,并且不想无缘无故地执行db迁移时,这非常有用

模型字段已经是属性,因此我认为您必须使用第二种方法来避免名称冲突

定义
foo=property(..)
时,它实际上会覆盖
foo=models..
行,因此该字段将不再可访问

您需要为属性和字段使用不同的名称。事实上,如果按照示例#1中的方式执行,当您尝试访问属性时,将得到一个无限循环,因为它现在尝试返回自身


<>编辑:也许你也应该考虑不要使用<代码> -foo< /Cord>作为一个字段名,而应该使用<代码> fo,然后为你的属性定义另一个名称,因为属性不能在QueReStest中使用,所以当你做一个过滤器时,你需要使用实际的字段名。作为实现自己的
django.db.models.Field
类的正确替代方案,应该使用
db\u列
参数和自定义(或隐藏)类属性。我只是在@Jiaaro的编辑中按照python中OOP更严格的约定重写代码(例如,如果
\u foo
实际上应该隐藏):

\uuuuufoo
将解析为
\umymodel\uuuuuufoo
(如
目录(…)
所示)从而隐藏()。请注意,这种形式还允许使用,这最终将是编写可读代码的更好方式


同样,django将创建带有两个字段的
\u MyModel
foo
bar
以前的解决方案会受到影响,因为@property会导致管理和.filter(\u foo)出现问题

更好的解决方案是重写setattr,除非这会导致从DB初始化ORM对象时出现问题。然而,有一个技巧可以解决这个问题,它是普遍存在的

class MyModel(models.Model):
    foo = models.CharField(max_length = 20)
    bar = models.CharField(max_length = 20)

    def __setattr__(self, attrname, val):
        setter_func = 'setter_' + attrname
        if attrname in self.__dict__ and callable(getattr(self, setter_func, None)):
            super(MyModel, self).__setattr__(attrname, getattr(self, setter_func)(val))
        else:
            super(MyModel, self).__setattr__(attrname, val)

    def setter_foo(self, val):
        return val.upper()

秘密是“attrname in self.\uu dict\uuu”。当模型从新的或从\uuuuuu dict\uuuuuu中水合初始化时

这取决于您的
属性是达到目的的手段还是目的本身

如果在过滤查询集时需要这种“覆盖”(或“回退”)行为(无需首先对其求值),我认为属性无法做到这一点,Python属性不能在数据库级别工作,因此不能在queryset筛选器中使用。请注意,您可以在筛选器中使用
\u foo
(而不是
foo
),因为它表示实际的表列,但是
get\u foo()
中的覆盖逻辑将不适用

但是,如果您的用例允许,则来自
django.db.models.functions
()的
Coalesce()
类可能会有所帮助

Coalesce()
。。。接受至少两个字段名的列表,或 表达式并返回第一个非空值(请注意 字符串不被视为空值)

这意味着您可以使用
Coalesce('bar','foo')
bar
指定为
foo
的覆盖。它返回
bar
,除非
bar
null
,在这种情况下,它返回
foo
。与您的
get\u foo()
相同(除了它不适用于空字符串),但在数据库级别

剩下的问题是如何实现这一点

如果你没有在很多地方使用它,简单的查询集可能是最简单的。以您的示例为例,在没有属性的情况下:

class MyModel(models.Model):
    foo = models.CharField(max_length = 20)
    bar = models.CharField(max_length = 20)
然后按如下方式进行查询:

from django.db.models.functions import Coalesce

queryset = MyModel.objects.annotate(bar_otherwise_foo=Coalesce('bar', 'foo'))
现在,查询集中的项具有神奇属性
bar\u otherwise\u foo
,可以对其进行筛选,例如
queryset.filter(bar\u otherwise\u foo='what I want')
,或者可以直接在实例上使用,例如
print(queryset.all()[0].bar\u otherwise\u foo)

queryset.query
得到的结果表明
Coalesce()
确实在数据库级别工作:

SELECT "myapp_mymodel"."id", "myapp_mymodel"."foo", "myapp_mymodel"."bar",
    COALESCE("myapp_mymodel"."bar", "myapp_mymodel"."foo") AS "bar_otherwise_foo" 
FROM "myapp_mymodel"
注意:您也可以调用您的模型字段
\u foo
,然后
foo=Coalesce('bar','u foo')
,等等。使用
foo=Coalesce('bar','foo')
会很有诱惑力,但这会引发
值错误:注释'foo'与模型上的字段冲突。

必须有几种方法来创建干式实现,例如编写一个或多个

自定义管理器可以轻松实现,如下所示(请参阅):

现在,
MyModel
的每个查询集都将自动具有
bar\u或者\u foo
注释,可以如上所述使用

但是,请注意,例如,在实例上更新
bar
不会更新注释,因为这是在queryset上进行的。首先需要重新评估queryset,例如从queryset获取更新的实例


也许可以使用带有注释的自定义管理器和Python
属性的组合来充分利用这两个方面()。

关于编辑,这是一个很好的建议-我正在维护一个现有系统,因为必须更改字段(或属性)的名称,所以我们放弃了这种方法。这项技术在未来可能会很有用:)不确定它是否可以通过闭包或类似的方式访问——诚然,我对闭包不太了解。我已经为下面的“__foo”提供了案例。使用foo作为字段和另一个_foo作为包装属性将公开foo以进行直接操作。虽然python和X语言的开发人员都应该知道自己是什么
from django.db.models.functions import Coalesce

queryset = MyModel.objects.annotate(bar_otherwise_foo=Coalesce('bar', 'foo'))
SELECT "myapp_mymodel"."id", "myapp_mymodel"."foo", "myapp_mymodel"."bar",
    COALESCE("myapp_mymodel"."bar", "myapp_mymodel"."foo") AS "bar_otherwise_foo" 
FROM "myapp_mymodel"
class MyModelManager(models.Manager):
    """ standard manager with customized initial queryset """
    def get_queryset(self):
        return super(MyModelManager, self).get_queryset().annotate(
            bar_otherwise_foo=Coalesce('bar', 'foo'))


class MyModel(models.Model):
    objects = MyModelManager()
    foo = models.CharField(max_length = 20)
    bar = models.CharField(max_length = 20)