django-filter 使用分页
Posted
技术标签:
【中文标题】django-filter 使用分页【英文标题】:django-filter use paginations 【发布时间】:2017-10-18 07:00:03 【问题描述】:我正在使用django-filter
包在我的列表视图中提供搜索功能。
现在我也想为该视图添加分页。 我正在尝试将分页与过滤的查询集结合起来,但我不知道如何继续。
到目前为止,我已经在views.py
上尝试了以下操作:
def search(request):
qs = local_url.objects.filter(global_url__id=1).all()
paginator = Paginator(qs, 25)
page = request.GET.get('page')
try:
pub = paginator.page(page)
except PageNotAnInteger:
pub = paginator.page(1)
except EmptyPage:
pub = paginator.page(paginator.num_pages)
url_filter = PublicationFilter(request.GET, queryset=qs)
return render(request, 'ingester/search_list.html', 'filter': url_filter, 'publication':pub)
【问题讨论】:
你是否创建了分页的html模板? 是的,我在示例中省略了它,因为我的主要问题是如何在我的视图中将 Filterset 对象与 Paginator 结合起来。 嘿@AnhTuanNguyen 你觉得任何答案有帮助吗? 【参考方案1】:这对我有用: 在我的模板中而不是使用这个
<li><a href="?page= i "> i </a></li>
我写了这个:
% if 'whatever_parameter_you_use_to_filter' in request.get_full_path %
<li><a href=" request.get_full_path &page= i " i </a></li>
% else %
<li><a href="?page= i "> i </a></li>
% endif %
希望对你有帮助:)
【讨论】:
唯一的问题是,就像@DonExo 在他的回答中所说,页面参数被附加到 url。但它确实有效。 这个问题是你不能超过 2 页。每次您从请求中获取完整路径时,您都会发现 page=X 并将被添加。【参考方案2】:要使用 Django 过滤器并对过滤后的结果进行分页,您可以执行以下操作:
为您的模型创建一个过滤器类:
开启my_project/my_app/filters.py
:
import django_filters
class MyModelFilter(django_filters.FilterSet):
class Meta:
model = MyModel
# Declare all your model fields by which you will filter
# your queryset here:
fields = ['field_1', 'field_2', ...]
每个FilterSet
对象都有一个.qs
属性,其中包含过滤后的查询集,您甚至可以override it if you want。
我们将对MyModelFilter
的.qs
属性进行分页:
开启my_project/my_app/views.py
:
from . import filters
from django.core.paginator import Paginator, EmptyPage, PageNotAnInteger
def my_view(request):
# BTW you do not need .all() after a .filter()
# local_url.objects.filter(global_url__id=1) will do
filtered_qs = filters.MyModelFilter(
request.GET,
queryset=MyModel.objects.all()
).qs
paginator = Paginator(filtered_qs, YOUR_PAGE_SIZE)
page = request.GET.get('page')
try:
response = paginator.page(page)
except PageNotAnInteger:
response = paginator.page(1)
except EmptyPage:
response = paginator.page(paginator.num_pages)
return render(
request,
'your_template.html',
'response': response
)
你有它!
PS_1: 根据我的经验,Django 过滤器,"plays" better with Django Rest Framework。
PS_2:如果您要使用 DRF,我已经编写了一个示例,说明如何在基于函数的视图中使用分页,您可以轻松地将其与 FilterSet
结合使用:
@api_view(['GET',])
def my_function_based_list_view(request):
paginator = PageNumberPagination()
filtered_set = filters.MyModelFilter(
request.GET,
queryset=MyModel.objects.all()
).qs
context = paginator.paginate_queryset(filtered_set, request)
serializer = MyModelSerializer(context, many=True)
return paginator.get_paginated_response(serializer.data)
【讨论】:
您或其他人能否添加一个 html 模板,我正在尝试让它工作,但我卡在模板上。 @GaryNobles 这是一个很好的起点:docs.djangoproject.com/en/2.0/topics/templates,本教程将对您有很大帮助:tutorial.djangogirls.org/en/django_templates 我一直在读它们,我会再读一遍,我有分页工作,但我无法让过滤工作。我有 filter.material.label_tag % render_field filter.form.material % 我真的被困在这个问题上,我的截止日期是星期一。 @GaryNobles 如果不仔细查看您的代码,没有人可以提供帮助。如果您被严重卡住,我建议您编写一个新问题:/ 很遗憾,我无法再发布问题,即使我的声誉为 116,而且我有阅读障碍,所以我以非正常方式提问。无论如何,我已经使用了这段代码,我有一半的工作,但我无法弄清楚过滤问题,这篇文章看起来是我所见过的最有前途的,但一个模板示例将帮助我理解如何实现它。我正在进行一个研究项目,如果我无法启动并运行它,我将不得不像去年一样求助于原始 sql。我没有设计数据库,计划在挖掘之后重新设计。【参考方案3】:为了增加答案,我也使用 html 表格以及 django-filters 和 Paginator 来完成。以下是我的视图和模板文件。需要模板标签来确保您将正确的参数传递给分页 url。
search_view.py
from django.shortcuts import render
from app.models.filters_model import ApiStatusFilter
from app.models.api_status import ApiStatus
from django.core.paginator import Paginator, EmptyPage, PageNotAnInteger
from datetime import datetime, timedelta
def status(request):
all_entries_ordered = ApiStatus.objects.values().order_by('-created_at')[:200]
for dictionarys in all_entries_ordered:
dictionarys
apistatus_list = ApiStatus.objects.values().order_by('-created_at')
apistatus_filter = ApiStatusFilter(request.GET, queryset=apistatus_list)
paginator = Paginator(apistatus_filter.qs, 10)
page = request.GET.get('page')
try:
dataqs = paginator.page(page)
except PageNotAnInteger:
dataqs = paginator.page(1)
except EmptyPage:
dataqs = paginator.page(paginator.num_pages)
return render(request, 'status_page_template.html', 'dictionarys': dictionarys, 'apistatus_filter': apistatus_filter, 'dataqs': dataqs, 'allobjects': apistatus_list)
status_template.html
% load static %
% load my_templatetags %
<!DOCTYPE html>
<html lang="en">
<head>
<link rel="stylesheet" type="text/css" href="% static 'css/table_styling.css' %">
<meta charset="UTF-8">
<title>TEST</title>
</head>
<body>
<table>
<thead>
<tr>
% for keys in dictionarys.keys %
<th> keys </th>
% endfor %
</tr>
</thead>
<form method="get">
apistatus_filter.form.as_p
<button type="submit">Search</button>
% for user in dataqs.object_list %
<tr>
<td> user.id </td>
<td> user.date_time </td>
<td> user.log </td>
</tr>
% endfor %
</form>
</tbody>
</table>
<div class="pagination">
<span>
% if dataqs.has_previous %
<a href="?% query_transform request page=1 %">« first</a>
<a href="?% query_transform request page=dataqs.previous_page_number %">previous</a>
% endif %
<span class="current">
Page dataqs.number of dataqs.paginator.num_pages .
</span>
% if dataqs.has_next %
<a href="?% query_transform request page=dataqs.next_page_number %">next</a>
<a href="?% query_transform request page=dataqs.paginator.num_pages %">last »</a>
% endif %
</span>
</div>
</body>
</html>
my_templatetags.py
from django import template
register = template.Library()
@register.simple_tag
def query_transform(request, **kwargs):
updated = request.GET.copy()
for k, v in kwargs.items():
if v is not None:
updated[k] = v
else:
updated.pop(k, 0)
return updated.urlencode()
【讨论】:
【参考方案4】:这里最重要的部分是如何在模板中构建 URL。
你可能有
% if pages.has_previous %
<li><a href="?page= pages.previous_page_number ">Prev</a></li>
% endif %
如果您只使用它来在初始分页结果之间切换,那就太好了。
但棘手的部分是当您使用 django-fitler
过滤器时,查询字符串('?' 之后的部分)会获得全新的键值对,而忽略您的 ?page=2
或类似.
因此,要使用过滤后的结果进行分页,当您单击“下一步”或“上一个”按钮时 - 在来自 django-fitler
的键值对中,您还需要将 &page=5
作为对传递。
正如@stathoula 提到的,您需要检查查询字符串中是否已经存在至少一个过滤器字段。如果是,那么您需要使用已经存在的键值对,然后是新的 &page=3
对。
看起来很简单,但是当用户点击箭头时,我不得不做一些小技巧,不要在查询字符串中一遍又一遍地重复&page=1
。
在我的情况下,我将“标题”作为过滤器,所以我需要检查它是否已经存在。
这是我为我的项目所做的完美工作的简要说明。
templates/pagination.html
<div class="paginator">
% with request.get_full_path as querystring %
<ul class="pagination nav navbar-nav">
<!-- Previous page section -->
% if pages.has_previous %
% if 'title' in querystring %
% if 'page' in querystring %
<li class="paginator % if pages.number == page %active% endif %">
<a href=" querystring|slice:":-7" &page= pages.previous_page_number ">Prev</a>
</li>
% else %
<li class="paginator % if pages.number == page %active% endif %">
<a href=" querystring &page= pages.previous_page_number ">Prev</a>
</li>
% endif %
% else %
<li class="paginator % if pages.number == page %active% endif %">
<a href="?page= pages.previous_page_number ">Prev</a>
</li>
% endif %
% endif %
<!-- All pages section -->
% for page in pages.paginator.page_range %
% if 'title' in querystring %
% if 'page' in querystring %
<li class="paginator % if pages.number == page %active% endif %">
<a href=" querystring|slice:":-7" &page= page "> page </a>
</li>
% else %
<li class="paginator % if pages.number == page %active% endif %">
<a href=" querystring &page= page "> page </a>
</li>
% endif %
% else %
<li class="paginator % if pages.number == page %active% endif %">
<a href="?page= page "> page </a>
</li>
% endif %
% endfor %
<!-- Next page section -->
% if pages.has_next %
% if 'title' in querystring %
% if 'page' in querystring %
<li class="paginator % if pages.number == page %active% endif %">
<a href=" querystring|slice:":-7" &page= pages.next_page_number ">Next</a>
</li>
% else %
<li class="paginator % if pages.number == page %active% endif %">
<a href=" querystring &page= pages.next_page_number ">Next</a>
</li>
% endif %
% else %
<li class="paginator % if pages.number == page %active% endif %">
<a href="?page= pages.next_page_number ">Next</a>
</li>
% endif %
% endif %
</ul>
% endwith %
</div>
这是视图,以防万一:
app/views.py
def index(request):
condo_list = Condo.objects.all().order_by('-timestamp_created')
condo_filter = CondoFilter(request.GET, queryset=condo_list)
paginator = Paginator(condo_filter.qs, MAX_CONDOS_PER_PAGE)
page = request.GET.get('page')
try:
condos = paginator.page(page)
except PageNotAnInteger:
condos = paginator.page(1)
except EmptyPage:
condos = paginator.page(paginator.num_pages)
return render(request, 'app/index.html',
'title': 'Home',
'condos': condos,
'page': page,
'condo_filter': condo_filter,
)
这是一个工作示例:
.
【讨论】:
超过 9 页的切片将无法完美运行。但是这个模板中已经有足够的条件了,过滤器的目的是减少页码,所以没有必要改变它。【参考方案5】:我花了一些时间找到 DRYer 和更清洁的解决方案来解决这个问题,我认为最好的解决方案是使用模板标签的解决方案。
from django import template
register = template.Library()
@register.simple_tag
def relative_url(value, field_name, urlencode=None):
url = '?='.format(field_name, value)
if urlencode:
querystring = urlencode.split('&')
filtered_querystring = filter(lambda p: p.split('=')[0] != field_name, querystring)
encoded_querystring = '&'.join(filtered_querystring)
url = '&'.format(url, encoded_querystring)
return url
在你的模板中
<a href="% relative_url i 'page' request.GET.urlencode %"> i </a>
来源:Dealing With QueryString Parameters
【讨论】:
【参考方案6】:我对分页结果“记住过滤器/查询 URL 参数”的方法:将当前 URL 参数作为上下文变量传递:
# views.py
class PublicationFilterView(FilterView):
model = Publication
filterset_class = PublicationFilter
paginate_by = 15
def get_context_data(self, *args, **kwargs):
_request_copy = self.request.GET.copy()
parameters = _request_copy.pop('page', True) and _request_copy.urlencode()
context = super().get_context_data(*args, **kwargs)
context['parameters'] = parameters
return context
# templates/path/to/pagination.html
<a href="?page= page_obj.next_page_number & parameters ">
Next
</a>
【讨论】:
【参考方案7】:这个 100% 适合我
views.py:
def search(request):
category=Category.objects.all()
try:
qs=request.GET["qs"]
products=Product.objects.filter(Q(name__icontains=qs) |Q(details__icontains=qs) | Q(category__name__icontains=qs) | Q(branch__child__icontains=qs) | Q(manufacturer__name__icontains=qs) | Q(color__name__icontains=qs)).distinct()
print(products)
search=f"qs=qs"
except:
search=None
在 HTML 中
<ul class="shop-p__pagination">
% if products.has_provious %
<li>
<a class="fas fa-angle-left" href="?page= products.previous_page_number &search"></a></li>
% endif %
% for i in products.paginator.page_range %
% if products.number == i %
<li class="is-active"><a href="?page=i&search">i</a></li>
% else %
<li><a href="?page=i&search">i</a></li>
% endif %
% endfor %
% if products.has_next %
<li>
<a class="fas fa-angle-right" href="?page= products.next_page_number &search"></a></li>
% endif %
</ul>
【讨论】:
【参考方案8】:据我了解,您的目标是对过滤后的查询集进行分页。如果是这样,您可以将 PublicationFilter 对象的“qs”属性传递给 Paginator 构造函数:
def search(request):
qs = local_url.objects.filter(global_url__id=1).all()
url_filter = PublicationFilter(request.GET, queryset=qs)
paginator = Paginator(url_filter.qs, 25)
page = request.GET.get('page')
try:
pub = paginator.page(page)
except PageNotAnInteger:
pub = paginator.page(1)
except EmptyPage:
pub = paginator.page(paginator.num_pages)
url_filter = PublicationFilter(request.GET, queryset=qs)
return render(request, 'ingester/search_list.html', 'publication':pub)
url_filter.qs 包含过滤的查询集url_filter.queryset 包含未过滤的查询集
【讨论】:
【参考方案9】: 简单又甜美, 使用这个,pip install filter-and-pagination
https://pypi.org/project/filter-and-pagination/
实施步骤
-
安装包
pip install filter-and-pagination
通过from filter_and_pagination import FilterPagination
在view.py中导入FilterPagination
在您的函数中按照以下标准编写代码...
queryset = FilterPagination.filter_and_pagination(request, Customer)
serialize_data = CustomerSerializer(queryset['queryset'], many=True).data
resultset = 'dataset': serialize_data, 'pagination': queryset['pagination']
在这段代码中Customer
是Django模型&
CustomerSerializer
是一个 DRF 序列化器类
-
在结果集中它包含数据集和分页数据,采用这种格式(API 响应)链接:https://github.com/ashish1997it/filter-pagination-dj#demo
对于 API 请求,请按照 PostMan 集合链接:https://github.com/ashish1997it/filter-pagination-dj#postman 在标题部分中,它将采用您根据需要自定义的参数和请求
如果您仍然遇到任何困难,请联系我:)
【讨论】:
嘿,@AlexWinkler 我在我的答案中添加了实施步骤,通过它:) 感谢您稍微改进了文档,但仍然没有运气。这是我正在研究的一个 SOF 问题,我尝试了基于函数的视图方法,但在基于类的方面取得了更多进展。 ***.com/questions/63719277/…【参考方案10】:在get_context_data()
函数中:
form_submitted = 'csrfmiddlewaretoken' in self.request.GET
context['cleaned_full_path'] = ''.format(
self.request.get_full_path().split('&page' if form_submitted else '?page')[0],
'&' if form_submitted else '?'
)
然后,在您的模板中,加载类似
<a href=" cleaned_full_path page= page_obj.paginator.num_pages "
【讨论】:
以上是关于django-filter 使用分页的主要内容,如果未能解决你的问题,请参考以下文章