How to Pass the request to another Serializer for Validation

Question:

I have two tables in my database to insert a product record. I am storing the product info in the Product table and the rest of the info like Pirce, Quantity etc storing to another table which is ProductStock.

I am planning to send the data to a server something like this.

{
    "name":'product name',
    "brand":"socialcodia"
    "product_stock":{
        "price":"100",
        "quantity":"50"
    }
}

I am easily able to validate the product info from ProductSerializer. But I don’t have any perfect idea to validate the ProductStockSerializer data.

ProductSerializer

from rest_framework import serializers
from .models import Product
from .models import Medical

class ProductSerializer(serializers.ModelSerializer):
    
    medical = serializers.CharField(read_only=True)
    id = serializers.CharField(read_only=True)
    is_visible = serializers.CharField(read_only=True,default=True)

    class Meta:
        model = Product
        fields = ['id','medical','category','item','brand','is_visible']

    def validate(self, attrs):
        request = self.context.get('request')
        attrs['medical'] = Medical.objects.get(pk=request.info.get('medical'))
        return attrs

Here I want to validate the product_stock info as well. cuz it’s coming with a single request. So is there any way that i can import the ProductStockSerializer into ProductSerializer and pass the data to that serializer. then validate it.

ProductStockSerializer

from rest_framework import serializers
from .models import ProductStock
from medical.models import Medical

class ProductStockSerializer(serializers.ModelSerializer):

    medical = serializers.CharField(read_only=True)
    
    class Meta:
        model = ProductStock
        fields = ['medical','distributer','product','variant','batch','purchase_price','price','quantity','location','low_stock','expire_date']


    def validate(self, attrs):
        
        attrs['medical'] = Medical.objects.get(self.context.get('request').info.get('medical'))
        batch = attrs.get('batch')
        purchase_price = attrs.get('purchase_price')
        price = attrs.get('price'),
        if len(batch) < 3 or len(batch)  > 30:
            raise serializers.ValidationError("Invalid Batch Number")

        if type(purchase_price) != int or type(purchase_price) != float:
            raise serializers.ValidationError("Invalid Purchase Price")

        if type(price) != int or type(price) != float:
            raise serializers.ValidationError("Invalid Price")

        return attrs;

ProductViewSet

class ProductViewSet(ModelViewSet):
    queryset = Product.objects.all()
    serializer_class = ProductSerializer
    authentication_classes = [JWTAuthentication]
    permission_classes = [IsAuthenticated]


    def get_queryset(self):
        if self.request.user.is_superuser:
            return Product.objects.all()
        return Product.objects.filter(medical=self.request.info['medical'])

I literally don’t have any idea how to do this.
Thanks

Asked By: mufazmi

||

Answers:

How about adding your ProductStockSerializer data as a field to your ProductSerializer

class ProductSerializer(serializers.ModelSerializer):
    ...
    product_stock = ProductStockSerializer()

    class Meta:
        fields = [ ... , product_stock ]

You should be able to use product_stock in your validation. If you plan to create ProductStock objects using this nested serializer please make sure to read on writable nested serializers.

Answered By: coderiot

The @coderiot answer is absolutely right.

Here’s How I Solved, But it has a lot more code than the above answer.

class ProductSerializer(serializers.ModelSerializer):
    
    medical = serializers.CharField(read_only=True)
    id = serializers.CharField(read_only=True)
    is_visible = serializers.CharField(read_only=True,default=True)
    stock = serializers.SerializerMethodField(read_only=True)

    class Meta:
        model = Product
        fields = ['stock','id','medical','category','item','brand','is_visible']

    def get_stock(self,instance):
        return ProductStockSerializer(instance=instance.stock,context=self.context).data

    def __init__(self,*args,**kwargs):
        super().__init__(*args,**kwargs)
        self.fields['stock'] = ProductStockSerializer(context=self.context)

    def validate(self, attrs):
        request = self.context.get('request')
        attrs['medical'] = Medical.objects.get(pk=request.info.get('medical'))
        return attrs
Answered By: mufazmi