Skip to content
Advertisement

Django – Get urls of all images that reference a Post?

I have this model where a Post can have many Photos. I’m trying to retrieve all posts, including ImageField url attribute (according to Django docs FileField and ImageField have a url attribute). So I’d like to ideally return all the urls of the images associated with the post (ideally in order of created).

model.py

class Post(AbstractBaseModel):
    creator_id = models.ForeignKey(
        User, on_delete=models.CASCADE, related_name="post_creator_id")
    goal_id = models.ForeignKey(Goal, on_delete=models.CASCADE)
    body = models.CharField(max_length=511, validators=[MinLengthValidator(5)])
    hash_tags = models.ManyToManyField(HashTag)


class Photo(AbstractBaseModel):
    created = models.DateTimeField('Created at', auto_now_add=True)
    post_id = models.ForeignKey(Post, on_delete=models.CASCADE)
    image = models.ImageField(upload_to=directory_path)

view.py

def get(self, request):
    data = Post.objects.order_by('created').values('body', 'goal_id__description', 'created',
                                                   'creator_id__username', replies=Count('replypost'),
                                                   cheers=Count('cheerpost'), image_urls='photo__image_url')
    return Response(list(data), status=status.HTTP_200_OK)

Advertisement

Answer

Please do not use .values(…) [Django-doc] to perform JSON serialization. This will errode the logical layer of a model, and for related objects it will act as a multiplier: per related Photo the body, etc. will be repeated.

We can define a function to retrieve the image URL of a Photo with:

class Photo(AbstractBaseModel):
    created = models.DateTimeField('Created at', auto_now_add=True)
    post_id = models.ForeignKey(Post, on_delete=models.CASCADE)
    image = models.ImageField(upload_to=directory_path)

    @property
    def image_url(self):
        return self.image.url

You can work with a serializers that determine how to return objects. For the Photo, we can work with a PhotoSerializer:

class PostSerialiser(serializers.ModelSerializer):
    image_urls = serializers.SlugRelatedField(
        source='photo_set', many=True, read_only=True, slug_field='image_url'
    )
    creator_name = serializers.SlugField()
    replies = serializers.IntegerField()
    cheers = serializers.IntegerField()
    goal_description = serializers.SlugField()

    class Meta:
        model = Post
        fields = ('body', 'goal_id', 'created', 'creator_name', 'goal_description', 'replies', 'cheers', 'created')

Then we can serialize and return the coressponding serialized data with:

from django.db.models import F, Count, Prefetch

def get(self, request):
    items = Post.objects.order_by('created').annotate(
        creator_name = F('creator_id__username'),
        goal_description = F('goal_id__description'),
        replies=Count('replypost', distinct=True),
        cheers=Count('cheerpost', distinct=True),
    ).prefetch_related(
        Prefetch('photo_set', Photo.objects.order_by('created'))
    )
    serializer = PostSerialiser(items, many=True)
    return Response({'data': serializer.data}, status=status.HTTP_200_OK)
Advertisement