Pagination with django and ajax/jquery

Hi guys. I have used Django to build a nice web-application. Now I want to polish it a little and to add AJAX to my pagination.
Problem is I can’t find any documentation on implementing it. I found some but for infinite-scroll pagination only.
I also tried this tutorial with no luck : https://pytutorial.com/how-to-make-a-pagination-with-django-and-ajaxjquery .
I don’t mind if the documentation is in english/french/german or spanish.

This is potentially a very broad topic.

I’m guessing you’ve already read the documentation on Pagination.

Using that with AJAX doesn’t fundamentally alter how this is done in Django - other than you want your template to return just the HTML component to be displayed instead of the entire page.

It might be easiest if you posted the code and the specific problem you’re having with it. (When you’re posting code, enclose it between two lines consisting only of three backtick (`) characters. Be sure to use the backtick and not the apostrophe (’).)

It would probably be most helpful to see the view generating your page, the view creating the “page fragment” being served by the AJAX request, and the JavaScript issuing that request.

Ken

In my web-app I have this user account list where an user can record food substitution for searched products. For now the user can navigate through the different pages of the list with regular Django Pagination. It works just fine but I want to add AJAX to make it snappier.

I have three databases in my app:

  • the one created bu django that lists the users
  • SavedProduct: Product_id, User_id, Product_substitution_id. All FK
  • Product: with every products details (name, brand, pictures…)

The biggest issue is that I can’t use Pagination because I have a "Object of type Page is not JSON serializable" error. So I decided first to implement my AJAX without pagination and then to add a way to paginate it without the use of Pagination.

This is my urls.py:

app_name = 'register'
urlpatterns = [
    path('account/', views.account, name='account'),
]

This is my view.py:

def account(request):    
    user_id = request.user
    sub_list = SavedProduct.objects.filter(username=user_id)
    paginator = Paginator(sub_list, 5)
    page_number = request.GET.get('page')
    saved_list = paginator.get_page(page_number) 
    context = {
        'paginate': True,
        'saved_list': saved_list,
    }
    if request.method=='POST':
        user_id = request.user 
        sub_list = SavedProduct.objects.filter(username=user_id)
        saved_list = []
        for fav in sub_list:
            products_json = {'or_name':0, 'sub_name':0, 'or_brand':0, 'sub_brand':0, 'sub_grade':0, 'or_grade':0, 'or_pic':0, 'sub_pic':0}
            products_json['sub_name']=(fav.sub_product.real_name)
            products_json['sub_brand']=(fav.sub_product.real_brand)
            products_json['or_name']=(fav.original_product.real_name)
            products_json['or_brand']=(fav.original_product.real_brand)
            products_json['sub_grade']=(fav.sub_product.nutrition_grade)
            products_json['or_grade']=(fav.original_product.nutrition_grade)
            products_json['sub_pic']=(fav.sub_product.picture)
            products_json['or_pic']=(fav.original_product.picture)           
            saved_list.append(products_json)               
        data = json.dumps(saved_list)                  
        return JsonResponse(data, safe=False)              
    return render(request, 'account/account.html', context)

My AJAX:

$(".nav_button_2").on("click", function(event) {
    event.preventDefault();      
    var page = $(this).val();
    console.log (page);                
    var url = '/register/account/';   
    $.ajax({        
        url: url,        
        type: "POST",
        data:{
            'page': page,            
            'csrfmiddlewaretoken': $('input[name=csrfmiddlewaretoken]').val()
        },
        datatype:'json',
        success: function(resp) {
            $('#fav_list').html('')            
            var resp = JSON.parse(resp); 
            $.each(resp, function(i, val) {                                       
            $('#fav_list').append('<h2>' + val.sub_name + val.or_name +'</h2>')
            });                  
        }
    }); 
});

My AJAX success function is just for checking that the right datas are sent back. And it works perfectly.

So for now datas are processed and sent back to AJAX. I just need now a way to display the new HTML.

My HTML when the user first land on the page:

  <div id='fav_list'  class="w-75  mx-auto container-fluid" style='background-color: transparent;'>

    {% for saved in saved_list %}

    <div class='row d-flex justify-content-between'>

      <div class="card mb-3" style="width: 49%;">
        <div class="row no-gutters">
          <div class="col-md-2 my-auto">
            <img class="mx-auto d-block" style="width:auto; height:auto; max-width:100px; max-height:100px; "
              src="{{ saved.original_product.picture }}">
          </div>
          <div class="col-md-10">
            <div class="card-body">
              <h5 class="card-title"><a href="{% url 'finder:detail' saved.original_product.id %}"
                  class="aaccount">{{ saved.original_product.real_name }}/ {{ saved.original_product.real_brand }}</a>
              </h5>
              <img src="/static/finder/img/nutriscore-{{ saved.original_product.nutrition_grade}}.svg"
                style="width:70px;"><br>
            </div>
          </div>
        </div>
      </div>

      <div class="card mb-3" style="width: 49%;">
        <div class="row no-gutters">
          <div class="col-md-2 my-auto">
            <img class="mx-auto d-block " style="width:auto; height:auto; max-width:100px; max-height:100px; "
              src="{{ saved.sub_product.picture }}">
          </div>
          <div class="col-md-9">
            <div class="card-body">
              <h5 class="card-title"><a href="{% url 'finder:detail' saved.sub_product.id %}"
                  class="aaccount">{{ saved.sub_product.real_name}}/ {{ saved.sub_product.real_brand }}</a>
              </h5>
              <img src="/static/finder/img/nutriscore-{{ saved.sub_product.nutrition_grade}}.svg"
                style="width:70px;"><br>
            </div>
          </div>
          <div class="col-md-1 my-auto mx-auto">
            
          <button type ='button' class=' btn substitut' value='{{ saved.id }}'>{% csrf_token %}<i class='fas fa-trash-alt'></i></button>              
          
          </div>
        </div>
      </div>
    </div>
    {% endfor %}

I understand I will have to substitute the double curly braced values for the one I get from the AJAX call. Should I create a new .html file with just <div id='fav_list'> ?
Is there a way to keep with Pagination in my AJAX?

Yes, you can use AJAX to paginate. There are multiple ways to handle it -

  1. Your view returns “data” that replaces the displayed data. This requires JavaScript do the work to clear out the current displayed data and replace it with the AJAX results.

  2. Your view returns multiple div or span elements as html to replace the current displayed elements. Less work than #1.

  3. Your view returns the complete div as html and you just replace the entire div. Much less JavaScript work is required.

Regarding the error you’re reporting on trying to use Pagination - you’ve never shown what your Page objects are, so I can’t comment on that. But you don’t Paginate a page, you paginate the objects being displayed on the page.

Ken

Ken,
At first I try to paginate saved_list, a list of dictionaries

[{'or_name': 'Frites de poulet panées', 'sub_name': 'Frites de poulet panées', 'or_brand': 'Le Gaulois', 'sub_brand': 'Le Gaulois', 'sub_grade': 'c', 'or_grade': 'c', 'or_pic': 'https://static.openfoodfacts.org/images/products/326/698/013/4693/front_fr.35.400.jpg', 'sub_pic': 'https://static.openfoodfacts.org/images/products/326/698/013/4693/front_fr.35.400.jpg'}, {'or_name': 'Nouilles aux oeufs', 'sub_name': "Jordan's muesli Bio Chocolat noir & graines de tournesol", 'or_brand': 'Suzi wan', 'sub_brand': 'Jordans', 'sub_grade': 'a', 'or_grade': 'b', 'or_pic': 'https://static.openfoodfacts.org/images/products/400/235/975/5200/front_fr.58.400.jpg', 'sub_pic': 'https://static.openfoodfacts.org/images/products/501/047/735/6239/front_fr.33.400.jpg'}, {'or_name': 'Polpa tomates concassées fines', 'sub_name': "Petit Pois Carottes à L'étuvée", 'or_brand': 'Mutti', 'sub_brand': 'Freshona', 'sub_grade': 'a', 'or_grade': 'a', 'or_pic': 'https://static.openfoodfacts.org/images/products/80042556/front_fr.90.400.jpg', 'sub_pic': 'https://static.openfoodfacts.org/images/products/20187873/front_fr.16.400.jpg'}, {'or_name': 'Polpa tomates concassées fines', 'sub_name': 'Tomaten gestückelt Tomates pelées en morceaux', 'or_brand': 'Mutti', 'sub_brand': 'Nostia', 'sub_grade': 'a', 'or_grade': 'a', 'or_pic': 'https://static.openfoodfacts.org/images/products/80042556/front_fr.90.400.jpg', 'sub_pic': 'https://static.openfoodfacts.org/images/products/20004132/front_fr.67.400.jpg'}, {'or_name': 'Polpa tomates concassées fines', 'sub_name': 'Fruchtcocktail gezuckert', 'or_brand': 'Mutti', 'sub_brand': 'Freshona', 'sub_grade': 'a', 'or_grade': 'a', 'or_pic': 'https://static.openfoodfacts.org/images/products/80042556/front_fr.90.400.jpg', 'sub_pic': 'https://static.openfoodfacts.org/images/products/20428921/front_fr.14.400.jpg'}]

But because it doesn’t worked out, I dumped it as it is and parsed it AJAX side.
So what I have now is a list of dictionaries I can iterate but now I don’t know what to do with it.
What I am trying now:

My urls:

app_name = 'register'
urlpatterns = [
    path('account/', views.account, name='account'),
    path('account/nav/', views.nav, name='nav'),    
]

My views.py:

def nav(request):    
    return render (request, 'account/nav.html')

My AJAX:

$(".nav_button_2").on("click", function(event) {
    event.preventDefault();      
    var page = $(this).val();                   
    var url = '/register/account/';   
    $.ajax({        
        url: url,        
        type: "POST",
        data:{
            'page': page,            
            'csrfmiddlewaretoken': $('input[name=csrfmiddlewaretoken]').val()
        },
        datatype:'json',
        success: function(resp) {
            $('#fav_list').html('')            
            var resp = JSON.parse(resp);
            console.log(resp)
            $.each(resp, function(i, val) {            
            $('#fav_list').load('nav')
            });                  
        }
    }); 
});

my nav.html

{%load static%}
<!DOCTYPE html>
<html>
    <div class='row d-flex justify-content-between'>
    <h1> Hello World</h1>
    </div> 
</html>

So far whenever I press the button, my div with the products disappears and in place I have ‘Hello World’.
But now I can’t figure out how to pass the datas from saved_list to the nav.html .

You don’t. The idea of AJAX is that you already have the page locally - you’re just updating elements on that page.

If you’re not already comfortable with using AJAX to update your already-present page, I suggest you make a detour and focus on that before trying to integrate your pagination issue into the mix. I think you’ll find it less frustrating than trying to do both at once.

Find some tutorial that shows how to get JSON from a web API to update your current page. Work through that, and then return to this.

Ken

Thanks Ken for you advice. First things first. I have to focus on the basis for now.