I am a beginner in Django, and I am really thoughtful about how things really work under the Django hood. I am currently implementing pagination in my web app.
Take a look at this view.py file:
def post_list(request):
object_list = Post.published.all(); # '.published' is a manager.
paginator = Paginator(object_list, 3); # 3 posts in each page.
page = request.GET.get("page");
try:
posts = paginator.page(page);
except PageNotAnInteger:
posts = paginator.page(1);
except EmptyPage:
posts = paginator.page(paginator.num_pages);
return render(request, "post/list.html", {"page": page, "posts": posts});
Isn't request.GET a dictionary object containing all the the GET request parameters that are in the url, and the .get() method used to return the value for the given key inside the parameter? As my URL is currently just localhost:8000 when I start the application, why does it work if I pass the key "page"?
My list.html file:
{% extends "base.html" %}
{% block title %}My Blog{% endblock %}
{% block content %}
<h1>My Blog</h1>
{% for post in posts %}
<h2><a href="{{ post.get_absolute_url }}">{{ post.title }}</a></h2> <!-- How does this absurl work?-->
<p class="date">Published {{ post.publish }} by {{ post.author }}</p> <!-- What does '.publish' print?-->
{{ post.body|truncatewords:30|linebreaks }}
{% endfor %}
{% include "pagination.html" with page=posts %}
<!-- The statement above is the little menu: "Page 1 of 2. Next" -->
<!-- It also sends the 'page' variable as a GET parameter. -->
{% endblock %}
My pagination.html file:
<!-- This pagination template expects a paginator object. -->
<div class="pagination">
<span class="step-links">
{% if page.has_previous %}
<a href="?page={{ page.previous_page_number }}">Previous</a>
{% endif %}
<span class="current">
Page {{ page.number }} of {{ page.paginator.num_pages }}. <!-- what!? -->
</span>
{% if page.has_next %}
<a href="?page={{ page.next_page_number }}">Next</a>
{% endif %}
</span>
</div>
When there are no parameters in the request (when you hit http://localhost:8000 directly), the value of page will be None. That is the default behaviour of request.GET.get() when it can't find the key you're asking for - the same as a normal Python dictionary (because GET extends it).
# page will be None
page = request.GET.get("page")
This means that None is passed to paginator.page():
try:
# Passing None here
posts = paginator.page(page)
except PageNotAnInteger:
Which likely means (although we can't see the code of paginagor) that a PageNotAnInteger exception is raised, and thus a value of 1 is passed to paginagor.page():
try:
posts = paginator.page(page) # Raises PageNotAnInteger because None passed
except PageNotAnInteger:
# Posts are retrieved for page 1
posts = paginator.page(1)
The posts from the above call, and the value of page (still None) are then passed to the template.
return render(request, "post/list.html", {"page": page, "posts": posts});
The template list.html then iterates the posts and displays them.
Rather confusingly, when the pagination.html template is included, it defines a context variable called page to the current value of posts:
<!-- Pass the value of posts using a variable name of page -->
{% include "pagination.html" with page=posts %}
So the places where the pagination.html template refers to page, it is actually using the value of posts.
<!-- Really posts.number and posts.paginator.num_pages -->
Page {{ page.number }} of {{ page.paginator.num_pages }}
Hope that helps to explain things.
One other thing, you don't need to add a semi-colon to the end of every line in Python.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With