فصل ۱۳: Pagination در Django — صفحه‌بندی دیتا

""

۱. مقدمه: چرا به صفحه‌بندی نیاز داریم؟

تصور کن فروشگاه آنلاین ما صدها یا هزاران محصول داره.
اگر همه رو یکجا توی یک صفحه نشون بدیم:

  • کاربر باید کلی اسکرول کنه
  • سرعت لود صفحه پایین میاد
  • فشار زیادی روی سرور و دیتابیس میاد

✅ راه‌حل: Pagination (صفحه‌بندی) → تقسیم داده به صفحات کوچک‌تر و مدیریت ناوبری بین صفحات.


۲. دو روش پیاده‌سازی Pagination در Django

  1. با استفاده از کلاس ListView (CBV) — ساده‌تر چون Pagination داخلی داره.
  2. با استفاده از Paginator کلاس جنگو — برای وقتی که از FBV استفاده می‌کنیم یا شخصی‌سازی زیاد میخوایم.

ما هر دو رو یاد می‌گیریم.


۳. روش اول — Pagination با ListView

در فصل قبل ما یک ویوی ProductListView داشتیم که با CBV نوشته بودیم.
حالا کافیه خط زیر رو بهش اضافه کنیم:

from django.views.generic import ListView
from .models import Product

class ProductListView(ListView):
    model = Product
    template_name = 'product_list.html'
    context_object_name = 'products'
    paginate_by = 6  # تعداد آیتم در هر صفحه

📌 داخل urls.py هم داریم:

path('products/', ProductListView.as_view(), name='product_list'),

قالب HTML برای صفحه‌بندی

<h1>لیست محصولات</h1>

{% for product in products %}
    <div>
        <h3>{{ product.name }}</h3>
        {% if product.image %}
            <img src="{{ product.image.url }}" width="120">
        {% endif %}
        <p>قیمت: {{ product.price }} تومان</p>
    </div>
{% empty %}
    <p>محصولی یافت نشد</p>
{% endfor %}

<hr>

<!-- ناوبری صفحه‌ها -->
{% if is_paginated %}
    <div>
        {% if page_obj.has_previous %}
            <a href="?page={{ page_obj.previous_page_number }}">⬅ قبلی</a>
        {% endif %}

        <span>صفحه {{ page_obj.number }} از {{ page_obj.paginator.num_pages }}</span>

        {% if page_obj.has_next %}
            <a href="?page={{ page_obj.next_page_number }}">بعدی ➡</a>
        {% endif %}
    </div>
{% endif %}

نکته:

  • is_paginated → اگر صفحه‌بندی فعال باشه، True میشه
  • page_obj → اطلاعات صفحه فعلی (شماره، صفحات بعدی/قبلی و …) رو داره

۴. روش دوم — Pagination با Paginator در FBV

برای کنترل دستی، باید ماژول زیر رو ایمپورت کنیم:

from django.core.paginator import Paginator

مثال:

from django.shortcuts import render
from django.core.paginator import Paginator
from .models import Product

def product_list(request):
    product_list = Product.objects.all()
    paginator = Paginator(product_list, 6)  # تعداد آیتم در هر صفحه
    page_number = request.GET.get('page')   # شماره صفحه از URL
    page_obj = paginator.get_page(page_number)

    return render(request, 'product_list.html', {'page_obj': page_obj})

📌 در قالب، به جای products از page_obj استفاده می‌کنیم:

{% for product in page_obj %}
    <h3>{{ product.name }}</h3>
{% endfor %}

<!-- دکمه‌های ناوبری -->
{% if page_obj.has_previous %}
    <a href="?page={{ page_obj.previous_page_number }}">⬅ قبلی</a>
{% endif %}

<span>صفحه {{ page_obj.number }} از {{ page_obj.paginator.num_pages }}</span>

{% if page_obj.has_next %}
    <a href="?page={{ page_obj.next_page_number }}">بعدی ➡</a>
{% endif %}

۵. شخصی‌سازی Pagination

گاهی نیاز داریم تعداد آیتم‌ها رو با انتخاب کاربر تغییر بدیم.
مثال:

