1

I'm trying to use Django (v2.0) pagination with CBV and got issues. The pagination is active because the tag {% if is_paginated %} returns "True" and shows the "PagNav" and the browser path changes too, like this (..?page=1, ...?page=2, etc...) but the displayed elements are all, not 3 just as I set in "paginate_by=3". For example, if the query has 15 elements must to show 3 elements per page and 1 to 5 in the pagination down bellow, but shows all elements. I attach an image and the code:

enter image description here

models.py:

from django.db import models
from django.contrib.auth.models import User
from ckeditor.fields import RichTextField

# Create your models here.

class Project(models.Model):
    user = models.ForeignKey(User, on_delete = models.CASCADE, default=1)
    name = models.CharField(verbose_name='Nombre del proyecto', max_length=200)
    client = models.CharField(verbose_name='Nombre del cliente', max_length=200)
    description = RichTextField(verbose_name='Descripción')
    start = models.DateField(verbose_name='Fecha de Inicio', null=True, blank=True)
    ending = models.DateField(verbose_name='Fecha de Finalización', null=True, blank=True)
    order = models.SmallIntegerField(verbose_name="Orden", default=0)
    created = models.DateTimeField(verbose_name='Fecha de creación', auto_now_add=True)
    updated = models.DateTimeField(verbose_name='Fecha de modificación', auto_now=True)

    class Meta:
        verbose_name = 'Proyecto'
        verbose_name_plural = 'Proyectos'
        ordering = ['-start', 'order']

    def __str__(self):
        return self.name

class Album(models.Model):
    project = models.ForeignKey(Project, verbose_name='Proyecto relacionado', on_delete = models.CASCADE)
    title = models.CharField(verbose_name='Título de la imagen', max_length=200)
    image = models.ImageField(verbose_name='Imagen', upload_to='portfolio')
    created = models.DateTimeField(verbose_name='Fecha de creación', auto_now_add=True)
    updated = models.DateTimeField(verbose_name='Fecha de modificación', auto_now=True)

    class Meta:
        verbose_name = 'Imagen en el album'
        verbose_name_plural = 'Imágenes en el album'
        ordering = ['created']

        def __str__(self):
            return self.title

views.py:

@method_decorator(staff_member_required(login_url='login'), name='dispatch')
class AlbumListView(SingleObjectMixin, ListView):

    paginate_by = 3
    template_name = "core/album_list_form.html"


    def get(self, request, *args, **kwargs):
        self.object = self.get_object(queryset=Project.objects.all())
        return super().get(request, *args, **kwargs)


    def get_context_data(self, **kwargs):
        context = super().get_context_data(**kwargs)
        context['project'] = self.object
        return context


    def get_queryset(self):
        return self.object.album_set.all()


@method_decorator(staff_member_required(login_url='login'), name='dispatch')
class ProjectUpdateView(UpdateView):

    model = Project
    template_name = "core/project_update_form.html"
    form_class = ProjectUpdateForm


    def get_success_url(self):
        return  reverse_lazy('portfolio_update', args=[self.object.id]) + '?ok'

urls.py:

from django.urls import path
from . import views

from .views import *

