如何在Django函数/类视图中实现多查询集分页?
多模型搜索结果的Django分页解决方案
核心思路
Django内置的Paginator类默认针对单个QuerySet,要实现多模型分页,需先将不同模型的搜索结果合并为统一可迭代集合,再用Paginator处理分页逻辑,最后在模板中根据对象模型类型渲染对应样式。
步骤1:修改视图代码
1.1 导入依赖
from django.core.paginator import Paginator, EmptyPage, PageNotAnInteger from itertools import chain from django.db.models import Q # 可选,用于实现标题/描述的OR匹配搜索 from django.http import HttpResponse from django.shortcuts import render
1.2 重构视图逻辑
支持POST搜索提交与GET分页请求,合并多模型结果并处理分页:
def search_results(request): # 获取搜索关键词:POST提交或GET分页传递 searched = request.GET.get('searched', '') if request.method == 'POST': searched = request.POST['searched'].strip() if not searched: return HttpResponse('Please type something in the search input.') # 无搜索词时返回空结果页面 if not searched: return render(request, "search_results.html", {'searched': ''}) # 多模型搜索(用Q对象实现标题/描述任意匹配,原代码为AND条件,可按需调整) books = Book.objects.filter( Q(title__icontains=searched) | Q(description__icontains=searched) ).order_by('-id') sermons = Sermon.objects.filter( Q(title__icontains=searched) | Q(description__icontains=searched) ).order_by('-id') other_sermons = SermonsByOtherFathers.objects.filter( Q(title__icontains=searched) | Q(description__icontains=searched) ).order_by('-id') other_books = BooksByOtherFathers.objects.filter( Q(title__icontains=searched) | Q(description__icontains=searched) ).order_by('-id') # 合并所有模型的搜索结果 all_results = list(chain(books, sermons, other_sermons, other_books)) # 可选:全局统一排序(比如按ID倒序,覆盖各模型单独排序) # all_results = sorted(all_results, key=lambda x: x.id, reverse=True) # 初始化分页器,每页显示10条 paginator = Paginator(all_results, 10) page_num = request.GET.get('page') # 处理页码异常 try: paginated_results = paginator.page(page_num) except PageNotAnInteger: paginated_results = paginator.page(1) except EmptyPage: paginated_results = paginator.page(paginator.num_pages) context = { 'searched': searched, 'paginated_results': paginated_results, } return render(request, "search_results.html", context)
步骤2:添加自定义模板标签
为在模板中识别对象所属模型,创建自定义标签:
- 在Django应用下新建
templatetags目录(若不存在),并创建custom_tags.py文件:
from django import template register = template.Library() @register.filter def model_name(obj): """获取对象所属模型的小写名称""" return obj._meta.model_name
步骤3:修改HTML模板
3.1 加载自定义标签
在模板顶部添加:
{% load custom_tags %}
3.2 渲染分页结果与导航
根据模型类型渲染不同内容,并添加分页导航:
{% for item in paginated_results %} {% if item|model_name == 'book' %} <tr> <td> <a href="{{ item.book_instance.url }}"> {{ item.title }} </a> <p> {{ item.author }} </p> <p> {{ item.category }} </p> <p> {{ item.description }} </p> </td> </tr> {% elif item|model_name == 'sermon' %} <!-- Sermon模型渲染代码 --> <tr> <td> <a href="{{ item.url }}"> {{ item.title }} </a> <p> {{ item.preacher }} </p> <p> {{ item.description }} </p> </td> </tr> {% elif item|model_name == 'sermonsbyotherfathers' %} <!-- SermonsByOtherFathers模型渲染代码 --> <tr> <td> <a href="{{ item.url }}"> {{ item.title }} </a> <p> {{ item.father_name }} </p> <p> {{ item.description }} </p> </td> </tr> {% elif item|model_name == 'booksbyotherfathers' %} <!-- BooksByOtherFathers模型渲染代码 --> <tr> <td> <a href="{{ item.book_instance.url }}"> {{ item.title }} </a> <p> {{ item.author }} </p> <p> {{ item.category }} </p> <p> {{ item.description }} </p> </td> </tr> {% endif %} {% empty %} <tr> <td colspan="4">没有找到匹配的结果</td> </tr> {% endfor %} <!-- 分页导航栏 --> <div class="pagination mt-4"> <span class="step-links"> {% if paginated_results.has_previous %} <a href="?searched={{ searched }}&page=1">« 首页</a> <a href="?searched={{ searched }}&page={{ paginated_results.previous_page_number }}">上一页</a> {% endif %} <span class="current mx-2"> 第 {{ paginated_results.number }} 页 / 共 {{ paginated_results.paginator.num_pages }} 页 </span> {% if paginated_results.has_next %} <a href="?searched={{ searched }}&page={{ paginated_results.next_page_number }}">下一页</a> <a href="?searched={{ searched }}&page={{ paginated_results.paginator.num_pages }}">末页 »</a> {% endif %} </span> </div>
注意事项
model_name返回的是小写模型类名,模板中判断条件需对应小写。- 分页链接必须携带
searched参数,避免分页后丢失搜索条件。 - 若模型结构兼容,可使用
union方法替代chain提升合并效率,但chain更适配不同结构的模型。
内容的提问来源于stack exchange,提问作者Rawy Murgany
相关产品推荐
相关产品推荐