def product_list(request):
    per_page = request.GET.get('per_page', 6)  # پیش‌فرض 6
    products = Product.objects.all()
    paginator = Paginator(products, per_page)
    page_number = request.GET.get('page')
    page_obj = paginator.get_page(page_number)

    return render(request, 'product_list.html', {'page_obj': page_obj})

📌 حالا کاربر می‌تونه با URL مثل:

/products/?per_page=12

12 محصول در هر صفحه ببینه.


۶. بهبود UI صفحه‌بندی

در Bootstrap، می‌تونیم صفحه‌بندی زیباتر بسازیم:

{% if is_paginated %}
<ul class="pagination">
    {% if page_obj.has_previous %}
    <li class="page-item">
        <a class="page-link" href="?page={{ page_obj.previous_page_number }}">قبلی</a>
    </li>
    {% endif %}

    {% for num in page_obj.paginator.page_range %}
        {% if page_obj.number == num %}
            <li class="page-item active"><span class="page-link">{{ num }}</span></li>
        {% elif num > page_obj.number|add:'-3' and num < page_obj.number|add:'3' %}
            <li class="page-item"><a class="page-link" href="?page={{ num }}">{{ 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>
    {% endif %}
</ul>
{% endif %}

۷. نکات حرفه‌ای (Best Practices)

  1. ترتیب نمایش → قبل از صفحه‌بندی، order_by() رو روی QuerySet بزن.
  2. فیلتر و صفحه‌بندی هماهنگ → اگر کاربر محصولات رو فیلتر می‌کنه (مثلاً بر اساس دسته‌بندی)، همون QuerySet فیلتر شده رو صفحه‌بندی کن.
  3. حفظ پارامترهای جستجو → اگر کاربر جستجو کرده، توی لینک صفحه‌بندی باید پارامترهای جستجو (?search=...&page=2) رو نگه داری.
  4. استفاده از کش (cache) برای لیست‌های بزرگ.

۸. مثال نهایی — لیست محصولات فروشگاه با جستجو + صفحه‌بندی

views.py:

from django.shortcuts import render
from django.core.paginator import Paginator
from .models import Product

def product_list(request):
    search_query = request.GET.get('q', '')
    products = Product.objects.all()

    if search_query:
        products = products.filter(name__icontains=search_query)

    paginator = Paginator(products, 6)
    page_number = request.GET.get('page')
    page_obj = paginator.get_page(page_number)

    return render(request, 'product_list.html', {
        'page_obj': page_obj,
        'search_query': search_query
    })

product_list.html:

<form method="get">
    <input type="text" name="q" value="{{ search_query }}" placeholder="جستجو محصول...">
    <button type="submit">جستجو</button>
</form>

{% for product in page_obj %}
    <h3>{{ product.name }}</h3>
{% empty %}
    <p>محصولی یافت نشد</p>
{% endfor %}

{% if page_obj.has_previous %}
    <a href="?q={{ search_query }}&page={{ page_obj.previous_page_number }}">⬅ قبلی</a>
{% endif %}

صفحه {{ page_obj.number }} از {{ page_obj.paginator.num_pages }}

{% if page_obj.has_next %}
    <a href="?q={{ search_query }}&page={{ page_obj.next_page_number }}">بعدی ➡</a>
{% endif %}

۹. خطاهای رایج

خطاعلتراه‌حل
نمایش همه آیتم‌ها در یک صفحهفراموش کردن استفاده از paginate_by یا Paginatorاضافه کردن کد صفحه‌بندی
رفتن به صفحه نامعتبرورودی page خارج از محدودهاستفاده از get_page() به جای page()
گم شدن جستجو بعد از صفحه بعدیپارامترهای GET رو توی لینک قبلی/بعدی نگه نداشتناستفاده از ?q={{ search_query }}

۱۰. جمع‌بندی

در این فصل یاد گرفتیم:

  • مفاهیم صفحه‌بندی و دلیل اهمیتش
  • پیاده‌سازی با CBV (paginate_by)
  • پیاده‌سازی با FBV و کلاس Paginator
  • ایجاد صفحه‌بندی زیبا با Bootstrap
  • هماهنگ کردن جستجو با Pagination

محمد وب‌سایت

دیدگاهتان را بنویسید

نشانی ایمیل شما منتشر نخواهد شد. بخش‌های موردنیاز علامت‌گذاری شده‌اند *