Как обогатить постраничный навигатор, добавив поле из первого и последнего объектов в Django paginator?

Я создаю Django-приложение для управления процессом оцифровки рулонов аналоговой пленки.

Я хотел бы добавить поле film_roll.from_year модели FilmRoll в виджет постраничной навигации в шаблоне Django. Это значительно облегчает переход на нужную страницу, если посмотреть на диапазон year_from под каждой страницей.

Мой вид определяется следующим образом:

def index(request):
    film_roll_list = FilmRoll.objects.order_by(
        "from_year", "from_month", "title"
    ).annotate(
        scan_count=Count("myScanned_VS_NLP", distinct=True),
    )
    paginator = Paginator(film_roll_list, 20)
    page_number = request.GET.get("page")
    try:
        page_obj = paginator.get_page(page_number)
    except PageNotAnInteger:
        # If the page parameter is not an integer, show the first page
        page_obj = paginator.get_page(1)
    except EmptyPage:
        # If the page is out of range, show the last page
        page_obj = paginator.page(paginator.num_pages)

    film_roll_count = paginator.count

    context = {
        "film_roll_list": film_roll_list,
        "page_obj": page_obj,
        "film_roll_count": film_roll_count,
    }
    return render(request, "filmrolls/index.html", context)

Вот код навигатора страницы в шаблоне:

{# Page navigator: start #}
{% if page_obj.has_other_pages %}
<div class="row">
    <div class="btn-group" role="group" aria-label="Item pagination">
        {% if page_obj.has_previous %}
            <a href="?page={{ page_obj.previous_page_number }}" class="btn btn-outline-primary">&laquo;</a>
        {% endif %}

        {% for page_number in page_obj.paginator.page_range %}
            {% if page_obj.number == page_number %}
                <button class="btn btn-outline-primary active">
                    <span>{{ page_number }} <span class="sr-only"></span></span>
                    # TODO - add 'year_from' range for the current page
                </button>
            {% else %}
                {% if page_number == paginator.ELLIPSIS %}
                <button class="btn">
                    <span>{{ paginator.ELLIPSIS }} <span class="sr-only"></span></span>
                </button>
                {% else %}
                <a href="?page={{ page_number }}" class="btn btn-outline-primary">
                    {{ page_number }}
                    # TODO - add 'year_from' range for each other page
                </a>
                {% endif %}
            {% endif %}
        {% endfor %}

        {% if page_obj.has_next %}
            <a href="?page={{ page_obj.next_page_number }}" class="btn btn-outline-primary">&raquo;</a>
        {% endif %}
    </div>
</div>
{% endif %}
{# Page navigator: end #}

Поскольку коллекция рулонов пленки уже упорядочена по year_from, я, вероятно, смогу получить year_from из первого и последнего объекта в пагинационном списке.

Можно ли это добавить в шаблон Django, или мне придется вычислять эти свойства в представлении?

Самым чистым способом, вероятно, является создание пользовательской схемы пагинации:

from django.core.paginator import Page, Paginator

# Custom page class so you can add the range per page
class MoviePage(Page):
    def __init__(self, object_list, number, paginator):
        super().__init__(object_list, number, paginator)

        # Add the range
        if object_list:
            self.first_item_year = object_list[0].from_year
            self.last_item_year = object_list[-1].from_year

# Custom paginator for the purpose of invoking the custom page class
class MoviePaginator(Paginator):
    def _get_page(self, *args, **kwargs):
        return MoviePage(*args, **kwargs)

Приведенный выше код добавит в представление только диапазон для загружаемой страницы. Если вы пытаетесь отобразить диапазон для каждой страницы за один раз, я не уверен, насколько это осуществимо. Может быть, вы можете .annotate() набор запросов с помощью какого-то вида поиска, который выбирает каждый n элемент и добавляет его from_year атрибут в dict с индексом n.

Я нашел решение для реализации следующего: enter image description here

  1. Вид:
def index(request):
    # Paginator: max items per page:
    items_per_page = 20

    film_roll_list = FilmRoll.objects.order_by(
        "from_year", "from_month", "title"
    ).annotate(
        vs_nlp_scan_count=Count("myScanned_VS_NLP", distinct=True),
    )

    paginator = Paginator(film_roll_list, items_per_page)

    date_shot_start = {}
    date_shot_end = {}
    p = 1
    while p <= paginator.num_pages:
        index_start = (p - 1) * items_per_page + 1
        index_end = min(p * items_per_page + 1, paginator.count - 1)

        range_start_year = film_roll_list[index_start].from_year
        range_start_month = film_roll_list[index_start].from_month

        range_end_year = film_roll_list[index_end].from_year
        range_end_month = film_roll_list[index_end].from_month

        if range_start_year:
            if range_start_month:
                date_shot_start[p] = f"{range_start_year}-{range_start_month:02d}"
            else:
                date_shot_start[p] = f"{range_start_year}"
        else:
            date_shot_start[p] = "?"

        if range_end_year:
            if range_end_month:
                date_shot_end[p] = f"{range_end_year}-{range_end_month:02d}"
            else:
                date_shot_end[p] = f"{range_end_year}"
        else:
            date_shot_end[p] = "?"

        p += 1

    page_number = request.GET.get("page")
    try:
        page_obj = paginator.get_page(page_number)
    except PageNotAnInteger:
        # If the page parameter is not an integer, show the first page
        page_obj = paginator.get_page(1)
    except EmptyPage:
        # If the page is out of range, show the last page
        page_obj = paginator.page(paginator.num_pages)

    context = {
        "page_obj": page_obj,
        "date_shot_start": year_start,
        "date_shot_end": year_end,
    }
    return render(request, "filmrolls/index.html", context)
  1. Шаблон:
{% load index_tag %}

{# Page navigator: start #}
{% if page_obj.has_other_pages %}
<div class="row">
    <div class="btn-group" role="group" aria-label="Item pagination">
        {% if page_obj.has_previous %}
            <a href="?page={{ page_obj.previous_page_number }}" class="btn btn-outline-primary">&laquo;</a>
        {% endif %}

        {% for page_number in page_obj.paginator.page_range %}
            {% if page_obj.number == page_number %}
                <button class="btn btn-outline-primary active">
                    <span>{{ page_number }} <br />{{ date_shot_start|index:page_number }}<br/>-<br/>{{ date_shot_end|index:page_number }}<span class="sr-only"></span></span>
                </button>
            {% else %}
                {% if page_number == paginator.ELLIPSIS %}
                <button class="btn">
                    <span>{{ paginator.ELLIPSIS }} <span class="sr-only"></span></span>
                </button>
                {% else %}
                <a href="?page={{ page_number }}" class="btn btn-outline-primary">
                    {{ page_number }} <br />{{ date_shot_start|index:page_number }}<br/>-<br/>{{ date_shot_end|index:page_number }}
                </a>
                {% endif %}
            {% endif %}
        {% endfor %}

        {% if page_obj.has_next %}
            <a href="?page={{ page_obj.next_page_number }}" class="btn btn-outline-primary">&raquo;</a>
        {% endif %}
    </div>
</div>
{% endif %}
{# Page navigator: end #}
  1. Пользовательский тег index (<app>/templatetags/index_tag.py) для индексации элементов в шаблоне Django:
from django import template

register = template.Library()


@register.filter
def index(indexable, i):
    return indexable[i]
Вернуться на верх