如何按相关模型的字段对 Django 查询进行排序?

How can I order a Django query by a field of a related model?

提问人:cy23 提问时间:10/23/2023 最后编辑:cy23 更新时间:10/25/2023 访问量:66

问:

我有这两个模型,和 ,它们是相关的。我的目标是对查询进行排序,使具有最新消息的聊天室(由 确定)显示在结果的顶部。ChatRoomChatMessageChatRoomsent_timestamp

虽然我在这个平台上发现了一些类似的问题,但似乎没有一个能准确地解决我的具体问题。

我知道 annotate 方法,我可以用它来在模型的类之外实现这一点,但我有兴趣直接在模型的类中设置顺序。MetaMetaChatRoom

有没有办法在 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",)
python django django 模型 django-queryset

评论


答:

2赞 willeM_ Van Onsem 10/23/2023 #1

将引入重复:对于每个相关的 ,将列出。ordering = ("-messages__sent_timestamp",)ChatMessageChatRoom

您可以使用最新的时间戳对此进行注释,然后按此排序,因此:

from django.db.models import Max

ChatRoom.objects.alias(latest=Max('messages__sent_timestamp')).order_by('-latest')
1赞 0urz4g 10/23/2023 #2

试试这个:

ChatRoom.objects.all().alias(my_order=Max('chatmessage_set__sent_timestamp')).order_by('-my_order')

关于 Django 中聚合的文档

0赞 Hassaan AlAnsary 10/25/2023 #3

根据您不使用并希望将其添加到 . 我想您需要默认将此排序应用于所有查询。annotateMeta

我不认为你可以在 但是,你可以在 .您可以通过将其分配给MetaManagerobjects

# 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

请注意,这会将 A 添加到此模型的所有查询中,这将导致基于数据大小的查询速度变慢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

希望你觉得这有用