Django-Rest-Framework:分页嵌套对象

12

我有两个模型:

class Book(models.Model):
    title = models.CharField(max_length=250)
    author = models.CharField(max_length=250)
class WordInBook(models.Model):
    book = models.ForeignKey("Book")
    word = models.ForeignKey("Word")

对应的序列化程序:

class BookSerializer(ModelSerializer):
    wordinbook_set = WordInBookSerializer(many=True)

    class Meta:
        model = Book
        fields = ('id', 'title', 'author', 'wordinbook_set')

class WordInBookSerializer(ModelSerializer):
    class Meta:
        model = WordInBook
        fields = ('word')

现在我想对wordinbook_set进行分页。在序列化器之外很容易:

book = Book.objects.get(pk=book_id)
paginator = Paginator(book.wordinbook_set.all(), 10)
words = paginator.page(page).object_list

但这样让我得到了两个独立的序列化对象。

问题: 在序列化器中如何对wordinbook_set进行分页?
生成的json应该如下所示:

{id: '...', title: '...', author: '...', wordinbook_set: [ 10 WordInBook objects here ]}
2个回答

14

由于DRF 3.1中删除了PaginationSerializer,因此您需要自己实现逻辑,请参阅以下链接获取更多详细信息: https://stackoverflow.com/a/31500287/7469841

所以你必须更改你的BookSerializer来包含分页行为,如下所示:

BookSerializer

class BookSerializer(ModelSerializer):
        wordinbook_set = serializers.SerializerMethodField('paginated_wordinbook')

        class Meta:
            model = Book
            fields = ('id', 'title', 'author', 'wordinbook_set')

        def paginated_wordinbook(self, obj):
            page_size = self.context['request'].query_params.get('size') or 10
            paginator = Paginator(obj.wordinbook_set.all(), page_size)
            page = self.context['request'].query_params.get('page') or 1

            words_in_book = paginator.page(page)
            serializer = WordInBookSerializer(words_in_book, many=True)

            return serializer.data

首先,您需要使用位于 django.core.paginator 中的 Paginator 来对可迭代对象进行分页:

paginator = Paginator(obj.wordinbook_set.all(), page_size)

然后从分页数据中获取目标页面:

words_in_book = paginator.page(page)

使用 many=True 对分页集进行序列化:

serializer = WordInBookSerializer(words_in_book, many=True)

同时,为了使页面大小具有动态性,您可以使用查询参数(query_params)来接收所需的页面大小。例如,您可以选择在一个请求中将页面大小设置为10,在另一个请求中将其设置为100,以检索页面大小:

page_size = self.context['request'].query_params.get('size') or 10

最后,为了允许用户请求特定的页面,请再次使用query_params来接收它:

page = self.context['request'].query_params.get('page') or 1

2
paginated_wordinbook返回的数据是否包含下一页链接,就像ListAPIView中的传统分页类一样。 - Sarthak Bansal

2

嗯,我认为你应该稍微不同地处理它。

首先,在BooksViewSet上定义@detail_route - 假设是单词在书中:

@detail_route(method=['GET'], url_path='word-in-book')
def word_in_book(self, request, *args, **kwargs):
    object = self.get_object()
    queryset = object.wordinbook_set.all()

    page = self.paginate_queryset(queryset)
    if page is not None:
        serializer = WordInBookSerializer(page, many=True)
        return self.get_paginated_response(serializer.data)

    serializer = self.get_serializer(queryset, many=True)
    return Response(serializer.data)

以这种方式,你将获得额外的端点:/books/1/word-in-book/,该端点将返回分页结果的书中单词模型。
希望这可以帮到你。
我认为在你的情况下无法进行分页 - 你只能稍微修改代码来返回前10个对象。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接