简体   繁体   English

django-rest-framework,在列表响应中添加“ includes”(带有分页)

[英]django-rest-framework, adding 'includes' to list response, with pagination

With an API I am currently working on in django-rest-framework, I am attempting to implement something similar to a feature in the json-api standard. 我正在使用django-rest-framework中正在使用的API,尝试实现类似于json-api标准中的功能。 Given a book model: 给定一个书本模型:

class Book(models.Model):
    title = models.CharField(max_length=255)
    author = models.ForeignKey(Author, on_delete=models.CASCADE)
    publisher = models.ForeignKey(Publisher, on_delete=models.CASCADE)

I want to include a parameter in the url, include , which lets the user define if they want to include author and publisher models in the response. 我想在网址中包含一个参数include ,该参数可以让用户定义是否要在响应中包括authorpublisher模型。 The additional gotcha, is I am using limit/offset pagination. 额外的陷阱,是我正在使用限制/偏移分页。 Thus, the following url: 因此,以下网址:

https://my-api-domain/api/books?limit=5&offset=0&include=authors

should return something that looks like: 应该返回如下内容:

{
  "count": 152,
  "next": "https://my-api-domain/api/books/limit=5&offset=5&include=authors"
  "previous": null,
  "results": [
    {"id": 1, "title": "Book 1", "author": 1, "publisher": 18},
    {"id": 2, "title": "Book 2", "author": 2, "publisher": 26},
    ...
  ],
  "include": {
    "authors": [
      {"id": 1, "first_name": "Author", "last_name": "One"},
      {"id": 2, "first_name": "Author", "last_name": "Two"},
      ... for all author ids in paged `results` field above
    ]
  }
}

So far, my view looks like: 到目前为止,我的观点看起来像:

class BookViewSet(viewsets.ModelViewSet):
    queryset = Book.objects.all()
    serializer_class = BookSerializer
    permission_classes = (IsAuthenticated,)
    pagination_class = LimitOffsetPagination
    filter_class = BookFilterSet
    filter_backends = [DjangoFilterBackend]

    def list(self, request, *args, **kwargs):
        # Grab include from url parameters
        include = request.query_params.get('include', None)
        # Apply incoming filters to books
        queryset = self.filter_queryset(self.get_queryset())
        # Apply pagination to queryset
        page = self.paginate_queryset(queryset)

        # Assemble include data if necessary
        if include is not None:
            include_data = {}
            includes = include.split(',')
            response_data = serializer.data
            response_data['includes'] = {}
            for entity in includes:
                if entity == 'authors':
                    authors = Author.objects.distinct().filter(book__in=page)
                    authors_serializer = AuthorSerializer(authors, many=True)
                    include_data['authors'] = authors_serializer.data
                elif entity == 'publishers':
                    publishers = Publisher.objects.distinct().filter(book__in=page)
                    publishers_serializer = PublisherSerializer(publishers, many=True)
                    include_data['publishers'] = publishers_serializer.data

        serializer = self.get_serializer(page, many=True)

        # PROBLEM: How can I inject include_data into my response below???
        return self.get_paginated_response(serializer.data)

So I am able to take the incoming request, get the filtered and paged query, and pull the proper author and publisher serialized data. 因此,我能够处理传入的请求,获取经过过滤和分页的查询,并提取适当的authorpublisher序列化的数据。 However, I am not sure, with the pagination response, how to inject this data (see the last two lines of the above code). 但是,对于分页响应,我不确定如何注入此数据(请参见上面代码的最后两行)。

Any thoughts on how this can be accomplished? 关于如何实现这一目标有什么想法? Is doing this in the view even the right spot? 在视图中这样做是否正确? Or do I somehow need to be grabbing everything in my serializer? 还是我需要以某种方式获取序列化器中的所有内容? And if I do that, is there any way to get the include array as a sibling to the results array in my response (rather than include being embedded in results )? 如果我这样做了,有什么办法可以将include数组作为我的响应中的results数组的同级对象(而不是将include嵌入到results )?

Also, I realize that there is a django-rest-framwork-jsonapi package, however it also transforms my data in such a way that would require me doing drastic changes to my client code, which I am trying to avoid. 另外,我意识到这里有一个django-rest-framwork-jsonapi包,但是它也以一种方式转换我的数据,这要求我对我的客户端代码进行大刀阔斧的修改,而我试图避免这种情况。 Thus the 'light' version I am proposing here. 因此,我在这里提出“轻量”版本。

add

class CustomPagination(pagination.PageNumberPagination):
    def get_paginated_response(self, data, include_data):
        return Response({
            'links': {
                'next': self.get_next_link(),
                'previous': self.get_previous_link()
            },
            'count': self.page.paginator.count,
            'results': data,
            'include': include_data
        })

and configure its use 并配置其使用

REST_FRAMEWORK = {
    'DEFAULT_PAGINATION_CLASS': 'my_project.apps.core.pagination.CustomPagination',
    'PAGE_SIZE': 100
}

then in your list method do 然后在您的列表方法中

return self.get_paginated_response(serializer.data, include_data)

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM