1
votes

My problem is excatly with Paginator get_page() method. Here is my view code:

@login_required
def photo_index_default_album(request):
album = Album.get_default_album(request.user)

photo_list = album.photo_set.all()

paginator = Paginator(photo_list, 2)  # Show 2 photo per page
page = request.GET.get('sayfa')
photos = paginator.get_page(page)

context = {
    'title': album.name,
    'photo': photos,
}

return render(request, 'photo/index.html', context)

paginator.get_page(page) is working for only 1. page. After second page it returns a page that has a object_list with an empty QuerySet.

By the way, probably it is a basic knowledge. I just want to know, what is the difference between these 2 assigments:

Option 1:

photo_list = Photo.objects.all()
photo_list.filter(Q(album=album.id))

and

Option2:

photo_list = Photo.objects.filter(album=album.id)

Update: Ok, I understand the difference between 2 options. But my problem is still going on. I updated the code above for query according to your helpful answers.

Problem: This pagination is working only for 1. pagination. If the 'sayfa' variable for page value becomes different than 1, it is not working. In this situation paginator.get_page() returns a page that has a object_list with an empty QuerySet. Checking the photo_list.count(), page, they are correct in every call. Also the paginator variables in template are also true for first and last pages. But I am not showing the images cause of empty return. Why paginator.get_page(page) returns an object_list with empty QuerySet while pagination differ than 1? By the way I am using MongoDB as db backend.

Strange thing: If I assign photo_list = Photo.objects.all(), my template is working as expected. But I want to show only a specific albums' photos. Final update note: Due to lots of images I didn't notice the problem, it was also buggy.

Final Update: (SOLUTION) The reason of wrong query result is a bug about LIMIT-OFFSET queries of Djongo package. I opened an issue, https://github.com/nesdis/djongo/issues/106.

My models:

class Photo(models.Model):
image = models.ImageField()
album = models.ManyToManyField(Album)

class Album(models.Model):
name = models.CharField(max_length=150)
user = models.ForeignKey('accounts.MyUser', verbose_name='Album Owner', on_delete=models.CASCADE)

My template: sorry for inconvenience, it is some long..

{% extends 'base.html' %}

{% block title %}
{{ block.super }} - {{ title }}
{% endblock %}

{% block body %}
<div class="container">
    {% for pht in photo %}
        <div class="row justify-content-center" style="margin-bottom: 30px">
            <div class="col-md-6">
                <div class="card">
                    {% if pht.image %}
                        <img class="card-img-top img-fluid" src="{{ pht.image.url }}" alt="Card image cap">
                    {% endif %}
                    <div class="card-body">
                        <h5 class="card-title">{{ title }}
                            <small style="color: #4e555b"> {{ pht.upload_date|timesince }} before</small>
                        </h5>
                        {% if request.user.is_authenticated %}
                            <a href="{{ pht.get_delete_url }}" class="btn btn-danger">Delete</a>
                        {% endif %}
                    </div>
                </div>
            </div>
        </div>
    {% endfor %}

    <div class="row justify-content-center" style="margin-bottom: 30px">
        <div class="col-md-6">
            <div class="pagination">
        <span class="step-links">
            {% if photo.has_previous %}
                <a href="?sayfa=1{% if request.GET.q %}&q={{ request.GET.q }}{% endif %}">&laquo; first</a>
                <a href="?sayfa={{ photo.previous_page_number }}{% if request.GET.q %}&q={{ request.GET.q }}{% endif %}">previous</a>
            {% endif %}

            <span class="current">
                Page {{ photo.number }} of {{ photo.paginator.num_pages }}.
            </span>

            {% if photo.has_next %}
                <a href="?sayfa={{ photo.next_page_number }}{% if request.GET.q %}&q={{ request.GET.q }}{% endif %}">next</a>
                <a href="?sayfa={{ photo.paginator.num_pages }}{% if request.GET.q %}&q={{ request.GET.q }}{% endif %}">last &raquo;</a>
            {% endif %}
        </span>
            </div>
        </div>
    </div>

</div>

{% endblock %}
2

2 Answers

2
votes

Unless the result is stored back in photo_list, the second line of

photo_list = Photo.objects.all()
photo_list.filter(Q(album=album.id))

has no effect. This is the only difference between the two piece of codes you provided (and I suggest to use the second one, based on filter, for readability).

1
votes

Here:

photo_list = Photo.objects.all()
photo_list.filter(Q(album=album.id))

The second statement is functionnally a no-op - the call to photo_list.filter(...) creates a new QuerySet, which is immediatly discarded since you don't bind it to anything.

So with this code, the QuerySet you pass to Paginator is really Photo.objects.all().

In the second case, you do rebind photo_list to the result of Photo.objects.filter(album=album.id) (which is just a shortest way to write Photo.objects.filter(Q(album=album.id)), so instead of the whole table contents you only get the rows matching the current album. You didn't post the whole models but you could also get the same result using the related descriptor (by default this would be album.photo_set.all(), cf the fine manual). So your queryset will indeed have much less rows.

You didn't post the template code (well, the relevant part of...) either so we can't be sure there isn't some other problem, but depending on your current album's photos count this might be just the expected result. Hint: you can check how many photos (total) you should get for a given album using photo_list.count().