如何使用 django_filter 的分页
how can I use pagination with django_filter
在我的 TemplateView 中使用 django_filter 后出现分页问题。
在使用 django_filter 之前,我的分页工作正常,但现在它显示了每一页中的所有项目,我一直在网上寻找,但我没有找到一个好的解决方案。
我该如何解决?
谢谢
我的filter.py
class SnippetFilter(django_filters.FilterSet):
area = []
tecnology = Technology.objects.values('parent_area').distinct().order_by('parent_area_id')
for a in tecnology:
area.append(a['parent_area'])
parent_area = django_filters.ModelMultipleChoiceFilter(queryset=ApplicationArea.objects.filter(id__in=area), widget=forms.CheckboxSelectMultiple)
class Meta:
model = Technology
fields = ['title', 'category', 'kind', 'patent', 'patent_type', 'parent_area']
我的view.py
class TechnologyListView(LoginRequiredMixin, ListView):
model = Technology
template_name = "technology/technology.html"
paginate_by = 9
def get_queryset(self, *args, **kwargs):
queryset = super(TechnologyListView, self).get_queryset()
if self.request.user.is_authenticated and self.request.user.is_superuser or self.request.user.is_authenticated and self.request.user.is_staff:
queryset = Technology.objects.all()
elif self.request.user.is_authenticated and self.request.user.is_technology:
queryset = Technology.objects.filter(user=self.request.user).order_by('-id')
return queryset
def get_context_data(self, **kwargs):
selected_elements = []
data = super(TechnologyListView, self).get_context_data(**kwargs)
if self.request.user.is_authenticated and self.request.user.is_technology:
data['finished'] = Technology.objects.filter(user=self.request.user, category=0).count()
data['developed'] = Technology.objects.filter(user=self.request.user, category=1).count()
data['product'] = Technology.objects.filter(user=self.request.user, kind=0).count()
data['process'] = Technology.objects.filter(user=self.request.user, kind=1).count()
data['software'] = Technology.objects.filter(user=self.request.user, kind=2).count()
data['deposited'] = Technology.objects.filter(user=self.request.user, patent=0).count()
data['licensed'] = Technology.objects.filter(user=self.request.user, patent=1).count()
data['donthave'] = Technology.objects.filter(user=self.request.user, patent=2).count()
elif self.request.user.is_authenticated and self.request.user.is_superuser or self.request.user.is_authenticated and self.request.user.is_staff :
data['finished'] = Technology.objects.filter(category=0).count()
data['developed'] = Technology.objects.filter(category=1).count()
data['product'] = Technology.objects.filter(kind=0).count()
data['process'] = Technology.objects.filter(kind=1).count()
data['software'] = Technology.objects.filter(kind=2).count()
data['deposited'] = Technology.objects.filter(patent=0).count()
data['licensed'] = Technology.objects.filter(patent=1).count()
data['dontthave'] = Technology.objects.filter(patent=2).count()
data['filter'] = SnippetFilter(self.request.GET, queryset=self.get_queryset())
return data
template.html
<div class="container-fluid" >
<div class="row">
<div class="col-sm-2">
<div style="margin-top:20%; margin-bottom:25% ">
<h1 class="title text-center">Filtro:</h1>
<form method="GET" action="{% url 'technology:tech_index' %}" novalidate>
{{filter.form|bootstrap}}
<a class="btn btn-rw btn-danger" href="{% url 'technology:tech_index' %}">Limpar Filtro</a>
<input type='submit' value='Procurar' class='btn btn-rw btn-primary'>
</form>
</div>
</div>
<div class="col-sm-8 col-sm-offset-1">
<br>
<a class="btn btn-rw btn-primary" href="{% url 'technology:create_tech' %}"><i class="fa fa-bolt">
</i>Adicionar Tecnologia</a>
<h1 class="title text-center">Minhas Tecnologias</h1>
{% if filter.qs.count > 0 %}
<div class="tab-content tab-shop mt15" style="align-items: center !important;">
<div class="row" >
<div class="col-sm-4 col-md-4 col-lg-4">
<canvas id="category"></canvas>
</div>
<div class="col-sm-4 col-md-4 col-lg-4">
<canvas id="type" ></canvas>
</div>
<div class="col-sm-4 col-md-4 col-lg-4">
<canvas id="patent"></canvas>
</div>
<!-- <div class="col-lg-3 col-md-3 col-sm-3">
<canvas id="area_ap" width="80" height="80"></canvas>
</div> -->
</div>
<br>
</div>
<br>
<div class="tab-content tab-shop mt15">
<div id="home" class="tab-pane row fade in active">
{% for item in filter.qs %}
<div class="col-lg-4 col-md-4 col-sm-6 mb30">
<div class="view no-margin" style="background-color: #cfcfcf">
<!-- Blog Thumb -->
<div class="product-container">
{% if item.area_img %}
<img class="img-responsive full-width" style="width:100%; height:200px" src="{{item.area_img.image.url}}" alt="...">
{% else %}
<img class="img-responsive full-width" style="width:100%; height:200px" src="{% static 'images/notavailable.png' %}" alt="...">
{% endif %}
</div>
<div class="mask">
<div class="image-hover-content">
<!-- Zoom + Blog Link -->
<a href="{% url 'technology:detail_tech' item.pk %}" >
<div class="image-icon-holder"><span data-toggle="tooltip" data-placement="top" title="Visualizar" class="ion-eye image-icons"style="color:#fff"></span></div>
</a>
{% if request.user.is_authenticated and request.user.is_technology %}
<a href="{% url 'technology:update_tech' item.pk %}">
<div class="image-icon-holder"><span data-toggle="tooltip" data-placement="top" title="Editar" class="ion-edit image-icons" style="color:#fff"></span></div>
</a>
<a data-toggle="modal" data-target="#delete-{{item.pk}}">
<div class="image-icon-holder"><span data-toggle="tooltip" data-placement="top" title="Deletar" class="ion-ios7-trash image-icons" style="color:red"></span></div>
</a>
{% endif %}
<div class="modal fade" id="delete-{{item.pk}}" tabindex="-1" role="dialog" aria-labelledby="myModalLabel">
<div class="modal-dialog" role="document">
<div class="modal-content text-center">
<form action="{% url 'technology:delete_tech' pk=item.pk %}" method="post">
{% csrf_token %}
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal" aria-label="Close"><span aria-hidden="true">×</span></button>
<h2>Deletar</h2>
</div>
<div class="modal-body">
<p>Você deseja realmente deletar "{{ item }}"?</p>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-success" data-dismiss="modal">Voltar</button>
<input class="btn btn-danger" type="submit" value="Confirmar" />
</div>
</form>
</div>
</div>
</div>
</div><!-- /image hover content -->
</div><!-- /mask-->
</div>
<div class="shop-product content-box-shadow">
<a href="{% url 'technology:update_tech' item.pk %}"><h2>{{item.title}}</h2></a>
{% if item.description %}
<p>{{item.description|truncatechars:40}}</p>
{% else %}
<p class="text-danger">Não há descrição</p>
{% endif %}
</div>
</div>
{% if forloop.counter|divisibleby:4 %}
</li>
<li>
{% endif %}
{% endfor %}
</div>
</div>
{% include "includes/paginator.html" %}
{% else %}
<div class="tab-content tab-shop mt15 text-center " style="padding-top: 25%; padding-bottom: 25%">
<div class="row">
<h1 class="text-info">Nenhuma tecnologia cadastrada</h1>
</div>
</div>
{% endif %}
</div>
</div>
paginator.html
{% if is_paginated %}
<ul class="pagination">
{% if page_obj.has_previous %}
<li class="page-item">
<a href="?page={{ page_obj.previous_page_number }}">«</a>
</li>
{% else %}
<li class="disabled"><span class="page-link">«
</span></li>
{% endif %}
{% for page_num in paginator.page_range %}
{% if page_obj.number == page_num %}
<li class="page-item active">
<span class="page-link">
{{ page_num }}
<span class="sr-only">(current)</span>
</span>
</li>
{% elif page_num > page_obj.number|add:'-2' and page_num < page_obj.number|add:4 %}
<li class="page-item">
<a class="page-link" href="?page={{ page_num }}">{{page_num}}</a>
</li>
{% endif %}
{% endfor %}
{% if page_obj.has_next %}
<li class="page-item">
<a class="page-link" href="?page={{ page_obj.next_page_number }}">»</a></li>
{% else %}
<li class="page-item disabled">
<span class="page-link">
»
</span>
</li>
{% endif %}
</ul>
{% endif %}
就我个人而言,我将 django-tables 与 django-filter 一起使用,它会为我处理分页,所以我不是这方面的专家。但是,一旦您在视图中看到过滤器的结果,您似乎就需要对其进行分页。也许是这样的?
paginator = Paginator(data, 10)
在 SO 上查看此帖子。 and also this website explains how to do it generically. https://djangopy.org/how-to/pagination-with-django/
我遇到这个问题有一段时间了,发现这个解决方案是我最喜欢的。
django-filter 的 FilterView 已经支持与 ListView 相同的分页,除了如何使其工作不是很明显。您可以通过在应用过滤器时更改浏览器地址栏中的 'page=2' 来尝试此操作,果然它将转到下一个正确的页面。
所以只需几步就可以让它工作...
创建一个视图 mixin,它将 'page' 关键字(django 分页的默认设置)和 returns 剩余的查询字符串分开作为新的模板上下文变量。
class PaginatedFilterViews(View):
def get_context_data(self, **kwargs):
context = super(PaginatedFilterViews, self).get_context_data(**kwargs)
if self.request.GET:
querystring = self.request.GET.copy()
if self.request.GET.get('page'):
del querystring['page']
context['querystring'] = querystring.urlencode()
return context
然后将这个新对象包含在您想要分页的所有 FilterView 中...即:
class FilteredList(PaginatedFilterViews, FilterView):
model = Whatever
paginate_by = 10 # or whatever
# the rest of your view code
并更新您的分页模板以插入其余的 django-filter 查询字符串...
{% if page_obj.has_previous %}
<li>
<a href="?page=1{% if querystring %}&{{ querystring }}{% endif %}">
<i class="icon-page-first"></i>
<span class="btn-text">First</span>
</a>
</li>
<li>
<a href="?page={{ page_obj.previous_page_number }}{% if querystring %}&{{ querystring }}{% querystring %}">
<i class="icon-page-back"></i>
<span class="btn-text">Prev</span>
</a>
</li>
{% else %}
<li class="disabled">
<span class="icon-page-first">
<span class="btn-text">First</span>
</span>
</li>
<li class="disabled">
<span class="icon-page-back">
<span class="btn-text">Prev</span>
</span>
</li>
{# etc etc #}
{% endif %}
在很多视图中对我来说效果很好,不需要其他第 3 方依赖项。
回复晚了,但我会为未来的人分享我的解决方案。
我遇到了同样的问题,django-filter 和 pagination 各自都可以正常工作,但是结合起来只会过滤第一页。
这是因为 django-filters 通过请求发送过滤器参数,分页也是如此。因此,当您在 django-filter 表单上点击提交时,您的查询参数将附加到请求中。这可能看起来像:
'/?name=&project=&status=1'
类似地,当您点击下一页时,您可能将其设置为 link,它将页码附加到请求中。可能看起来像:
'/?page=2'
我找到的解决方案很老套,但它确实有效。我所做的是读取请求字典并创建一个格式为 &{parameter_name}={parameter_value} 的字符串以传递上下文,并附加到分页 links。
这个循环看起来像:
views.py
new_request = ''
for i in request.GET:
if i != 'page':
val = request.GET.get(i)
new_request += f"&{i}={val}"
context = {...'new_request':new_request...}
然后您可以在分页末尾将其添加到模板中 link href:
<a href="?page={{ page_obj.next_page_number }}{{ new_request }}">Next</a>
这将在浏览页面时通过 GET 请求保留您的过滤器选择。
在我的 TemplateView 中使用 django_filter 后出现分页问题。 在使用 django_filter 之前,我的分页工作正常,但现在它显示了每一页中的所有项目,我一直在网上寻找,但我没有找到一个好的解决方案。 我该如何解决? 谢谢
我的filter.py
class SnippetFilter(django_filters.FilterSet):
area = []
tecnology = Technology.objects.values('parent_area').distinct().order_by('parent_area_id')
for a in tecnology:
area.append(a['parent_area'])
parent_area = django_filters.ModelMultipleChoiceFilter(queryset=ApplicationArea.objects.filter(id__in=area), widget=forms.CheckboxSelectMultiple)
class Meta:
model = Technology
fields = ['title', 'category', 'kind', 'patent', 'patent_type', 'parent_area']
我的view.py
class TechnologyListView(LoginRequiredMixin, ListView):
model = Technology
template_name = "technology/technology.html"
paginate_by = 9
def get_queryset(self, *args, **kwargs):
queryset = super(TechnologyListView, self).get_queryset()
if self.request.user.is_authenticated and self.request.user.is_superuser or self.request.user.is_authenticated and self.request.user.is_staff:
queryset = Technology.objects.all()
elif self.request.user.is_authenticated and self.request.user.is_technology:
queryset = Technology.objects.filter(user=self.request.user).order_by('-id')
return queryset
def get_context_data(self, **kwargs):
selected_elements = []
data = super(TechnologyListView, self).get_context_data(**kwargs)
if self.request.user.is_authenticated and self.request.user.is_technology:
data['finished'] = Technology.objects.filter(user=self.request.user, category=0).count()
data['developed'] = Technology.objects.filter(user=self.request.user, category=1).count()
data['product'] = Technology.objects.filter(user=self.request.user, kind=0).count()
data['process'] = Technology.objects.filter(user=self.request.user, kind=1).count()
data['software'] = Technology.objects.filter(user=self.request.user, kind=2).count()
data['deposited'] = Technology.objects.filter(user=self.request.user, patent=0).count()
data['licensed'] = Technology.objects.filter(user=self.request.user, patent=1).count()
data['donthave'] = Technology.objects.filter(user=self.request.user, patent=2).count()
elif self.request.user.is_authenticated and self.request.user.is_superuser or self.request.user.is_authenticated and self.request.user.is_staff :
data['finished'] = Technology.objects.filter(category=0).count()
data['developed'] = Technology.objects.filter(category=1).count()
data['product'] = Technology.objects.filter(kind=0).count()
data['process'] = Technology.objects.filter(kind=1).count()
data['software'] = Technology.objects.filter(kind=2).count()
data['deposited'] = Technology.objects.filter(patent=0).count()
data['licensed'] = Technology.objects.filter(patent=1).count()
data['dontthave'] = Technology.objects.filter(patent=2).count()
data['filter'] = SnippetFilter(self.request.GET, queryset=self.get_queryset())
return data
template.html
<div class="container-fluid" >
<div class="row">
<div class="col-sm-2">
<div style="margin-top:20%; margin-bottom:25% ">
<h1 class="title text-center">Filtro:</h1>
<form method="GET" action="{% url 'technology:tech_index' %}" novalidate>
{{filter.form|bootstrap}}
<a class="btn btn-rw btn-danger" href="{% url 'technology:tech_index' %}">Limpar Filtro</a>
<input type='submit' value='Procurar' class='btn btn-rw btn-primary'>
</form>
</div>
</div>
<div class="col-sm-8 col-sm-offset-1">
<br>
<a class="btn btn-rw btn-primary" href="{% url 'technology:create_tech' %}"><i class="fa fa-bolt">
</i>Adicionar Tecnologia</a>
<h1 class="title text-center">Minhas Tecnologias</h1>
{% if filter.qs.count > 0 %}
<div class="tab-content tab-shop mt15" style="align-items: center !important;">
<div class="row" >
<div class="col-sm-4 col-md-4 col-lg-4">
<canvas id="category"></canvas>
</div>
<div class="col-sm-4 col-md-4 col-lg-4">
<canvas id="type" ></canvas>
</div>
<div class="col-sm-4 col-md-4 col-lg-4">
<canvas id="patent"></canvas>
</div>
<!-- <div class="col-lg-3 col-md-3 col-sm-3">
<canvas id="area_ap" width="80" height="80"></canvas>
</div> -->
</div>
<br>
</div>
<br>
<div class="tab-content tab-shop mt15">
<div id="home" class="tab-pane row fade in active">
{% for item in filter.qs %}
<div class="col-lg-4 col-md-4 col-sm-6 mb30">
<div class="view no-margin" style="background-color: #cfcfcf">
<!-- Blog Thumb -->
<div class="product-container">
{% if item.area_img %}
<img class="img-responsive full-width" style="width:100%; height:200px" src="{{item.area_img.image.url}}" alt="...">
{% else %}
<img class="img-responsive full-width" style="width:100%; height:200px" src="{% static 'images/notavailable.png' %}" alt="...">
{% endif %}
</div>
<div class="mask">
<div class="image-hover-content">
<!-- Zoom + Blog Link -->
<a href="{% url 'technology:detail_tech' item.pk %}" >
<div class="image-icon-holder"><span data-toggle="tooltip" data-placement="top" title="Visualizar" class="ion-eye image-icons"style="color:#fff"></span></div>
</a>
{% if request.user.is_authenticated and request.user.is_technology %}
<a href="{% url 'technology:update_tech' item.pk %}">
<div class="image-icon-holder"><span data-toggle="tooltip" data-placement="top" title="Editar" class="ion-edit image-icons" style="color:#fff"></span></div>
</a>
<a data-toggle="modal" data-target="#delete-{{item.pk}}">
<div class="image-icon-holder"><span data-toggle="tooltip" data-placement="top" title="Deletar" class="ion-ios7-trash image-icons" style="color:red"></span></div>
</a>
{% endif %}
<div class="modal fade" id="delete-{{item.pk}}" tabindex="-1" role="dialog" aria-labelledby="myModalLabel">
<div class="modal-dialog" role="document">
<div class="modal-content text-center">
<form action="{% url 'technology:delete_tech' pk=item.pk %}" method="post">
{% csrf_token %}
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal" aria-label="Close"><span aria-hidden="true">×</span></button>
<h2>Deletar</h2>
</div>
<div class="modal-body">
<p>Você deseja realmente deletar "{{ item }}"?</p>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-success" data-dismiss="modal">Voltar</button>
<input class="btn btn-danger" type="submit" value="Confirmar" />
</div>
</form>
</div>
</div>
</div>
</div><!-- /image hover content -->
</div><!-- /mask-->
</div>
<div class="shop-product content-box-shadow">
<a href="{% url 'technology:update_tech' item.pk %}"><h2>{{item.title}}</h2></a>
{% if item.description %}
<p>{{item.description|truncatechars:40}}</p>
{% else %}
<p class="text-danger">Não há descrição</p>
{% endif %}
</div>
</div>
{% if forloop.counter|divisibleby:4 %}
</li>
<li>
{% endif %}
{% endfor %}
</div>
</div>
{% include "includes/paginator.html" %}
{% else %}
<div class="tab-content tab-shop mt15 text-center " style="padding-top: 25%; padding-bottom: 25%">
<div class="row">
<h1 class="text-info">Nenhuma tecnologia cadastrada</h1>
</div>
</div>
{% endif %}
</div>
</div>
paginator.html
{% if is_paginated %}
<ul class="pagination">
{% if page_obj.has_previous %}
<li class="page-item">
<a href="?page={{ page_obj.previous_page_number }}">«</a>
</li>
{% else %}
<li class="disabled"><span class="page-link">«
</span></li>
{% endif %}
{% for page_num in paginator.page_range %}
{% if page_obj.number == page_num %}
<li class="page-item active">
<span class="page-link">
{{ page_num }}
<span class="sr-only">(current)</span>
</span>
</li>
{% elif page_num > page_obj.number|add:'-2' and page_num < page_obj.number|add:4 %}
<li class="page-item">
<a class="page-link" href="?page={{ page_num }}">{{page_num}}</a>
</li>
{% endif %}
{% endfor %}
{% if page_obj.has_next %}
<li class="page-item">
<a class="page-link" href="?page={{ page_obj.next_page_number }}">»</a></li>
{% else %}
<li class="page-item disabled">
<span class="page-link">
»
</span>
</li>
{% endif %}
</ul>
{% endif %}
就我个人而言,我将 django-tables 与 django-filter 一起使用,它会为我处理分页,所以我不是这方面的专家。但是,一旦您在视图中看到过滤器的结果,您似乎就需要对其进行分页。也许是这样的?
paginator = Paginator(data, 10)
在 SO 上查看此帖子。
我遇到这个问题有一段时间了,发现这个解决方案是我最喜欢的。
django-filter 的 FilterView 已经支持与 ListView 相同的分页,除了如何使其工作不是很明显。您可以通过在应用过滤器时更改浏览器地址栏中的 'page=2' 来尝试此操作,果然它将转到下一个正确的页面。
所以只需几步就可以让它工作...
创建一个视图 mixin,它将 'page' 关键字(django 分页的默认设置)和 returns 剩余的查询字符串分开作为新的模板上下文变量。
class PaginatedFilterViews(View):
def get_context_data(self, **kwargs):
context = super(PaginatedFilterViews, self).get_context_data(**kwargs)
if self.request.GET:
querystring = self.request.GET.copy()
if self.request.GET.get('page'):
del querystring['page']
context['querystring'] = querystring.urlencode()
return context
然后将这个新对象包含在您想要分页的所有 FilterView 中...即:
class FilteredList(PaginatedFilterViews, FilterView):
model = Whatever
paginate_by = 10 # or whatever
# the rest of your view code
并更新您的分页模板以插入其余的 django-filter 查询字符串...
{% if page_obj.has_previous %}
<li>
<a href="?page=1{% if querystring %}&{{ querystring }}{% endif %}">
<i class="icon-page-first"></i>
<span class="btn-text">First</span>
</a>
</li>
<li>
<a href="?page={{ page_obj.previous_page_number }}{% if querystring %}&{{ querystring }}{% querystring %}">
<i class="icon-page-back"></i>
<span class="btn-text">Prev</span>
</a>
</li>
{% else %}
<li class="disabled">
<span class="icon-page-first">
<span class="btn-text">First</span>
</span>
</li>
<li class="disabled">
<span class="icon-page-back">
<span class="btn-text">Prev</span>
</span>
</li>
{# etc etc #}
{% endif %}
在很多视图中对我来说效果很好,不需要其他第 3 方依赖项。
回复晚了,但我会为未来的人分享我的解决方案。
我遇到了同样的问题,django-filter 和 pagination 各自都可以正常工作,但是结合起来只会过滤第一页。
这是因为 django-filters 通过请求发送过滤器参数,分页也是如此。因此,当您在 django-filter 表单上点击提交时,您的查询参数将附加到请求中。这可能看起来像:
'/?name=&project=&status=1'
类似地,当您点击下一页时,您可能将其设置为 link,它将页码附加到请求中。可能看起来像:
'/?page=2'
我找到的解决方案很老套,但它确实有效。我所做的是读取请求字典并创建一个格式为 &{parameter_name}={parameter_value} 的字符串以传递上下文,并附加到分页 links。
这个循环看起来像:
views.py
new_request = ''
for i in request.GET:
if i != 'page':
val = request.GET.get(i)
new_request += f"&{i}={val}"
context = {...'new_request':new_request...}
然后您可以在分页末尾将其添加到模板中 link href:
<a href="?page={{ page_obj.next_page_number }}{{ new_request }}">Next</a>
这将在浏览页面时通过 GET 请求保留您的过滤器选择。