在django-restframework中限制分页的初始查询集对象
我正在使用 django-rest-framework
定义一个 ModelViewSet
。
我需要修改默认的数据查询方式,以便在返回响应之前,对查询到的数据进行一些处理。
这个处理过程比较耗时,所以我希望只对实际会返回给用户的数据进行处理,因为响应是分页的。而不是对所有数据都进行处理,然后再进行分页,这种方式我觉得(如果我理解错了请纠正我)是DRF的默认行为。
简单来说,我需要的是:
如果默认查询的数据是 1000 个对象
,但分页限制为 每页 25 个对象
,我只想对这 25 个对象
进行处理。请注意,除了分页之外,没有其他限制来减少最终返回的对象数量。
有没有办法做到这一点?
在这种情况下,修改默认的数据查询方式是个坏主意吗?
谢谢!
2 个回答
0
理论:
之前提到过:
Django的查询集是懒加载的。
它们只有在绝对必要的时候才会访问数据库(比如在你需要处理查询和分页之前。)
DRF的分页过程有两个部分:
paginate_queryset
get_paginated_response
我们可以根据需要选择覆盖哪一部分(见实践部分)
实践:
根据你真正想处理的内容,有两个选择。
在这里假设我们要扩展/覆盖LimitOffsetPagination
类,这样更容易举例,但同样的原则适用于其他所有DRF分页。
处理模型对象:
如果你想在模型对象上执行预处理,并且希望这个处理在数据库中是永久性的,你需要覆盖
paginate_queryset
方法:class MyPaginationMethod(LimitOffsetPagination): def paginate_queryset(self, queryset, request, view=None): self.count = _get_count(queryset) self.limit = self.get_limit(request) if self.limit is None: return None self.offset = self.get_offset(request) self.request = request if self.count > self.limit and self.template is not None: self.display_page_controls = True if self.count == 0 or self.offset > self.count: return [] """ Do your processing here on the queryset[self.offset:self.offset + self.limit] Which has actually self.limit (e.g 25) amount of objects. """ return list(YOUR_PROCESSED_QUERYSET)
处理分页响应:
如果你想在响应上执行预处理,并且不希望这个处理在数据库中是永久性的,你需要覆盖
get_paginated_response
方法:class MyPaginationMethod(LimitOffsetPagination): def get_paginated_response(self, data): """ Do your processing here on the data variable. The data is a list of OrderedDicts containing every object's representation as a dict. """ return Response(OrderedDict([ ('count', self.count), ('next', self.get_next_link()), ('previous', self.get_previous_link()), ('results', YOUR_PROCESSED_DATA) ]))
1
没有“简单”的方法来做到这一点。在Django REST框架中,分页和渲染是用同一种方式来处理的。
所以我想最好的办法是定义你自己的视图集,并重新声明列表方法:
from rest_framework.viewssets import ModelViewSet
class MyModelViewSet(ModelViewSet):
def list(self, request, *args, **kwargs):
self.object_list = self.filter_queryset(self.get_queryset())
if not self.allow_empty and not self.object_list:
warnings.warn(
'The `allow_empty` parameter is due to be deprecated. '
'To use `allow_empty=False` style behavior, You should override '
'`get_queryset()` and explicitly raise a 404 on empty querysets.',
PendingDeprecationWarning
)
class_name = self.__class__.__name__
error_msg = self.empty_error % {'class_name': class_name}
raise Http404(error_msg)
page = self.paginate_queryset(self.object_list)
## INSERT YOUR CODE HERE
if page is not None:
serializer = self.get_pagination_serializer(page)
else:
serializer = self.get_serializer(self.object_list, many=True)
return Response(serializer.data)