Django List View With Show More

ed30f27d8bd2b7210d1ca723690737f0

Sometime you want to have infinite scrolling in a Django ListView. And there are good post about this topic on the internet like this one but nothing about infinite scrolling with filters.

Basic setup

In this tutorial I assume you have a Product django model and a class view like this:

model.py

from django.db import model

class Product(models.Model):
    title = models.CharField(max_length=250)
    description = models.TextField()

    def __str__(self):
        return self.title

views.py

from django.views.generic.list import ListView

class ProductsView(ListView):
    model = Product
    paginate_by = 2
    context_object_name = 'products'
    template_name = 'product.html'
    ordering = ['title']

Following the post by thepylot1 this is a template for the list view:

templates/product_list.html

        <div class="container">
            <div class="row infinite-container">
                {% for product in products %}
                    <div class="col-md-6 infinite-item">
                        <div class="card mb-4 shadow-sm">
                            <div class="card-body">
                                <h5>{{product.title}}</h5>
                                <p class="card-text">
                                    {{product.description}}
                                </p>
                            </div>
                        </div>
                    </div>
                {% endfor %}
            </div>
            {% if page_obj.has_next %}
            <a class="infinite-more-link" href="?page={{ page_obj.next_page_number }}"></a>
            {% endif %}
        </div>


<script src="/static/js/jquery-2.2.4.min.js"></script>
<script src="/static/js/jquery.waypoints.min.js"></script>
<script src="/static/js/infinite.min.js"></script>
<script>
var infinite = new Waypoint.Infinite({
    element: $('.infinite-container')[0],
    handler: function(direction) {},
    offset: 'bottom-in-view',
    onBeforePageLoad: function () {$('.spinner-border').show();},
    onAfterPageLoad: function () {$('.spinner-border').hide();}
});
</script>

The js import here is all from Waypoints and works. But it return the “normal” pagination. If I add a DjangoFilter2 this code not work with it. You need to edit the get parameters of the url.

The important parts are:

  • .infinite-more-link is matching the “Next Page” url for the pagination
  • .infinite-item is a selector string for the singolar item in the list

So for the filter to work we need to change the infinite-more-link link with the parameters of the filter.

Adding my stuff

We need to create a new tag for add or edit the get parameters of the url.

templatetags/app_tags.py

from django import template

register = template.Library()


@register.simple_tag
def url_replace_diff(request, field, value):
    """
    Give a field and a value and it's update the post parameter for the url accordly
    """
    dict_ = request.GET.copy()
    dict_[field] = value
    return dict_.urlencode()

So you need to change the template like this:

templates/product_list.html

        <div class="container">
            <div class="row infinite-container">
                {% for product in products %}
                    <div class="col-md-6 infinite-item">
                        <div class="card mb-4 shadow-sm">
                            <div class="card-body">
                                <h5>{{product.title}}</h5>
                                <p class="card-text">
                                    {{product.description}}
                                </p>
                            </div>
                        </div>
                    </div>
                {% endfor %}
            </div>
            {% if page_obj.has_next %}
              {% load app_tags %}
                <a class="infinite-more-link" href="?{% url_replace_diff request 'page' page_obj.next_page_number %}"></a>
            {% endif %}
        </div>

<script src="/static/js/jquery-2.2.4.min.js"></script>
<script src="/static/js/jquery.waypoints.min.js"></script>
<script src="/static/js/infinite.min.js"></script>
<script>
var infinite = new Waypoint.Infinite({
    element: $('.infinite-container')[0],
    handler: function(direction) {},
    offset: 'bottom-in-view',
    onBeforePageLoad: function () {$('.spinner-border').show();},
    onAfterPageLoad: function () {$('.spinner-border').hide();}
});
</script>

I change the .infinite-more-link url with the new tag for edit the parameters of the filter. In this way you can have infinite scrolling with Django Filter and Django Pagination.

This is the easy way to do it. Or at least it’s the easiest way I can find.

If you can find a better way to do it, please let me know (tweet, email, post with webmention, …) thanks.


To reply to this post, you can send a Webmention or you can toot me at [email protected]
You mentioned this post on your site? Send a Webmention

See Also