在我的 Django model
中,我定义了一个 @property
,它工作得很好,并且该属性可以在管理 list_display
中显示,没有任何问题。
我不仅在管理中需要此属性,而且在其他地方的代码逻辑中也需要此属性,因此将其作为我的模型的属性是有意义的。
现在我想让这个属性的列可排序,并在 the Django documentation of the When object 的帮助下, this StackOverflow question for the F()-calculation和 this link for the sorting我成功构建了如下所示的工作解决方案。
在这里提出问题的原因是:事实上,我实现了我的逻辑两次,一次用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
这是模型管理员:
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'
如果没有其他办法,我也希望确认事实作为答案。
最佳答案
TL/DR:是的,您的解决方案似乎遵循唯一有意义的方式。
<小时/>嗯,您在此处撰写的内容似乎是您在问题中列出的来源中推荐的方式,并且有充分的理由。
充分的理由是什么?
我还没有在代码库中找到明确的答案,但我想这与 @property 装饰器在 Python 中的工作方式有关。
当我们使用装饰器设置属性时,我们无法向其添加属性,并且由于 admin_order_field
是一个属性,因此我们不能将其添加到其中。 Django Admin's list_display
documentation 似乎强化了这一说法。其中存在以下段落:
Elements of
list_display
can also be properties. Please note however, that due to the way properties work in Python, settingshort_description
on a property is only possible when using theproperty()
function and not with the@property
decorator.
该引用与此质量检查相结合:AttributeError: 'property' object has no attribute 'admin_order_field'似乎解释了为什么无法将模型属性中的可订购项直接放入管理面板中。
<小时/>这就解释了(可能?)是时候进行一些心理体操了!!
在 previously mentioned part of the documentation我们还可以看到,从 2.1 版本开始,admin_order_field
可以接受查询表达式:
Query expressions may be used in admin_order_field. For example:
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)
最后,在 admin.py
上我们只需要:
class DeAdmin(admin.ModelAdmin):
list_display = ("[...]", "s_d")
关于python - 如何在 Django Admin 中实现计算模型属性的排序,而无需编写两次逻辑?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/58366953/