DEV Community

Cover image for Django Infinite scrolling with javascript fetch api and function based view.
tochimclaren
tochimclaren

Posted on

Django Infinite scrolling with javascript fetch api and function based view.

Hello, I will take you straight through the process of adding inifinte pagination in your django application like it's abc complete code is down below

Note: this is for absolute beginner

django-infinite-scroll

First thing first, we create our django project.

mkdir django-infinite-scroll
Enter fullscreen mode Exit fullscreen mode
cd django-infinite-scroll
Enter fullscreen mode Exit fullscreen mode
pipenv shell #this initializes with Pipfile and creates the environment
Enter fullscreen mode Exit fullscreen mode

We install our django application

pipenv install django
Enter fullscreen mode Exit fullscreen mode

We create a new django project

django-admin startproject core .
Enter fullscreen mode Exit fullscreen mode

We create our app blog and add it to INSTALLED_APPS.

python manage.py startapp blog
Enter fullscreen mode Exit fullscreen mode
INSTALLED_APPS = [
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',

    # our blog app
    'blog.apps.BlogConfig',
]
Enter fullscreen mode Exit fullscreen mode

Run the development server and make sure everything is working properly.

python manage.py runserver
Enter fullscreen mode Exit fullscreen mode

Migrate the database and createsuperuser in my case the credentials were username: admin password: admin run the server and let the fun begin!

python manage.py migrate
Enter fullscreen mode Exit fullscreen mode
python manage.py createsuperuser
Enter fullscreen mode Exit fullscreen mode
python manage.py runserver
Enter fullscreen mode Exit fullscreen mode

Creating a simple model for a blog to hold

# models.py
from django.db import models

class Post(models.Model):
    title = models.CharField(max_length=255)
    content = models.TextField()
    created = models.DateTimeField(auto_now_add=True)
    updated = models.DateTimeField(auto_now=True)
    published = models.BooleanField(default=False)

    def __str__(self):
        return self.title
Enter fullscreen mode Exit fullscreen mode
# views.py
from django.core.paginator import Paginator
from django.http import JsonResponse
from django.shortcuts import render
from .models import Post


def posts(request):
    # it's not a bug if it's intentional ;)
    post_list = Post.objects.filter(published=False)
    # we get page 1 returns 10 post objects
    paginator = Paginator(post_list, 10)

    # page_number is initialized to `1` see main.js
    page_number = request.GET.get('page')

    # we are applying page number which defaults to `1`
    page_obj = paginator.get_page(page_number)

    if page_number:
        # We are checking if `page_number` < or == 
        paginator.num_pages total amount of pages returned by the `Paginator` this only runs if the above conditions are met
        if int(page_number) <= paginator.num_pages:

            obj_list = paginator.get_page(page_number)

            obj_list = obj_list.object_list.values()

            return JsonResponse(list(obj_list), status=200, safe=False)

    ctx = {'page_obj': page_obj}
    return render(request, 'post/posts.html', ctx)

Enter fullscreen mode Exit fullscreen mode

Add your app url to project url config

#core/urls.py

from django.contrib import admin
from django.urls import include, path

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

Enter fullscreen mode Exit fullscreen mode

Define your app urls.py

# blog/urls.py
from django.urls import path
from . import views

urlpatterns = [
    path('', views.posts, name="posts")
]

Enter fullscreen mode Exit fullscreen mode

We create a simple example template

# template
# 'blog/templates/post/posts.html'
{% load static %}
<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
    <style>
        #content{
            margin: 0 auto;
            width: 40vw;
        }
    </style>
</head>

<body>
    <div id="content">
        {% for post in page_obj %}
            <div>
                <h1>{{post.title}}</h1>
                <p>{{post.content}}</p>
            </div>
        {% endfor %}
    </div>
</body>
<script type="text/javascript" src="{% static 'blog/js/main.js' %}"></script>

</html>

Enter fullscreen mode Exit fullscreen mode

On the js side of things, we are listening to scroll event, when we scroll to the bottom of the page, we trigger a function that calls our view which in turn returns a Json objects that we can inject to our template.

# main.js
# 'blog/static/blog/js/main.js'

content = document.querySelector("#content");
let page = 1

window.onscroll = function() {
    url = `/?page=${page}`
    if (window.innerHeight + window.pageYOffset >= document.body.offsetHeight) {
        fetch(url).then(res => {
            if (res.ok) {
                return res.json();
            }
        }).then(data => {
            console.dir(data)
            page += 1
            content.innerHTML += data.map(
                obj=>`<div>
                <h1>${obj.title}</h1> 
                <p>${obj.content}</p> 
                </div>`
                ).join("\n")
        }).catch(err => {

        })
    }
}
Enter fullscreen mode Exit fullscreen mode

I hope this helps shed some weight on your web application, because things like these are not worth intsalling an external library (waypointjs i'm looking at you), please help your app shed some weight! Happy coding!

Top comments (2)

Collapse
 
ifechukwumunac profile image
f_iasco

is it possible to invert this

Collapse
 
tochimclaren profile image
tochimclaren

Please explain what you meant by invert.