簡體   English   中英

django-filter 使用分頁

[英]django-filter use paginations

我正在使用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})

這對我有用:

在我的模板中而不是使用這個

<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 %}

我希望它有幫助:)

要使用 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屬性,其中包含過濾后的查詢集,您甚至可以根據需要覆蓋它

  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 過濾器與 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 表以及 django-filters 和 Paginator 來完成。 以下是我的視圖和模板文件。 需要模板標簽來確保將正確的參數傳遞給分頁 url。

搜索視圖.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()

這里最重要的部分是您如何在模板中構建 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

就我而言,我將“標題”作為過濾器,所以我需要檢查它是否已經存在。

這是我為我的項目所做的完美工作的片段。

模板/分頁.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>

這是視圖,以防萬一:

應用程序/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,
    })

這是一個工作示例:

.

我花了一些時間找到 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>

資料來源: 處理 QueryString 參數

我對分頁結果“記住過濾器/查詢 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>

這個和我 100% 合作

視圖.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>

據我了解,您的目標是對過濾后的查詢集進行分頁。 如果是這樣,您可以將 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包含過濾的 QuerySet
url_filter.queryset包含未過濾的 QuerySet


實施步驟

  1. 通過pip install filter-and-pagination安裝包
  2. from filter_and_pagination import FilterPagination在 vi​​ew.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 在標題部分中,它將采用您根據需要自定義的參數和請求

如果您仍然遇到任何困難,請聯系我 :)

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 }}"

除了@stathoula 和對@Benbb96 的回應,我設法用正則表達式刪除了額外的page參數,覆蓋了基於類的視圖中的setup方法:

import re

...

class MyView(ListView):
...

    def setup(self, request, *args, **kwargs) -> None:
        request.GET.get("page")
        request.META["QUERY_STRING"] = re.sub("(&|\?)page=(.)*", "", request.META.get("QUERY_STRING", ""))
        return super().setup(request, *args, **kwargs)

希望它可以幫助任何人!

更多信息:

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM