djangodjango-rest-frameworkdjango-select-relateddjango-prefetch-related

What is the best method to query db in django rest framework


I have models.py

class Category(MPTTModel):
    # few fields

class Brand(models.Model):
    # few fields

class Attribute(models.Model):
   # few fields

class AttributeValue(models.Model):
   attributes = models.ForeignKey(Attribute, # other conditions)
   # few fields

class Product(models.Model):
   category = models.ForeignKey(Category, # other conditions)
   brand = models.ForeignKey(Brand, # other conditions)
   attributes = models.ManyToManyField(Attribute, # other conditions)
   # few other fields

class ProductImages(models.Model):
   product = models.ForeignKey(Product, # other conditions)

In views.py, I have

class ProductAPIView(generics.GenericAPIView):

    serializer_class = ProductSerializer
    queryset = Product.objects.all()

serializers.py

class ProductSerializer(serializers.ModelSerializer):
    brand = serializers.SlugRelatedField(queryset = Brand.objects.all())
    category = serializers.SlugRelatedField(queryset = Category.objects.all())
    attributes = AttributeSerializer(many = True, read_only = True)
    product_images = ProductImageSerializer(many = True, read_only = True)

I'm getting my json response like this

{
    "brand": ...,
    "category": ...,
    "attributes": [
        { "attribute_values": [...] },
        { "attribute_values": [...] }
    ],
    "product_images": [{...}, {...}]
}

I came across select_related and prefetch_related which will optimise the db queries on foreign key fields and many-to-many fields.

I changed the queryset to queryset = Product.objects.select_related('category', 'brand').all()

How can i change the queryset in ProductAPIView to include the attributes, product_images and attribute_values fields also and improve the performance here ?


Solution

  • you can chain select_realted & prefetch_related

    class ProductAPIView(generics.GenericAPIView):
    
        serializer_class = ProductSerializer
        queryset = Product.objects.select_related('category', 'brand').prefetch_related("attributes")
    
    

    or override get_queryset function

    class ProductAPIView(generics.GenericAPIView):
    
        serializer_class = ProductSerializer
        
        def get_queryset(self):
            qs =  super().get_queryset()
            return qs.select_related('category', 'brand').prefetch_related("attributes")
    

    You can use the Prefetch class to specify the queryset that is used in prefetch_related() and this way combine it with select_related():