OBu*_*OBu 5 python django django-admin
在我的Django中model,我定义了一个@property效果很好的属性,该属性可以在管理中显示,list_display没有任何问题。
我不仅需要在管理员中使用此属性,还需要在其他地方的代码逻辑中使用此属性,因此将其作为模型的属性是有意义的。
现在,我想使此属性的列可排序,并在When对象的Django文档的帮助下,此F()计算的StackOverflow问题以及此排序的链接,我设法构建了如下所示的工作解决方案。
在这里提出问题的原因是:实际上,我实现了两次逻辑,一次在python中实现,一次以表达式形式实现,这违反了只实现一次逻辑的设计规则。所以我想问我是否错过了一个更好的解决方案。任何想法表示赞赏。
这是模型(标识已修改):
class De(models.Model):
fr = models.BooleanField("[...]")
de = models.SmallIntegerField("[...]")
gd = models.SmallIntegerField("[...]")
na = models.SmallIntegerField("[...]")
# [several_attributes, Meta, __str__() removed for readability]
@property
def s_d(self):
if self.fr:
return self.de
else:
return self.gd + self.na
Run Code Online (Sandbox Code Playgroud)
这是Model Admin:
class DeAdmin(admin.ModelAdmin):
list_display = ("[...]", "s_d", "gd", "na", "de", "fr" )
def get_queryset(self, request):
queryset = super().get_queryset(request)
queryset = queryset.annotate(
_s_d=Case(
When(fr=True, then='s_d'),
When(fr=False, then=F('gd') + F('na')),
default=Value(0),
output_field=IntegerField(),
)
)
return queryset
def s_d(self, obj):
return obj._s_d
s_d.admin_order_field = '_s_d'
Run Code Online (Sandbox Code Playgroud)
如果没有其他方法,我也希望确认这一事实作为答案。
TL/DR:是的,您的解决方案似乎遵循唯一有意义的方法。
好吧,您在这里撰写的内容似乎是您在问题中列出的来源的推荐方式,并且有充分的理由。
什么是好的理由?
我还没有在代码库中找到明确的答案,但我想这与@property装饰器在 Python 中的工作方式有关。
当我们用装饰器设置一个属性时,我们不能向它添加属性,因为它admin_order_field是一个属性,所以我们不能在那里拥有它。该声明似乎从Django Admin 的list_display文档中得到了加强,其中存在以下段落:
的元素
list_display也可以是属性。但是请注意,由于属性在 Python 中的工作方式,只有在使用函数时才可以设置short_description属性,property()而不能与@property装饰器一起使用。
与此 QA 相结合的引用:AttributeError: 'property' object has no attribute 'admin_order_field'似乎解释了为什么不可能将模型属性的可订购项直接放入管理面板。
这解释了(可能?)是时候进行一些心理体操了!!
在前面提到的文档部分中,我们还可以看到admin_order_field自 2.1 版以来可以接受查询表达式:
可以在 admin_order_field 中使用查询表达式。例如:
Run Code Online (Sandbox Code Playgroud)from django.db.models import Value from django.db.models.functions import Concat class Person(models.Model): first_name = models.CharField(max_length=50) last_name = models.CharField(max_length=50) def full_name(self): return self.first_name + ' ' + self.last_name full_name.admin_order_field = Concat('first_name', Value(' '), 'last_name')
结合上一部分关于property()方法的内容,我们可以重构您的代码并将annotation部分移动到模型中:
class De(models.Model):
...
def calculate_s_d(self):
if self.fr:
return self.de
else:
return self.gd + self.na
calculate_s_d.admin_order_field = Case(
When(fr=True, then='s_d'),
When(fr=False, then=F('gd') + F('na')),
default=Value(0),
output_field=IntegerField(),
)
s_d = property(calculate_s_d)
Run Code Online (Sandbox Code Playgroud)
最后,admin.py我们只需要:
class DeAdmin(admin.ModelAdmin):
list_display = ("[...]", "s_d")
Run Code Online (Sandbox Code Playgroud)