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,然后为你的属性定义另一个名称,因为属性不能在
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)