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)