Как обогатить постраничный навигатор, добавив поле из первого и последнего объектов в 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">«</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">»</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
.
Я нашел решение для реализации следующего:
- Вид:
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)
- Шаблон:
{% 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">«</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">»</a>
{% endif %}
</div>
</div>
{% endif %}
{# Page navigator: end #}
- Пользовательский тег
index
(<app>/templatetags/index_tag.py
) для индексации элементов в шаблоне Django:
from django import template
register = template.Library()
@register.filter
def index(indexable, i):
return indexable[i]