在Django中使用many = True截断模型序列化器的长度

时间:2019-03-22 16:12:58

标签: python django django-rest-framework

因此,在我的项目中,我有一个包含许多消息的模型聊天。我想使用带有MessageSerializer的ChatSerializer,并且其中很多= True。

但是,我只想包含最近50种Message模型。

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 个答案:

答案 0 :(得分: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

答案 1 :(得分:1)

您可以在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实例运行一个单独的查询,但是它仅获取所需的行数。您必须根据需要自定义字段名称(chatdate)。


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

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

要避免的一件事是在prefetch_related('messages')的{​​{1}}的{​​{1}}中使用queryset,因为该预取将完全不使用并且会拖拉来自数据库的所有消息中的所有消息仅在未使用时被丢弃。


subquery alternative在另一个答案中被驳回,因为缓慢实际上是很有趣的。它可以为您节省与Chats一样多的数据库往返。但是,作为交换,数据库必须在内部执行两倍多的查询。

多余的查询非常轻巧(通过id选择少量消息并对其进行排序),因此保存的往返行程可以轻松地弥补它们。在我的快速测试中,该方法的速度是使用ViewSet的10倍以上。它可能在某种程度上取决于数据。自己测试一下:

Chat

答案 2 :(得分:0)

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

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

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

实例化序列化程序时,必须将要序列化的数据传递给它。对于您的情况,从数据库收集数据时应执行此反向关系限制。

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

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

但是,Django does not support slicing in prefetch related query sets。有解决方法,但是您需要执行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
...

答案 3 :(得分:-1)

每个人对CREATE OR REPLACE PROCEDURE ex_2h(nazw VARCHAR2, imi VARCHAR2) IS CURSOR team IS SELECT * FROM people p WHERE p.id_manager= (SELECT id_ppl FROM people WHERE name= imi AND surname=nazw); tmp people%ROWTYPE; BEGIN OPEN team; LOOP FETCH team INTO tmp; EXIT WHEN team%NOTFOUND; DBMS_OUTPUT.PUT_LINE(NVL(tmp.name, '?')); END LOOP; CLOSE team; END; 都是正确的。需要提及的一件事是将列表排序为数组的最后一条消息。

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

messages = serializers.SerializerMethodField()