djangodjango-rest-frameworkdjango-serializerdjango-media

Incomplete media url in nested serializers


I have a django-rest-api application with models Record, Tag and Weight, where Record and Tag have many-to-many relation through the Weight model:

# models.py
class Tag(models.Model):
    image = models.ImageField(upload_to='tag_images', null=True, blank=True)

class Record(models.Model):
    file = models.FileField(upload_to='record_files', null=True, blank=True)

class Weight(models.Model):
    record = models.ForeignKey(
        Record,
        related_name='weights',
        on_delete=models.CASCADE
    )
    tag = models.ForeignKey(
        Tag,
        related_name='weights',
        on_delete=models.CASCADE
    )
    value = models.IntegerField()

    class Meta:
        unique_together = ('record', 'tag',)

Both Record and Tag models have a FileField/ImageField parameter. In my REST API view I would like to display Record detail with its full file url and all related Tags with their full image url. This is how my serializers look like:

# serializers.py
class RecordSerializer(serializers.ModelSerializer):
    tags = serializers.SerializerMethodField()

    class Meta:
        model = Record
        fields = ('id', 'file', 'tags')

    def get_tags(self, obj):
        return TagSerializer(Tag.objects.filter(weights__in=obj.weights.all()), many=True).data

class TagSerializer(serializers.ModelSerializer):

    class Meta:
        model = Tag
        fields = ('id', 'image')

The problem is that when I see record detail view its file url is complete but tag image urls are not:

# record detail results
{
    "id": 1,
    "file": "http://127.0.0.1:8080/media/record_files/record00.mp3",
    "tags": [
        {
            "id": 4,
            "image": "/media/tag_images/image04.jpg"
        },
        {
            "id": 10,
            "image": "/media/tag_images/image10.jpg"
        }
    ]
}

In the end I have bypassed the problem by creating a separate tag list view and now the urls are complete even though a haven't changed a bit in my serializers.py.

# record tag list results
[
    {
        "id": 4,
        "image": "http://127.0.0.1:8080/media/tag_images/image04.jpg"
    },
    {
        "id": 10,
        "image": "http://127.0.0.1:8080/media/tag_images/image10.jpg"
    }
]

I suppose this is a cleaner rest api solution anyway (even though I am pretty sure I will not need record detail and its tags separately). Nevertheless, it still bugs me why the urls are incomplete when the data are part of a nested serializer. Is this how its meant to be or what do I do wrong?


Solution

  • Seems like you may have found a better solution, but just in case you are still curious: the problem with your initial attempt was that you put the "nested" serializer call inside a SerializerMethodField. The usual way to declare a nested serializer is to initialize the serializer when setting the actual field. In your case it would look something like this:

    class RecordSerializer(serializers.ModelSerializer):
        tags = TagSerializer()
    

    When you do it this way, the TagSerializer can infer context from the parent serializer, and part of that context is the request. This is then used in to_representation in a call to build_absolute_uri to generate the full url you were hoping for.