Implement An Infinite Scrolling With Django

As we know that Django is one of the most powerful Python frameworks for Web Development. In Django, sometimes we have to deal with a huge amount of data in the database and render them simultaneously.

Kudos to Django developers, who provides both high-level and low-level ways to manage paginated data that is splitting the data across several pages instead of showing all the contents of the object once.

To implement infinite scrolling, we will be using Django’s  Paginator class and a jQuery plug-in called jScroll.

jScroll is a jQuery plugin for creating infinite scrolling in websites that has the ability to load content via AJAX within the current page when the user scrolls to the bottom of the page.

 

How jScroll work?

jScroll loaded the new content automatically each time when we scroll to the end of the existing content by triggering to load by clicking a navigation link at the existing content and appending new data to the selected container.

Okay, these are all theories up to now. Let’s start the project on infinite scrolling.

I assumed that you have already created your Django project and are ready to integrate infinite scrolling into your project.

Note: I have already created a Django project called infinite_scrolling with one app named books.

 

About the infinite_scrolling project

Here for demonstration purposes, we will be creating a Book model that holds information like title, author, description, and image and we will display book information in the listview by allowing each page to display a limited amount of information.

Step 1: Creating a Book model

Edit the  models.py  a module of the books app.

from django.db import models


class Book(models.Model):
    title = models.CharField(max_length=50)
    author = models.CharField(max_length=50)
    description = models.TextField(max_length=500)
    image = models.ImageField(blank=True, upload_to='book_images')

    def __str__(self):
        return self.title

Note: We are using  ImageField in the Book model. We will need to install the Pillow library to handle images through pip.

pip install pillow

After installing the pillow library, to enable Django to server media files uploaded by users with the development server, add the following settings to the  settings.py  of your project as like:

settings.py

STATIC_URL = '/static/'
STATICFILES_DIRS = [os.path.join(BASE_DIR, 'static'),]

MEDIA_URL = '/media/'
MEDIA_ROOT = os.path.join(BASE_DIR, 'media/')

 

Now, we have to update the main  urls.py  of the infinite_scrolling project as follows:

infinite_scrolling > urls.py

from django.contrib import admin
from django.urls import path, include
from django.conf import settings
from django.conf.urls.static import static

urlpatterns = [
    path('admin/', admin.site.urls),
    path('', include('books.urls')),
]

if settings.DEBUG:
    urlpatterns += static(settings.MEDIA_URL,
                          document_root=settings.MEDIA_ROOT)

 

By doing this, the Django development server serves the media files when  DEBUG = True.

At last, let’s create a new python module  urls.py  for the books app. This module performs background tasks by mapping URLs path expressions to views functions.

books > urls.py

from django.urls import path
from .views import BookListView

urlpatterns = [path('', BookListView.as_view(), name='book-list')]

BookListView is the class-based view in the  views.py  file inside the books app that we are going to define in the next step.

 

Step 2: Creating BookListView view

from .models import Book
from django.views.generic import ListView


class BookListView(ListView):
    model = Book
    context_object_name = 'books'
    paginate_by = 5
    template_name = 'books/book-list.html'

 

What we have done above?

→ First, we import the Book model from the  models.py  of books app.

→ Second, to display a list of objects, we import ListView from class django.views.generic which is the built-in feature offered by Django.

→ Then we created class-based view  BookListView

  • Inside the class, we have specified  model = Book  which is the same as Book.objects.all()
  • We specified  context_object_name = ‘ books’  for query results. Note: If you do not specify any context_object_name, the default variable will be ‘object_list
  • We assigned  paginate_by = 5  to display five objects per page
  • At last, we specified the  template_name = ‘books/book-list.html’  to render our objects

 

Step 3: Creating template book-list.html

Inside the books directory, we would need to create a new folder called templates and inside it create another folder with the name of the app i.e books, and again inside the books folder create HTML template book-list.html. It should look like this:

|
|__ books
    |
    |__ templates
        |
        |__ books
            |
            |__ book-list.html

 

book-list.html

{% load static %}
<!doctype html>
<html lang="en">

<head>
    <!-- meta tags -->
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
    <!-- Bootstrap CSS CDN -->
    <link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.5.2/css/bootstrap.min.css"
          integrity="sha384-JcKb8q3iqJ61gNV9KGb8thSsNjpSL0n8PARn9HuZOnIxN0hoP+VmmDGMN5t9UJ0Z" crossorigin="anonymous">
    <title>Books – CodeFires</title>
