如何根据子表Room的更新时间对Django父表Topic排序
Django QuerySet排序问题:关联Room更新/新增时让对应Topic置顶
不需要修改数据库架构,推荐两种可行方案:
方案一:通过聚合查询动态排序(无需修改模型)
直接在视图中使用annotate和Max聚合函数,获取每个Topic关联的最新Room的更新时间,以此作为排序依据:
修改views.py中的topics视图:
from django.db.models import Max def topics(request): """topics page for listed view of topics""" room_count = Room.objects.all().count() # 为每个Topic标注关联Room的最新更新时间,按该时间倒序排序 topics = Topic.objects.annotate( latest_room_updated=Max('room__updated') ).order_by('-latest_room_updated', '-id') # 第二个排序条件'-id'确保无更新的Topic按创建时间倒序排列 context = {'topics': topics, 'room_count': room_count} return render(request, 'base/topics.html', context)
说明
annotate(latest_room_updated=Max('room__updated'))会为每个Topic添加一个临时字段,存储该Topic下所有Room的updated字段最大值(即最新的活动时间)order_by('-latest_room_updated', '-id')先按最新活动时间倒序,无关联Room的Topic会排在末尾;同时间的Topic按ID倒序(创建时间越晚越靠前)- 优点:无需修改模型结构,实现简单;缺点:每次查询都会执行聚合计算,数据量大时性能略差
方案二:新增字段维护活动时间(性能更优)
给Topic模型添加last_activity字段,通过Django信号自动维护该字段的值,查询时直接按此字段排序:
步骤1:修改models.py
from django.db.models.signals import post_save, post_delete from django.dispatch import receiver class Topic(models.Model): name = models.CharField(max_length=200) last_activity = models.DateTimeField(auto_now_add=True) # 初始值为Topic创建时间 def __str__(self): return self.name class Room(models.Model): host = models.ForeignKey(User, on_delete=models.SET_NULL, null=True) topic = models.ForeignKey(Topic, on_delete=models.SET_NULL, null=True) name = models.CharField(max_length=200) updated = models.DateTimeField(auto_now=True) created = models.DateTimeField(auto_now_add=True) class Meta: ordering = ['-updated', '-created'] def __str__(self): return self.name # 信号接收器:Room保存时更新对应Topic的活动时间 @receiver(post_save, sender=Room) def update_topic_activity_on_save(sender, instance, **kwargs): if instance.topic: instance.topic.last_activity = instance.updated instance.topic.save() # 信号接收器:Room删除时更新对应Topic的活动时间 @receiver(post_delete, sender=Room) def update_topic_activity_on_delete(sender, instance, **kwargs): if instance.topic: # 查找该Topic下剩余最新的Room,若无则保留原活动时间 latest_room = instance.topic.room_set.order_by('-updated').first() instance.topic.last_activity = latest_room.updated if latest_room else instance.topic.last_activity instance.topic.save()
步骤2:修改views.py
def topics(request): """topics page for listed view of topics""" room_count = Room.objects.all().count() # 直接按last_activity倒序排序 topics = Topic.objects.order_by('-last_activity') context = {'topics': topics, 'room_count': room_count} return render(request, 'base/topics.html', context)
说明
- 通过
post_save和post_delete信号,在Room创建、更新、删除时自动同步Topic的last_activity字段 - 查询时直接按
last_activity排序,无需聚合计算,性能更优 - 优点:查询效率高,适合数据量大、访问频繁的场景;缺点:需要新增字段并维护信号逻辑
内容的提问来源于stack exchange,提问作者user2572155
相关产品推荐
相关产品推荐

