Outlined in this tutorial is a simple way to lazy load content using Django’s built-in pagination and the jQuery library. The code samples shown below are for paginating posts in a blog application.
Templates
Create 2 templates, index.html and posts.html.
index.html
<html>
<head>
<script type="text/javascript">
// A CSRF token is required when making post requests in Django
// To be used for making AJAX requests in script.js
window.CSRF_TOKEN = "{{ csrf_token }}";
</script>
</head>
<body>
<h2>My Blog Posts</h2>
<div id="posts">{% include 'myapp/posts.html' %}</div>
<div>
<a id="lazyLoadLink"
href="javascript:void(0);"
data-page="2">Load More Posts</a>
</div>
</body>
</html>
posts.html
{% for post in posts %}
<div>
<h4>{{ post.title }}</h4>
<p>{{ post.content }}</p>
</div>
{% endfor %}
Urls, Views, Model
Create/update urls.py and views.py as follows.
urls.py
from myapp import views
urlpatterns = [
url(r'^$', views.index, name='index'),
url(r'^lazy_load_posts/$', views.lazy_load_posts, name='lazy_load_posts'),
]
views.py
from django.shortcuts import render
from myapp.models import Post
from django.template import loader
from django.core.paginator import Paginator, EmptyPage, PageNotAnInteger
from django.http import JsonResponse
def index(request):
posts = Post.objects.all()[:5]
return render(request, 'myapp/index.html', {'posts': posts})
def lazy_load_posts(request):
page = request.POST.get('page')
posts = Post.objects.all()
# use Django's pagination
# https://docs.djangoproject.com/en/dev/topics/pagination/
results_per_page = 5
paginator = Paginator(posts, results_per_page)
try:
posts = paginator.page(page)
except PageNotAnInteger:
posts = paginator.page(2)
except EmptyPage:
posts = paginator.page(paginator.num_pages)
# build a html posts list with the paginated posts
posts_html = loader.render_to_string(
'myapp/posts.html',
{'posts': posts}
)
# package output data and return it as a JSON object
output_data = {
'posts_html': posts_html,
'has_next': posts.has_next()
}
return JsonResponse(output_data)
Create a post model.
models.py
from __future__ import unicode_literals
from django.db import models
class Post(models.Model):
title = models.CharField()
content = models.TextField()
The index
function in views.py renders the index.html page. It retrieves and sends a list of post objects (the first page) to the template. The lazy_load_posts
function is called when the “Load More Posts” link is clicked. It retrieves the next page of posts using the Paginator
class and generates a html string using the posts.html template.
The paginator object provides a has_next method which checks if there’s another page to load. If there is, the page data-
attribute of the anchor tag in index.html is incremented by 1 so that when “Load More Posts” is clicked again, it loads the next page.
script.js
(function($) {
$('#lazyLoadLink').on('click', function() {
var link = $(this);
var page = link.data('page');
$.ajax({
type: 'post',
url: '/lazy_load_posts/',
data: {
'page': page,
'csrfmiddlewaretoken': window.CSRF_TOKEN // from index.html
},
success: function(data) {
// if there are still more pages to load,
// add 1 to the "Load More Posts" link's page data attribute
// else hide the link
if (data.has_next) {
link.data('page', page+1);
} else {
link.hide();
}
// append html to the posts div
$('#div').append(data.posts_html);
},
error: function(xhr, status, error) {
// shit happens friends!
}
});
});
}(jQuery));
The snippet above listens for click events on the “Load More Posts” link and sends AJAX requests to the lazy_load_posts
view. If a request is successful, the returned data is appended to the posts div
(id="posts"
).
All the code snippets in this tutorial can be found on this Github Gist.
Last modified on 2023-03-14