Nested serializer cho phép response/request có quan hệ lồng (như Order chứa list OrderItem).
Read thì DRF làm sẵn; write (tạo/sửa cùng nested data) phải override create()/update() thủ công.
class OrderItemSerializer(serializers.ModelSerializer):
class Meta:
model = OrderItem
fields = ['sku', 'qty', 'price']
class OrderSerializer(serializers.ModelSerializer):
items = OrderItemSerializer(many=True)
class Meta:
model = Order
fields = ['id', 'customer', 'items']
def create(self, validated_data):
items_data = validated_data.pop('items')
with transaction.atomic():
order = Order.objects.create(**validated_data)
OrderItem.objects.bulk_create(
[OrderItem(order=order, **item) for item in items_data]
)
return orderCó 3 trade-off cần cân nhắc trước khi nested. Update phức tạp gấp bội — phải tự quyết item nào thêm mới, item nào cập nhật, item nào xoá khi client gửi partial. Validation cross-item (vd tổng qty không vượt stock) phải đặt ở validate(data) của parent, rất dễ quên. Performance giảm nhanh khi nested 3+ tầng, query và serialize đều phình ra — nhiều khi tách thành 2 endpoint (/orders/ + /orders/{id}/items/) đơn giản và nhanh hơn.
Với write nested phức tạp, dùng thư viện drf-writable-nested, hoặc tốt hơn là tách logic ghép DB sang service layer và để serializer chỉ làm việc validate. Cách thứ 2 giúp test dễ hơn nhiều và serializer không bị phình thành 200 dòng.
Nested serializers let responses/requests carry relations (Order containing a list of OrderItem).
Read works out of the box; write (creating/updating with nested data) requires manually overriding create()/update().
class OrderItemSerializer(serializers.ModelSerializer):
class Meta:
model = OrderItem
fields = ['sku', 'qty', 'price']
class OrderSerializer(serializers.ModelSerializer):
items = OrderItemSerializer(many=True)
class Meta:
model = Order
fields = ['id', 'customer', 'items']
def create(self, validated_data):
items_data = validated_data.pop('items')
with transaction.atomic():
order = Order.objects.create(**validated_data)
OrderItem.objects.bulk_create(
[OrderItem(order=order, **item) for item in items_data]
)
return orderTrade-offs to weigh:
- Update is far more complex: you must decide which item is new, updated, deleted — easy to mishandle when the client sends a partial.
- Cross-item validation (e.g. total qty not exceeding stock) belongs in the parent's validate(data), easy to forget.
- Performance: 3+ levels deep → many queries/serializations; consider splitting into two endpoints (/orders/ + /orders/{id}/items/).
Pitfall: For complex writable nested, use drf-writable-nested or extract a service layer while serializers only validate. Moving DB stitching outside the serializer makes tests far easier and keeps serializers from ballooning to 200 lines.