【问题标题】:django-filter use paginationsdjango-filter 使用分页
【发布时间】: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 你觉得任何答案有帮助吗?

标签: python django pagination django-filter


【解决方案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 过滤器并对过滤后的结果进行分页,您可以执行以下操作:

  1. 为您的模型创建一个过滤器类:

    开启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', ...]
    
  2. 每个FilterSet 对象都有一个.qs 属性,其中包含过滤后的查询集,您甚至可以override it if you want

  3. 我们将对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 %}">&laquo; 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 &raquo;</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 的键值对中,您还需要将 &amp;page=5 作为对传递。

    正如@stathoula 提到的,您需要检查查询字符串中是否已经存在至少一个过滤器字段。如果是,那么您需要使用已经存在的键值对,然后是新的 &amp;page=3 对。

    看起来很简单,但是当用户点击箭头时,我不得不做一些小技巧,不要在查询字符串中一遍又一遍地重复&amp;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】:

            实施步骤

            1. 安装包pip install filter-and-pagination
            2. 通过from filter_and_pagination import FilterPagination在view.py中导入FilterPagination
            3. 在您的函数中按照以下标准编写代码...
            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 序列化器类
            1. 在结果集中它包含数据集和分页数据,采用这种格式(API 响应)链接:https://github.com/ashish1997it/filter-pagination-dj#demo
            2. 对于 API 请求,请按照 PostMan 集合链接:https://github.com/ashish1997it/filter-pagination-dj#postman 在标题部分中,它将采用您根据需要自定义的参数和请求

            如果您仍然遇到任何困难,请联系我:)

            【讨论】:

            • 嘿,@AlexWinkler 我在我的答案中添加了实施步骤,通过它:)
            • 感谢您稍微改进了文档,但仍然没有运气。这是我正在研究的一个 SOF 问题,我尝试了基于函数的视图方法,但在基于类的方面取得了更多进展。 stackoverflow.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 '?'
            )
            

            然后,在您的模板中,加载类似

            &lt;a href="{{ cleaned_full_path }}page={{ page_obj.paginator.num_pages }}"

            【讨论】:

              猜你喜欢
              • 2021-12-29
              • 1970-01-01
              • 2019-06-26
              • 2020-03-05
              • 1970-01-01
              • 1970-01-01
              • 2018-01-31
              • 2020-07-17
              • 2011-04-13
              相关资源
              最近更新 更多