Django入门:按属性及日期排序QuerySet遇报错求解决
解决方案
错误原因分析
- 第一种方案报错是因为
sorted()返回的是Python列表,而ModelViewSet要求queryset必须是Django的QuerySet实例,列表没有model属性,导致报错。 - 第二种方案有两个问题:
- 计算
has_ended的逻辑错误,F('start_date')-F('end_date')是日期差值,不是判断项目是否结束的逻辑。 - 注解字段名
has_ended与模型中的@property重名,Django尝试给模型实例赋值该属性,但@property是只读的,因此抛出AttributeError。
- 计算
正确实现方式
推荐使用数据库层面的注解排序,效率更高且符合ModelViewSet的要求:
步骤1:导入必要模块
from django.db.models import Case, When, BooleanField, Value from django.utils import timezone
步骤2:构造正确的QuerySet
class ProjectViewSet(viewsets.ModelViewSet): serializer_class = ProjectSerializer permission_classes = [permissions.IsAuthenticated] def get_queryset(self): return Project.objects.annotate( # 用is_ended作为别名,避免和模型的has_ended属性冲突 is_ended=Case( # 判断条件:end_date不为空且小于当前日期 When(end_date__isnull=False, end_date__lt=timezone.now().date(), then=Value(True)), default=Value(False), output_field=BooleanField() ) ).order_by('is_ended', '-start_date')
逻辑说明
Case/When在数据库层面计算项目是否已结束,生成is_ended布尔字段。- 排序时,
is_ended为False(未结束)的项目会排在前面(布尔值排序中False对应0,True对应1),再按start_date倒序排列,完全符合需求。
备选方案(不推荐)
如果必须用Python层面排序,需在get_queryset中返回排序后的列表,但会丢失Django QuerySet的分页、过滤等功能:
def get_queryset(self): qs = Project.objects.all().order_by('-start_date') # 按has_ended排序,未结束(False)在前,已结束(True)在后 return sorted(qs, key=lambda project: project.has_ended)
内容的提问来源于stack exchange,提问作者Guillermo Lupiáñez Tapia
相关产品推荐
相关产品推荐

