1

I'm trying to build a simple Django gallery that allows photos to be added to an album. Everything thus far is working fine (upload photo, add to album, paginated all photos listing, photo details/display pages, etc), until I try to display the 'album' pages. I can get the page to render the the album and all of the associated photos, but if I try to paginate the album, things start to get weird.

Here are my models:

# models.py

class Albums(models.Model):
    id = models.AutoField(primary_key=True, unique=True)
    name = models.CharField(max_length=500)
    slug = models.SlugField(max_length=500, unique=True)
    description = models.TextField(blank=True, null=True)

    def __str__(self):
        return self.name

    def save(self, *args, **kwargs):
        if not self.slug:
            self.slug = slugify(self.name)
        return super().save(*args, **kwargs)


class Photos(models.Model):    
    id = models.AutoField(primary_key=True, unique=True)
    album = models.ForeignKey(
        Albums, on_delete=models.SET_NULL, null=True, blank=True, verbose_name="Album"
    )
    photo = models.ImageField(upload_to="photos/")
    slug = models.CharField(max_length=16, null=False, editable=False) # I know I'm not using a SlugField here; that's on purpose
    title = models.CharField(max_length=500)
    description = models.TextField(blank=True, null=True)
    upload_date = models.DateTimeField(
        default=timezone.now, verbose_name="Date uploaded"
    )
    
    def __str__(self):
        return self.title

    def save(self, *args, **kwargs):
        [...] # stuff to get the image's EXIF data and use that to set the slug
        self.slug = str(datetime.strftime(img_date, "%Y%m%d%H%M%S"))
        [...] # plus a bunch of other stuff that happens on save, not important here
        super(Photos, self).save()

    def delete(self, *args, **kwargs):
        [...] # a bunch of stuff that happens on delete, not important here
        super(Photos, self).delete()

And the view that's making me miserable:

# views.py

class PhotoAlbum(DetailView):
    context_object_name = "album"
    model = Albums
    slug_field = "slug"
    template_name = "page.html"

    def get_photos(self, **kwargs):
        # get the integer id of the album
        a = Albums.objects.filter(slug=self.kwargs["slug"])
        a_id = a.values()[0]["id"]
        # get the list go photos in that album
        photos = Photos.objects.all().order_by("-capture_date").filter(album_id=a_id)
        return photos

    def get_context_data(self, **kwargs):
        context = super(PhotoAlbum, self).get_context_data(**kwargs)
        context["photos"] = self.get_photos()
        return context

These work in a basic template...

# album.html
<h2>{{ album.name }}</h2>
<p>{{ album.description }}</p>
<ul>
    {% for photo in photos %}
        <li><a href="{% url 'photo_detail' photo.slug %}">{{ photo.title }}</a></li>
    {% endfor %}
</ul>

...but if I try to paginate the photos by changing...

photos = Photos.objects.all().order_by("-capture_date").filter(album_id=a_id)

...to...

photos = Paginator(Photos.objects.all().order_by("-capture_date").filter(album_id=a_id), 10)

...I get nothing.

In searching around, almost all of the questions I've seen relating to pagination are for DRF, so have kind of hit a wall here. I had all sorts of trouble getting the photo-to-album relationship to display (the get_photos() function), so am guessing part of my problem lies with that. Any suggestions on how to get this working?

campegg
  • 123
  • 1
  • 1
  • 11

3 Answers3

1

Paginator will give you a paginator object, not the objects you are paginating. To get the objects, you first need to specify the page, so for example:

paginator = Paginator(Photos.objects.all().order_by("-capture_date").filter(album_id=a_id), 10)
photos = paginator.page(1)

photos here will then contain Photos objects that are on page 1.

Brian Destura
  • 11,487
  • 3
  • 18
  • 34
  • Thanks! This does answer my question, but raises another; is there a way to use `is_paginated` in my template tags and get the `page_obj.paginator.num_pages` and `page_obj.previous_page_number` values in my template like if I was using the built in `paginate_by`? – campegg Nov 08 '21 at 01:34
  • Yes but you will have to build out the template to support it, similar to what `ListView` does. Maybe you could use `ListView` with a list of photos filtered by album id instead? – Brian Destura Nov 08 '21 at 04:27
  • 1
    Of course… I completely forgot I was using a DetailView. Thanks again! – campegg Nov 08 '21 at 10:33
1

Based on Brian Destura's comment, I went back and started again using a ListView and after re-reading the Django docs and a bit of messing about, arrived at this solution for my view which, based on a little testing, works for me:

class AlbumDetail(ListView):
    context_object_name = "photos"
    template_name = "page.html"
    paginate_by = 6

    def get_queryset(self):
        a = Albums.objects.filter(slug=self.kwargs["slug"])
        a_id = a.values()[0]["id"]
        self.albums = get_object_or_404(Albums, slug=self.kwargs["slug"])
        return Photos.objects.filter(album_id=a_id).order_by("-capture_date")

    def get_context_data(self, **kwargs):
        context = super(AlbumDetail, self).get_context_data(**kwargs)
        context["album"] = self.albums
        return context
campegg
  • 123
  • 1
  • 1
  • 11
0

viwes.py

class PhotoAlbum(ListView):
    context_object_name = "album"
    model = Albums
    slug_field = "slug"
    template_name = "page.html"
    paginate_by = 5

page.html

This is pagination logic

{%if is_paginated %}

         {%if page_obj.has_previous %}
          <a style="margin-left: 20px; padding: 10px 20px;" class=" btn btn-primary" href="?page={{page_obj.previous_page_number}}"> {{page_obj.num_pages}} previous</a>
          
         {% endif %}

         {%if page_obj.has_next %}
         {% if page_obj.num_pages == 1 %}
             <a style="margin-left: 930px; padding: 10px 28px;" class=" btn btn-primary" href="?page={{page_obj.next_page_number}}">next </a>
          {% else %}
            <a style="margin-left: 880px; padding: 10px 28px;" class=" btn btn-primary" href="?page={{page_obj.next_page_number}}">next </a>
           {% endif %}
         {% endif %}

       {% endif %} 
BiswajitPaloi
  • 586
  • 4
  • 16
  • That gives me a basic list of the albums (which I already have), but what I wanted to do was get a list of the photos *in* a given album, based on the foreign key relationship outlined in the models in my original question. The [answer](https://stackoverflow.com/a/69885160/1462477) I posted above does this. – campegg Nov 08 '21 at 16:24