3

所以在我的项目中,我有一个带有许多消息的模型聊天。我想在里面有带有 MessageSerializer 的 ChatSerializer,并且很多 = True。

但是,我只想包含最后 50 个消息模型。

class MessageSerializer(serializers.ModelSerializer):
    class Meta(object):
        model = Message
        fields = '__all__'

class ChatSerializer(serializers.ModelSerializer):
    messages = MessageSerializer(many=True)
    class Meta:
        model = Chat
        fields = '__all__'

我可以添加什么messages = MessageSerializer(many=True)使其仅返回最新的 50 条消息吗?

谢谢!

4

4 回答 4

2

您可以使用SerializerMethodField来做到这一点。

MESSAGE_COUNT = 50
class ChatSerializer(serializers.ModelSerializer):
    messages = serializers.SerializerMethodField()
    class Meta:
        model = Chat
        fields = '__all__'

    def get_messages(self, obj):
        return MessageSerializer(obj.messages[:MESSAGE_COUNT ], many=True).data
于 2019-03-22T16:33:51.747 回答
2

SerializerMethodField您可以使用ChatSerializer如下:

messages = serializers.SerializerMethodField()

def get_messages(self, chat):
    qs = Message.objects.filter(chat=chat).order_by('-date')[:50]
    return MessageSerializer(instance=qs, many=True).data

这会为每个实例运行一个单独的查询Chat,但它只获取所需的行数。您必须根据需要自定义字段名称 ( chat, date)。


替代语法@spiritsree 针对相同 SQL 中的结果,使用隐式过滤而不是显式过滤:

qs = chat.messages.order_by('-date')[:50]

需要避免的一件事是在返回列表prefetch_related('messages')querysetof the中使用,因为预取根本不会被使用,并且会从数据库中提取所有消息,而只会在未使用的情况下被丢弃。ViewSetChat


在另一个答案中因为慢而被驳回的子查询替代方案实际上很有趣。它为您节省了与聊天次数一样多的数据库往返次数。然而,作为交换,数据库必须在内部执行两倍的查询。

多余的查询非常轻量级(通过 id 选择少量消息并对其进行排序),保存的往返可能很容易弥补它们。在我的快速测试中,这种方法比使用SerializerMethodField. 它可能在某种程度上取决于数据;自己测试一下:

from rest_framework import viewsets
from django.db.models import Prefetch, Subquery, OuterRef

class ChatViewSet(viewsets.ModelViewSet):
    prefetch = Prefetch(
        'messages',
        queryset=Message.objects
            .filter(id__in=Subquery(Message.objects
                .filter(chat=OuterRef('chat_id'))
                .order_by('-date')
                .values_list('id', flat=True)[:4]))
            .order_by('-date')
    )

    queryset = Chat.objects.prefetch_related(prefetch)
于 2019-03-22T18:51:52.033 回答
0

@Tobey 的答案与嵌套限制无关,应该被忽略。

@spritsree 的答案将强制在 Python 中执行限制,而不是在您希望在数据库级别执行限制。

您要查找的详细信息未在序列化程序级别实现。

当您实例化您的序列化程序时,您必须将您希望序列化的数据传递给它。在您的情况下,您应该在从数据库收集数据时执行这种反向关系限制。

我只能假设您在 Chat-Message 上是 1-N 关系。因此,您希望在编写QuerySet时可以执行以下操作:

QuerySet = Chat.objects.all().prefetch_related(
    Prefetch(
        "messages", queryset=Message.objects.all().order_by("-created_at")[:50]
    )
)

但是,Django 不支持在预取相关查询集中进行切片。描述了一种解决方法,但您需要执行 IN 查询,这是解决此问题的最慢方法。

相反,您应该将消息收集分离到一个单独的查询中:

# assuming that you are only interested in a single chat
chat = Chat.objects.latest("created_at")
messages = Message.objects.filter(chat=chat).order_by("created_at")[:50]

# instantiate serializer
serializer = ChatSerializer(data={"chat": chat, "messages": messages, ...})
serializer.data
...
于 2019-03-22T16:35:51.567 回答
-1

每个人都对messages = serializers.SerializerMethodField(). 需要提到的一件事是将列表排序为数组的最后一条消息。

对于我的实现,我需要按升序时间戳排序的最后 50 条消息(稍后),所以这就是我所做的。

MESSAGE_COUNT = 50
class ChatSerializer(serializers.ModelSerializer):
    messages = serializers.SerializerMethodField()
    class Meta:
        model = Deal
        fields = '__all__'

    def get_messages(self, chat):
        qs = Message.objects.filter(deal=deal).order_by('timestamp')
        if len(qs) > MESSAGE_COUNT:
            qs = qs[len(qs)-MESSAGE_COUNT:]
        return MessageSerializer(instance=qs, many=True).data
于 2019-03-25T14:54:48.523 回答