I am building a Django app form managing the digitalisation process of analog film rolls.
I would like to add the film_roll.from_year
field of the FilmRoll
model to my page navigation widget in a Django template. This greatly facilitates navigating to the right page by looking at the year_from
range below each page.
My view is defined as follows:
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)
Here's the page navigator code in the template:
{# 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 #}
Since the film roll collection is already ordered by year_from
, I could probably fetch year_from
from the first and last object in the paginated list.
Is this something that can be added in the Django template, or do I have to compute these properties in the view?
I found a solution to implement the following:
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
tag (<app>/templatetags/index_tag.py
) to index items in a Django template:from django import template
register = template.Library()
@register.filter
def index(indexable, i):
return indexable[i]