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)