urlpatterns = [
    path('', HomePageView.as_view(), name="home"),
    path('album/list/<int:pk>', AlbumListView.as_view(), name="portfolio_list_album"),
    # Update Views
    path('project/update/<int:pk>', ProjectUpdateView.as_view(), name="portfolio_update"),
    .
    .
    .

album_list_form.html:

{% extends "core/base.1.html" %}
{% load static %}
{% block title %}Imágenes de {{project.name}}{% endblock %}

{% block content %}

    <div class="container">
        <div class="row mt-5 mb-2 ml-1">
            <div class="mt-3">
                <h2 class="mt-5 mb-0"><a style="color: #343a40;" href="{% url 'portfolio_update' project.id %}">{{project.name}}</a></h2>
                <div class="subheading mb-5">Imágenes:</div>
            </div>
        </div>
    </div>


    <div class="album py-5 bg-light">
        <div class="container" style="margin-bottom: 2.5rem!important; margin-top: 2.5rem!important;">
            <div class="row">

                {% for album in project.album_set.all %}          

                        <div class="col-md-4">
                            <div class="card mb-4 shadow-sm">
                            <img class="card-img-top border-bottom p-2 p-2 p-2 p-2 bg-light" src="{{album.image.url}}" alt="Card image cap">
                            <div class="card-body">
                                <p class="card-text" title="{{album.title}}" style="color: #343a40;"><strong>{{album.title|truncatechars:31}}</strong></p>
                                <div class="d-flex justify-content-between align-items-center">
                                <div class="btn-group">
                                    <button type="button" class="btn btn-sm btn-outline-warning"><i class="fa fa fa-pencil"></i></button>
                                    <button type="button" class="btn btn-sm btn-outline-danger"><i class="fa fa fa-times"></i></button>    
                                </div>
                                <p></p>

                                </div>
                                <small class="text-muted">Última modificación: {{album.updated|date:"SHORT_DATE_FORMAT"}} {{album.updated|time:"h:i a"}}</small>
                            </div>
                            </div>
                        </div>           

                {% endfor %}
            </div>


        </div>

            {% if is_paginated %}

                <nav aria-label="Page navigation example">
                    <ul class="pagination pagination-lg justify-content-center">

                        {% if page_obj.has_previous %}
                            <li class="page-item">
                                <a class="page-link" style="color:#bd5d38;" href="?page={{ page_obj.previous_page_number }}" aria-label="Previous">
                                    <span aria-hidden="true">&laquo;</span>
                                    <span class="sr-only">Previous</span>
                                </a>
                            </li>
                        {% endif %}

                        {% for i in paginator.page_range %}
                            {% if page_obj.number == i %}
                                <li class="page-item"><a class="page-link" style="color:#ffffff; background-color: #343a40;" href="?page={{ i }}">{{ i }}</a></li>
                            {% else %}
                                <li class="page-item"><a class="page-link" style="color:#bd5d38;" href="?page={{ i }}">{{ i }}</a></li>
                            {% endif %}
                        {% endfor %}

                        {% if page_obj.has_next %}
                            <li class="page-item">
                                <a class="page-link" style="color:#bd5d38;" href="?page={{ page_obj.next_page_number }}" aria-label="Next">
                                <span aria-hidden="true">&raquo;</span>
                                <span class="sr-only">Next</span>
                                </a>
                            </li>
                        {% endif %}

                    </ul>
                </nav>

            {% endif %}

    </div>

{% endblock %}
nilsoviani
  • 344
  • 5
  • 16
  • 1
    But you are not paginating the `album`s, you are paginating the `Project`s. By paginating a queryset, you do not paginate the related objects. – Willem Van Onsem Sep 01 '18 at 09:17
  • 1
    A quick fix could be to replace `{% for album in project.album_set.all %}` with `{% for album in object_list %}`. This is actually *one* of the mahy reasons why you should *not* write queries in the template. Furthermore this looks rather "hackelish". – Willem Van Onsem Sep 01 '18 at 09:19

1 Answers1

3

The core of the problem I think is that you write in the template:

{% for album in project.album_set.all %}

You thus make a project object. But regardless whether you paginate your class-based view, you will not paginate a related object manager. You only paginate the object_list.

Probably you can solve it with:

{% for album in object_list %}

Furthermore I think you make the class-based view extremely complicated: this is a class-based view over Albums. Yes, filtered albums, but so the Album should be central here. I think it can be rewritten to:

from django.urls import path
from . import views

from .views import *

urlpatterns = [
  path('', HomePageView.as_view(), name="home"),
  path('album/list/<int:project_pk>', AlbumListView.as_view(), name="portfolio_list_album"),
  path('project/update/<int:pk>', ProjectUpdateView.as_view(), name="portfolio_update"),
]

Then in the view itself:

@method_decorator(staff_member_required(login_url='login'), name='dispatch')
class AlbumListView(ListView):

    model = Album
    paginate_by = 3
    template_name = "core/album_list_form.html"

    def get_context_data(self, **kwargs):
        context = super().get_context_data(**kwargs)
        context['project'] = Project.objects.get(pk=self.kwargs['project_pk'])
        return context

    def get_queryset(self):
        return Album.objects.filter(project_id=self.kwargs['project_pk'])
Willem Van Onsem
  • 443,496
  • 30
  • 428
  • 555
  • Thanks a lot! {% for album in object_list %} was the solution, I was rewriting the template, trying many solutions and I didn't see that. I'm goin to follow the advice, let me try it! – nilsoviani Sep 01 '18 at 10:21
  • project_pk in get_context_data is not recognized as declared variable – nilsoviani Sep 01 '18 at 10:29
  • @nilsoviani: does it help by accessing it through `self.kwargs`? – Willem Van Onsem Sep 01 '18 at 10:30
  • AttributeError at /album/list/2 'AlbumListView' object has no attribute 'object' Request Method: GET Request URL: http://127.0.0.1:8000/album/list/2 Django Version: 2.0.2 Exception Type: AttributeError Exception Value: 'AlbumListView' object has no attribute 'object' – nilsoviani Sep 01 '18 at 10:44
  • 1
    @nilsoviani: I think this is due to the `SingleObjectMixin`, which makes no sense. You can say that a `SingleObjectMixin` is the opposite of a `ListView`. Probably it helps by removing it. – Willem Van Onsem Sep 01 '18 at 10:45
  • 1
    Yes, thanks again! It was that. Works correctly. I saw that information about pagination with CBV [here](https://django.readthedocs.io/en/latest/topics/class-based-views/mixins.html) – nilsoviani Sep 01 '18 at 10:51