Optimizing queries in SerializerMethodField in Django REST framework

Viewed 2671

I am accessing the related field's data in my SerializerMethodField and there is a query for every object that is being rendered. My models look like (will keep short for brevity):

class Listing(models.Model):

variant = models.ForeignKey(to='Variant', related_name='variant_listings')
seller = models.ForeignKey(to='user.Seller', related_name='seller_listings')
locality = models.ForeignKey(to='user.Locality', blank=True, null=True)
price = models.IntegerField(blank=True, null=True)

Variant, Seller and Locality are all related models.

My Viewset:

class ListingViewSet(viewsets.ModelViewSet):
    """Viewset class for Listing model"""

    queryset = Listing.objects.all()
    serializer_class = ListingSerializer
    pagination_class = TbPagination
    filter_backends = (filters.DjangoFilterBackend,)
    filter_class = ListingFilter

    def get_queryset(self):

        listing_qs = Listing.objects.filter(status='active')
        listing_qs = ListingSerializer.setup_eager_loading(listing_qs)
        return listing_qs

And my serializer:

class ListingSerializer(serializers.ModelSerializer):
"""Serializer class for Listing model"""

@staticmethod
def setup_eager_loading(queryset):
    queryset = queryset.prefetch_related('variant', 'seller', 'locality')
    return queryset

@staticmethod
def get_car_link(obj):
    variant_name_slug = obj.variant.name.replace(' ', '-').replace('+', '')
    return '/buy-' + obj.seller.city.name.lower() + '/' + variant_name_slug

car_link = serializers.SerializerMethodField(read_only=True)

@staticmethod
def get_car_info(obj):
    return {
        'id': obj.id,
        'variant_name': obj.variant.name,
        'localities': obj.locality.name,
    }

car_info = serializers.SerializerMethodField(read_only=True)

@staticmethod
def get_image_urls(obj):
    caption_ids = [1, 2, 3, 5, 7, 8, 18]
    attachments_qs = Attachment.objects.filter(listing_id=obj.id, caption_id__in=caption_ids)
    image_urls = []
    for attachment in attachments_qs:
        url = str(obj.id) + '-' + str(attachment.file_number) + '-360.jpg'
        image_urls.append(url)

    return image_urls

image_urls = serializers.SerializerMethodField(read_only=True)

class Meta:
    model = Listing
    fields = ('car_link', 'car_info', 'sort_by', 'image_urls')

For each listing returned by the listing viewset, there is a query for every related field accessed in the SerializerMethodField.

I found some related questions like this. But that didn't help. Also, I tried doing prefetch_related on my get_queryset method of the viewset and also implemented eager loading with the help of this article. But nothing helped.

Is there any way to avoid these queries?

Edit

The get_car_info function written above, contains a few more fields (along with the ones already present) which are required separately in a nested JSON by the name of car_info in the final serialized data that is being rendered at the front end.

1 Answers
Related