[Solved]-Nested Serializer not showing up

33đź‘Ť

âś…

image = ImageSerializer(many=True, read_only=True, source='image_set')

or

image_set = ImageSerializer(many=True, read_only=True)  # use image_set in fields list too.

Let’s say you have a Gallery object similar to this:

g = Gallery.objects.get(pk=1)

Now the queryset for all the images related the given Galley object will be:

Image.objects.filter(gallery_id=g)  # g is the gallery object

In Django we can simplify it as:

g.image_set  # same as Image.objects.filter(gallery_id=g)

Now the thing is where does this magical image_set comes from. In Django ORM if you can use related_name in model’s ForeignKey to query related objects, like this:

gallery_id = models.ForeignKey(Gallery, on_delete=models.CASCADE, related_name='something')
# if you do this in your models.py then to get all images of a gallery you will now do:
g.something

But since you didn’t specified related_name in the ForeginKey it defaults to model name all lowercase + _set, therefore in this case: image_set.
Here is a link to docs.

If you specify two ForeignKey to same model from a model django will ask you to add related_name too (when making migrations), as it can default related name for one fields only.

This is called reverse_relationship. forward_relationship will be when you do something like this:

img = Image.objects.get(pk=1)
img.gallery_id  # get the gallery object related to the image

This is quite straightforward as gallery_id is a field in your model.


Also side note don’t name your ForeignKey fields with a trailing _id, it’s misleading. img.gallery_id is not the id of the Gallery it’s the whole Gallery object. Django saves Related fields with a trailing id in the database, so in your case the column name in your database will be gallery_id_id (most likely, might have been changed in newer version).
When querying if your field name is gallery_id:

img.gallery_id  # the gallery object
img.gallery_id.id  # actual id of the gallery
img.gallery_id_id  # actual id of the gallery

But if you name your field just gallery:

img.gallery  # the gallery object
img.gallery.id  # actual id of the gallery
img.gallery_id  # actual id of the gallery

Which is a lot more clear.


So now you know why image_set is an attribute of your model class. And drf looks for model attributes in field names, so you either have your field name same the attribute(image_set) or specify the attribute using the source argument of the serializer.

5đź‘Ť

For things to work, you need to specify where Serializer should take data, using source keyword

In your case, this should do the trick.

class GallerySerializer(serializers.HyperlinkedModelSerializer):
    image = ImageSerializer(source="image_set", many=True, read_only=True)

    def validate(self, data):
        # Check if user id is equal object id before creation or if SuperUser
        request = self.context.get("request")
        if request.user.id != data["author"].id and request.user.is_superuser is not True:
            raise ValidationError("Unauthorized User Post")
        return data

    class Meta:
        model = Gallery
        fields = ["title", "author", "created_on", "modified_on", "image", "id"]

In the case where you used “related_name” in your FK declaration, you should use this reverse related name.

As you expected many Image instance, I strongly suggest you name your field images instead of image

I also suggest you have a look on permission instead of using getting your logic into your Serializers.

Leave a comment