自定义Django ListView分页:如何对接Elasticsearch返回的分页信息?
Django ListView 对接 Elasticsearch 自定义分页方案
核心思路
Django ListView 默认的分页逻辑会对get_queryset返回的查询集做自动计数和切片,适配数据库查询场景。你已经从Elasticsearch拿到了总结果数,只需要重写视图的paginate_queryset方法,绕过默认的查询集计数逻辑,手动传入ES返回的总条数即可,不需要拉取全量数据。
具体实现步骤
- 首先调整你封装的
search方法:新增页码、每页条数两个入参,在ES查询时通过from、size参数提前完成单页数据查询,方法返回值调整为(当前页结果列表, 搜索总条数)两个值,避免全量拉取数据。 - 重写ListView的
paginate_queryset方法,手动构造分页器,传入ES返回的总条数替换默认的查询集计数。
完整修改后代码
from django.core.paginator import Paginator from django.http import Http404 from .elastic import search class SearchView(ListView): template_name = "search/index.html" paginate_by = 50 def get_queryset(self): keyword = self.kwargs['keyword'] lang = get_language() # 获取当前请求页码 page_num = self.request.GET.get('page', 1) # 调用ES查询,返回当前页结果和总条数 self.es_page_results, self.es_total = search( keyword, lang, page_num=page_num, page_size=self.paginate_by ) return self.es_page_results def paginate_queryset(self, queryset, page_size): # 构造分页器,手动覆盖总条数为ES返回的结果总数 paginator = Paginator(queryset, page_size) paginator._count = self.es_total # 处理页码参数 page = self.request.GET.get('page') or 1 try: page_number = int(page) except ValueError: if page == 'last': page_number = paginator.num_pages else: raise Http404("页码格式无效") page_obj = paginator.get_page(page_number) # 直接返回已查询到的当前页数据,不需要二次切片 return (paginator, page_obj, page_obj.object_list, page_obj.has_other_pages())
额外优化说明
如果你使用的是django-elasticsearch-dsl提供的Search查询对象,不需要做上面的重写操作:直接在get_queryset中返回未执行的Search对象即可,它已经实现了Django查询集的切片、计数接口,默认分页器可以直接识别ES的总结果数,自动完成分页查询。
注意:Elasticsearch 默认的
from+size分页最大支持查询到第10000条数据,如果你的搜索结果总条数超过1万,需要改用search_after或滚动查询实现分页,避免触发ES的查询限制。
内容的提问来源于stack exchange,提问作者Fausto Sanchez
相关产品推荐
相关产品推荐