</head>

<body>
<!-- container for showing book lists -->
<div class="container">
    <h1>Books</h1>
    {% if books %}
        <div class="book-list">
            {% for book in books %}
                <div class="card mb-3">
                    <img src="{{ book.image.url }}" class="card-img-top" alt="...">
                    <div class="card-body">
                        <h5 class="card-title">{{ book.title }}</h5>
                        <p class="card-text">{{ book.description }}</p>
                        <p class="card-text"><small class="text-muted">{{ book.author }}</small></p>
                    </div>
                </div>
            {% endfor %}
            {% if page_obj.has_next %}
                <p class="pagination">
                    <a class="next-page" href="?page={{ page_obj.next_page_number }}">More...</a>
                </p>
            {% endif %}
        </div>
    {% else %}
        <p>There are no any books uploaded yet!!!</p>
    {% endif %}
</div>

<!-- jQuery CDN hosted by Google -->
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.5.1/jquery.min.js"></script>
<!-- jScroll plug-in CDN -->
<script src="https://cdnjs.cloudflare.com/ajax/libs/jscroll/2.4.1/jquery.jscroll.min.js"></script>

<script>
    jQuery(function ($) {
        var $list = $('.book-list');
        $list.jscroll({
            loadingHtml: '<div class="text-center">' +
                '<img src="{% static 'img/loading.gif' %}" alt="Loading" />' +
                '</div>',
            padding: 100,
            pagingSelector: '.pagination',
            nextSelector: 'a.next-page:last',
            contentSelector: '.card,.pagination'
        });
    });
</script>
</body>
</html>

 

Let’s discuss what we have done in the template by dividing it into different sections:

Header section

<head>
    --------------------------

    <!-- Bootstrap CSS CDN -->
    <link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.5.2/css/bootstrap.min.css"
          integrity="sha384-JcKb8q3iqJ61gNV9KGb8thSsNjpSL0n8PARn9HuZOnIxN0hoP+VmmDGMN5t9UJ0Z" crossorigin="anonymous">

    --------------------------
</head>

We have used the CSS framework Bootstrap CDN(Content Delivery Network) for building an attractive and responsive webpage. Simply copy the link from Bootstrap’s official website and paste them inside your <head>….</head> tags.

 

Body section

I assumed that there is no need for explanation in the container part for showing book lists, as it is pretty much clear. Well, the main part of this tutorial is following scripts because without them we can not create infinite scrolling in our project.

Scripts

<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.5.1/jquery.min.js"></script>

<script src="https://cdnjs.cloudflare.com/ajax/libs/jscroll/2.4.1/jquery.jscroll.min.js"></script>

 

We have placed jQuery CDN hosted by Google and Cloudflare CDN URL to load the jScroll plug-in to the bottom of the closing <body> tag rather than downloading separately.

Loading jScroll method inside <script> tags

<script>
    jQuery(function ($) {
        var $list = $('.book-list');
        $list.jscroll({
            loadingHtml: '<div class="text-center">' +
                '<img src="{% static 'img/loading.gif' %}" alt="Loading" />' +
                '</div>',
            padding: 100,
            pagingSelector: '.pagination',
            nextSelector: 'a.next-page:last',
            contentSelector: '.card,.pagination'
        });
    });
</script>

 

  • loadingHTML⇒ HTML code to show loading spinner at the bottom of the list while loading a new page of items
  • padding⇒ Distance from the bottom of the scrollable page at which the new page should be loaded. Here in the example, we give padding of 100
  • pagingSelector⇒ Simply select the class that has the responsibility of linking to another page and hides the element in browsers when the jScroll plugin activated
  • nextSelector⇒ A selector for finding the next link of the page with the href attribute
  • contentSelector⇒ A selector that specifies which HTML elements should be extracted from AJAX-loaded content and added to the container.
Finally, run the project using the command python manage.py runserver.
You will notice that whenever scrolling down, the later pages are loaded and attached at the bottom until all the objects are completely loaded. In this way, we successfully implemented infinite scrolling in our project.
You can check this project for reference on GitHub: github.com/thecodefires/infinite-scrolling-django

Happy Learning 🙂

Leave a Comment