我有这两个模型,
ChatRoom
和ChatMessage
,它们是相关的。我的目标是对 ChatRoom
查询进行排序,使包含最新消息的房间(由 sent_timestamp
确定)显示在结果的顶部。
虽然我在这个平台上发现了一些类似的问题,但似乎没有一个能够准确解决我的具体问题。
我知道注释方法,我可以使用该方法在模型的
Meta
类之外实现此目的,但我有兴趣直接在 Meta
模型的 ChatRoom
类中设置排序。
有没有办法在 Meta 类中实现这种排序,或者我应该考虑其他方法吗?
class ChatRoom(models.Model):
members = models.ManyToManyField(User, related_name="chat_rooms")
class Meta:
# Intended for this to order the chat rooms so that the room that
# has the most recent chat message come first, but it's not working
ordering = ("-messages__sent_timestamp",)
class ChatMessage(models.Model):
room = models.ForeignKey(ChatRoom, models.CASCADE, related_name="messages")
sender = models.ForeignKey(User, models.CASCADE)
text = models.TextField(validators=[MaxLengthValidator(280)])
sent_timestamp = models.DateTimeField(auto_now_add=True)
delivery_timestamp = models.DateTimeField(null=True, blank=True)
read_timestamp = models.DateTimeField(null=True, blank=True)
class Meta:
ordering = ("-sent_timestamp",)
ordering = ("-messages__sent_timestamp",)
将引入重复:对于每个相关的ChatMessage
,ChatRoom
将被列出。
你可以用最新的时间戳来注释它,然后按它排序,所以:
from django.db.models import Max
ChatRoom.objects.alias(latest=Max('messages__sent_timestamp')).order_by('-latest')
试试这个:
ChatRoom.objects.all().alias(my_order=Max('chatmessage_set__sent_timestamp')).order_by('-my_order')
基于您不使用
annotate
并希望将其添加到 Meta
的请求。
我想您需要在所有查询中默认应用此顺序。
我认为你不能在
Meta
中做到这一点
但是,您可以在 Manager
中做到这一点。您可以将其分配给 objects
将其用作默认管理器
# managers.py
class ChatRoomManager(models.Manager):
def get_queryset(self):
queryset = super().get_queryset()
queryset = queryset.alias(latest=Max('messages__sent_timestamp')).order_by('-latest')
# based on "Willem Van Onsem"'s answer
return queryset
# models.py
class ChatRoom(models.Model):
...
objects = ChatRoomManager() # default manager
考虑到这会为您对此模型的所有查询添加
join
,这将导致基于您的数据大小的查询速度变慢
如果您想有办法禁用随意订购,您可以执行以下操作
# managers.py
class ChatRoomManager(models.Manager):
def __init__(self, no_ordering: bool = False):
super().__init__()
self.no_ordering= no_ordering
def get_queryset(self):
queryset = super().get_queryset()
if self.no_ordering:
return queryset
queryset = queryset.alias(latest=Max('messages__sent_timestamp')).order_by('-latest')
return queryset
# models.py
class ChatRoom(models.Model):
...
objects = ChatRoomManager() # default manager
raw_objects = ChatRoomManager(no_ordering=True) # original manager
希望您觉得这很有用