Alpha Coder

Lazy load your content with Django and jQuery

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

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
<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

1
2
3
4
5
6
{% 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

1
2
3
4
5
6
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

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
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

1
2
3
4
5
6
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

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
    (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.

Need help getting something to work in your project? Try Alpha Coder Support!

Subscribe to the Alpha Coder Newsletter!

Get timely updates on new articles, courses and more from Nicholas Kajoh. Unsubscribe anytime.

Enjoy the content on Alpha Coder? Please buy me a coffee. 😊

Next post: Server-side form validation from A to Z with Sails.js


comments powered by Disqus