如何在Django Admin中按模型@property属性对对象进行排序?
要在Django Admin里按这个completed_this_month属性排序,你得先明白一个关键点:原生的@property是Python层面计算的,数据库根本不知道这个值,所以没法直接用来排序。Django Admin的排序是基于数据库查询的,所以我们得把这个统计逻辑移到数据库层面,用annotate来实现,然后让Admin用这个数据库层面的注解字段来排序。
下面是具体的实现步骤,结合你的代码来修改:
首先,导入需要的模块:
from django.contrib import admin from django.db.models import Count, Q from django.utils import timezone from django.db.models.functions import TruncMonth from .models import Worker, Task
然后修改你的WorkerAdmin类:
class WorkerAdmin(admin.ModelAdmin): # 把你的其他字段和completed_this_month一起放到list_display里 list_display = ('username', 'name', 'completed_this_month') # 替换成你实际的字段 def get_queryset(self, request): # 获取当前时间(用Django的timezone避免时区问题) today = timezone.now() # 调用父类的queryset,然后给每个Worker注解本月完成的任务数 qs = super().get_queryset(request).annotate( # 这里的`tasks`是Task模型中外键指向Worker的related_name # 如果你的Task外键没设置related_name,默认是`task_set`,记得改成对应的名称 _completed_this_month=Count( 'tasks', # 过滤条件:本月完成的任务 filter=Q(completed_on__truncmonth=TruncMonth(today)), distinct=True # 防止重复统计,根据你的关联关系可选 ) ) return qs def completed_this_month(self, obj): # 直接返回注解好的值,不用再单独查数据库(解决原来的N+1查询问题) return obj._completed_this_month # 关键:指定这个显示字段的排序依据是我们注解的数据库字段 completed_this_month.admin_order_field = '_completed_this_month' # 给这个字段设置显示名称(可选) completed_this_month.short_description = '本月完成任务数'
几个需要注意的点:
- 关联名称:如果你的
Task模型里的外键是worker = models.ForeignKey(Worker, on_delete=models.CASCADE),没有设置related_name,那上面的'tasks'要改成'task_set',否则会报错。如果设置了related_name='tasks',就用'tasks'。 - 时区问题:用
timezone.now()代替datetime.date.today(),确保和Django的时区设置一致,避免统计日期出错。 - 性能优化:原来的
@property会导致每个Worker实例都单独查一次数据库(N+1查询),用annotate后只需要一次查询就统计完所有Worker的任务数,性能提升很多。 - 保留原@property:如果你在其他地方还需要用原来的
@property,可以保留它,Admin里我们用重写的completed_this_month方法就好,互不影响。
这样修改后,你在Worker Admin页面点击本月完成任务数的表头,就能正常排序啦!
内容的提问来源于stack exchange,提问作者Greg K
相关产品推荐
相关产品推荐

