# Using Pagination with JsonResponse

**URL:** <https://forum.djangoproject.com/t/using-pagination-with-jsonresponse/20207>\
**Category:** Forms & APIs\
**Created:** [April 12, 2023, 3:36pm UTC](https://forum.djangoproject.com/t/using-pagination-with-jsonresponse/20207 "2023-04-12T15:36:18Z")\
**Posts on this page:** 6\
**Page:** 1

<div class="post-metadata">

**Author:** ![louis](https://sea2.discourse-cdn.com/flex026/user_avatar/forum.djangoproject.com/louis/32/13187_2.png) [@louis](https://forum.djangoproject.com/u/louis)\
**Post date:** [April 12, 2023, 3:36pm UTC](https://forum.djangoproject.com/t/using-pagination-with-jsonresponse/20207/1 "2023-04-12T15:36:18Z")

</div>

Hello,

I am new to the forum and currently working on CS50W Network project which is basically a twitter clone.

I want to get different sets of posts via a fetch API request, which was working fine until I implemented Pagination. Now I get a 404 not found.

for example, when the DOM of my index page is loaded I call the getPosts function passing in the string ‘all’ as the username parameter and currentPage as the page parameter.

```auto
document.addEventListener('DOMContentLoaded', function() {
   
  // By default, getPosts
  getPosts('all', currentPage);
});
    
    let currentPage = 1;
    const itemsPerPage = 10;
    
    function getPosts(username, page) {
      // Make a GET request to the API endpoint
      fetch(`/get_post/${username}/?page=${page}&per_page=${itemsPerPage}`)
        .then(response => response.json())
        .then(data => displayPosts(data.data))
        .catch(error => console.error(error));
      }

```

the path for the API is:

```auto
path("get_post/<str:username>", views.get_post, name="get_post"),

```

My view function to get the requested posts and handle the pagination:

```auto
@login_required
def get_post(request, username):

    if username == "all":
    # get all posts
        posts = Post.objects.all()

    else:
        # get users posts
        user = User.objects.get(username=username)
        posts = Post.objects.filter(user=user)

    # change button color
    for post in posts:
        if post.likes.filter(id=request.user.id).exists():
            post.liked = True
            post.save()
        else:
            post.liked = False
            post.save()

    # order posts in reverse chronological order
    posts = posts.order_by("-timestamp").all()

    # Get the current page number and the number of items per page from the query parameters
    page_number = int(request.GET.get('page', 1))
    items_per_page = int(request.GET.get('per_page', 10))
    
    # Paginate the posts based on the page number and the number of items per page
    paginator = Paginator(posts, items_per_page)
    page_obj = paginator.get_page(page_number)

    # Create a list of serialized posts for the current page
    serialized_posts = [post.serialize() for post in page_obj]

    # Return a JSON response that includes the serialized posts and pagination metadata
    return JsonResponse({
        'data': serialized_posts,
        'meta': {
            'page': page_obj.number,
            'per_page': items_per_page,
            'total_pages': paginator.num_pages,
            'total_items': paginator.count
        }
    })

```

the received data gets handed over to the displayPosts function:

```auto
function getPosts(username, page) {
  // Make a GET request to the API endpoint
  fetch(`/get_post/${username}/?page=${page}&per_page=${itemsPerPage}`)
    .then(response => response.json())
    .then(data => displayPosts(data.data))
    .catch(error => console.error(error));
  }

```

the displayPosts function is supposed to append the Posts to the DOM:

```auto
function displayPosts(posts) {

    // create a div element for each post
    posts.forEach(post => {
        let div = document.createElement('div');
        div.className = "card";
        div.innerHTML = `
        <div class="card-body">
          <a class="card-title" id="user-link" href="profile/${post['username']}"><strong><h6>${post['username']}</h6></strong></a>
          <h7 class="card-subtitle mb-2 text-muted">${post['timestamp']}</h7>
          <p class="card-text">${post['text']}</p>
          <button class="card-text like-button" pk="${post['id']}" onclick="like_post(${post.id});"><h3> ♥ </button> </h3>
          <span class="like-count" pk="${post['id']}">${post['likes']}</span>
        </div> 
        `;
        // append div to posts-view
        document.querySelector('#posts-view').append(div);
        // get the button
        button = document.querySelectorAll('button[pk="'+post['id']+'"]')[0];
        // change button color
        if (post.liked == true){
          button.classList.add('liked')
        }
        else
          button.classList.remove('liked')
    });
}

```

In the browser console I get a 404 not found, pointing to the fetch.

I am not sure why. I checked the django docs page [Pagination | Django documentation | Django](https://docs.djangoproject.com/en/4.0/topics/pagination/)  
but in the docs they just return render the page\_obj.  
and as you can see, I have not added the page navigation buttons yet.

---

<div class="post-metadata">

**Author:** ![louis](https://sea2.discourse-cdn.com/flex026/user_avatar/forum.djangoproject.com/louis/32/13187_2.png) [@louis](https://forum.djangoproject.com/u/louis)\
**Post date:** [April 12, 2023, 3:58pm UTC](https://forum.djangoproject.com/t/using-pagination-with-jsonresponse/20207/2 "2023-04-12T15:58:07Z")

</div>

I found the mistake

I forgot the slash after str:username in the path, its supposed to be:

> ```
> path("get_post/<str:username>/", views.get_post, name="get_post"),
> 
> ```

and I also removed the int() for the page\_number and items\_per\_page.

now its working fine. It’s always the same, right after I Post a question in a forum I find the mistake right away… 🤦‍♂️

---

<div class="post-metadata">

**Author:** ![leandrodesouzadev](https://sea2.discourse-cdn.com/flex026/user_avatar/forum.djangoproject.com/leandrodesouzadev/32/8981_2.png) [@leandrodesouzadev](https://forum.djangoproject.com/u/leandrodesouzadev)\
**Post date:** [April 13, 2023, 10:12am UTC](https://forum.djangoproject.com/t/using-pagination-with-jsonresponse/20207/3 "2023-04-13T10:12:53Z")

</div>

Great to know that you found the solution.  
Additionally, I will advise you to take a look into this specific piece of code. This is doing a lot of queries to the database, you probably can refactor this to use less the database. This can kill the performance of your website / database.

> [@louis](#):
>
> ```auto
> # change button color
> for post in posts:
> if post.likes.filter(id=request.user.id).exists():
> post.liked = True
> post.save()
> else:
> post.liked = False
> post.save()
> 
> ```

---

<div class="post-metadata">

**Author:** ![louis](https://sea2.discourse-cdn.com/flex026/user_avatar/forum.djangoproject.com/louis/32/13187_2.png) [@louis](https://forum.djangoproject.com/u/louis)\
**Post date:** [April 13, 2023, 5:21pm UTC](https://forum.djangoproject.com/t/using-pagination-with-jsonresponse/20207/4 "2023-04-13T17:21:09Z")

</div>

Thanks alot for the tip!  
Looking into this, it seems that I can just remove the section completely. I already set liked in my like\_post function, when clicking the like button.

> @csrf\_exempt  
> @login\_required  
> def like\_post(request, post\_id):
> 
> ```
> # get the Post
> post = Post.objects.filter(pk=post_id).first()
> if post is None:
> return JsonResponse({"error": "Post not found."}, status=404)
> 
> # Return post contents
> if request.method == "GET":
> return JsonResponse(post.serialize())
> 
> # Update likes
> elif request.method == "PUT":
> data = json.loads(request.body)
> if data.get("like") is not None and data.get("like") is True:
> if post.likes.filter(id=request.user.id).exists():
> post.likes.remove(request.user)
> post.liked = False
> post.save()
> else:
> post.likes.add(request.user)
> post.liked = True 
> post.save() 
> # Post's pk, the total number of likes
> return JsonResponse({'pk':post.pk, 'like_count':post.likes.all().count(), 'liked':post.liked})
> 
> # Post must be via GET or PUT
> else:
> return JsonResponse({
> "error": "GET or PUT request required."
> }, status=400)
> 
> ```

---

<div class="post-metadata">

**Author:** ![louis](https://sea2.discourse-cdn.com/flex026/user_avatar/forum.djangoproject.com/louis/32/13187_2.png) [@louis](https://forum.djangoproject.com/u/louis)\
**Post date:** [April 14, 2023, 6:18pm UTC](https://forum.djangoproject.com/t/using-pagination-with-jsonresponse/20207/5 "2023-04-14T18:18:48Z")

</div>

ok I was a little confused, this is obviousely not working if someone else loggs in …

still trying to fix this.

---

<div class="post-metadata">

**Author:** ![louis](https://sea2.discourse-cdn.com/flex026/user_avatar/forum.djangoproject.com/louis/32/13187_2.png) [@louis](https://forum.djangoproject.com/u/louis)\
**Post date:** [April 15, 2023, 10:27am UTC](https://forum.djangoproject.com/t/using-pagination-with-jsonresponse/20207/6 "2023-04-15T10:27:59Z")

</div>

This is getting a bit off topic, but I still wanted to show how I handled the like button color change without all the database queries in the view function.

first I removed the ‘liked’ field of my ‘Post’ model.

then I created a list of dictionaries representing the users who liked the post called likes\_list. Each dictionary includes the `id` and `username` fields of the user. I returned it in my serializer function as likes.

> class Post(models.Model):  
> text = models.TextField(max\_length=512)  
> timestamp = models.DateTimeField(auto\_now\_add=True)  
> user = models.ForeignKey(User, on\_delete=models.CASCADE, null=True)  
> likes = models.ManyToManyField(User, related\_name=“post\_likes”, blank=True)
> 
> ```
> class Meta:
> ordering = ['timestamp']
>         
> def total_likes(self):
> return self.likes.count()
> 
> def serialize(self):
> 
> likes_list = []
> for user in self.likes.all():
> likes_list.append({
> "id": user.id,
> "username": user.username,
> })
> 
> return {
> "id": self.id,
> "text": self.text,
> "timestamp": self.timestamp.strftime("%b %d %Y, %I:%M %p"),
> "username": self.user.username,
> "total_likes": self.total_likes(),
> "likes": likes_list,
> }
> 
> ```

Now the likes\_list is returned in the JsonResponse of my ‘get\_post’ view function.  
therefore I can now access the id and Username of everyone that liked the post.

so I changed the condition to change button color inside the ‘displayPosts’ Javascript function to:

> ```
> if (post.likes.some(like => like.id == request_user_id)){
> button.classList.add('liked')
> }
> else
> button.classList.remove('liked')
> 
> ```

Would this be a better way to handle it?
