All skills
Skillintermediate

DRF Serializers

```python from rest_framework import serializers

Claude Code Knowledge Pack7/10/2026

Overview

DRF Serializers

ModelSerializer

from rest_framework import serializers

class ProductSerializer(serializers.ModelSerializer):
    # Read-only computed field
    category_name = serializers.CharField(source='category.name', read_only=True)

    # Write-only for input
    category_id = serializers.PrimaryKeyRelatedField(
        queryset=Category.objects.all(),
        source='category',
        write_only=True
    )

    # Nested read-only
    created_by = UserSerializer(read_only=True)

    class Meta:
        model = Product
        fields = [
            'id', 'name', 'slug', 'description', 'price', 'stock',
            'category_name', 'category_id', 'created_by', 'created_at'
        ]
        read_only_fields = ['slug', 'created_at']

Field-Level Validation

class ProductSerializer(serializers.ModelSerializer):
    class Meta:
        model = Product
        fields = ['name', 'price', 'stock']

    def validate_price(self, value):
        if value < 0:
            raise serializers.ValidationError("Price cannot be negative")
        return value

    def validate_name(self, value):
        if Product.objects.filter(name__iexact=value).exists():
            raise serializers.ValidationError("Product name already exists")
        return value

Object-Level Validation

class OrderSerializer(serializers.ModelSerializer):
    class Meta:
        model = Order
        fields = ['product', 'quantity', 'shipping_address']

    def validate(self, attrs):
        product = attrs['product']
        quantity = attrs['quantity']

        if quantity > product.stock:
            raise serializers.ValidationError({
                'quantity': f'Only {product.stock} items available'
            })

        if not attrs.get('shipping_address') and quantity > 5:
            raise serializers.ValidationError(
                "Shipping address required for large orders"
            )

        return attrs

Nested Serializers

class OrderItemSerializer(serializers.ModelSerializer):
    class Meta:
        model = OrderItem
        fields = ['product', 'quantity', 'price']

class OrderSerializer(serializers.ModelSerializer):
    items = OrderItemSerializer(many=True)

    class Meta:
        model = Order
        fields = ['id', 'user', 'items', 'total', 'created_at']

    def create(self, validated_data):
        items_data = validated_data.pop('items')
        order = Order.objects.create(**validated_data)

        for item_data in items_data:
            OrderItem.objects.create(order=order, **item_data)

        return order

    def update(self, instance, validated_data):
        items_data = validated_data.pop('items', None)

        for attr, value in validated_data.items():
            setattr(instance, attr, value)
        instance.save()

        if items_data is not None:
            instance.items.all().delete()
            for item_data in items_data:
                OrderItem.objects.create(order=instance, **item_data)

        return instance

SerializerMethodField

class ProductSerializer(serializers.ModelSerializer):
    discount_price = serializers.SerializerMethodField()
    is_available = serializers.SerializerMethodField()

    class Meta:
        model = Product
        fields = ['name', 'price', 'discount_price', 'is_available']

    def get_discount_price(self, obj) -> float:
        discount = self.context.get('discount', 0)
        return obj.price * (1 - discount / 100)

    def get_is_available(self, obj) -> bool:
        return obj.stock > 0 and obj.is_active

Quick Reference

Field TypeUse Case
CharField(source=...)Computed from related
PrimaryKeyRelatedFieldFK input
SerializerMethodFieldCustom computed
Nested SerializerRelated objects
MethodPurpose
validate_<field>()Single field validation
validate()Cross-field validation
create()Custom creation logic
update()Custom update logic
to_representation()Custom output