Filter returns much more objects

Question:

I have two models:

class PhotoAlbum(models.Model):
    id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False, auto_created=True)
    name = models.CharField(max_length=50, verbose_name='Album name')

class AlbumImage(models.Model):
    id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False, auto_created=True)
    album = models.ForeignKey(PhotoAlbum, on_delete=models.CASCADE, related_name='photos')
    image = models.ImageField(upload_to='images/', height_field=None, width_field=None, max_length=100, blank=True)

When i send GET request without query parameters i see a normal response like (one random entry):

[
    {
        "id": 1,
        "name": "Album name",
        "photos": [
            {
                "id": 2,
                "url": "http://localhost:8000/media/random_image_one.png"
            },
            {
                "id": 3,
                "url": "http://localhost:8000/media/random_image_two.png"
            }
        ]
    }
]

And there is a problem! When i overrided the method get_query_set like:

has_photos = self.request.query_params.get('has_photos')
if has_photos == 'true':
    return PhotoAlbum.objects.filter(photos__isnull=False)
elif has_photos == 'false':
    return PhotoAlbum.objects.filter(photos__isnull=True)

In the case has_photos = 'true' i see the response:

[
    {
        "id": 1,
        "name": "Album name",
        "photos": [
            {
                "id": 2,
                "url": "http://localhost:8000/media/random_image_one.png"
            },
            {
                "id": 3,
                "url": "http://localhost:8000/media/random_image_two.png"
            }
        ]
    },
    {
        "id": 1,
        "name": "Album name",
        "photos": [
            {
                "id": 2,
                "url": "http://localhost:8000/media/random_image_one.png"
            },
            {
                "id": 3,
                "url": "http://localhost:8000/media/random_image_two.png"
            }
        ]
    }
]

It returns one entry for each photo object in photos.

So if, the photo album has 10 associated AlbumImage objects in photos it returns 10 similar entries instead of 1.

What i did wrong?

Asked By: Anonymous Wizard

||

Answers:

I think get_queryset method has an issue.
You can remove duplicated album objects with distinct method.

This problem occurs because a join occurs while evaluating a query.

As-is

has_photos = self.request.query_params.get('has_photos')
if has_photos == 'true':
    return PhotoAlbum.objects.filter(photos__isnull=False)
elif has_photos == 'false':
    return PhotoAlbum.objects.filter(photos__isnull=True)

To-be (1)

has_photos = self.request.query_params.get('has_photos')
if has_photos == 'true':
    return PhotoAlbum.objects.filter(photos__isnull=False).distinct()
elif has_photos == 'false':
    return PhotoAlbum.objects.filter(photos__isnull=True).distinct()

To-be (2)

qs = PhotoAlbum.objects.annotate(count=Count('photos'))
has_photos = self.request.query_params.get('has_photos')
if has_photos == 'true':
    return qs.filter(count__gte=1)
elif has_photos == 'false':
    return qs.filter(count=0)
Answered By: Jaewoo Pyo