Serializer khai báo field thủ công — dùng khi input/output không map trực tiếp 1-1 với model (multi-source, computed field, action không tạo entity). ModelSerializer sinh field tự động từ model và có sẵn create(), update() — dùng cho CRUD chuẩn.
class PostSerializer(serializers.ModelSerializer):
author_name = serializers.CharField(source='author.full_name', read_only=True)
class Meta:
model = Post
fields = ['id', 'title', 'body', 'author', 'author_name', 'created_at']
read_only_fields = ['created_at']
def validate_title(self, value):
if Post.objects.filter(title=value).exists():
raise serializers.ValidationError('Title đã tồn tại')
return valueValidation chạy theo thứ tự: field validator built-in → validate_<field> → validate(data) (cross-field).
Đừng dùng fields = '__all__' cho API public — vấn đề mass-assignment y hệt như ở Form. Và khi response cần nhiều field tính toán, tách PostReadSerializer vs PostWriteSerializer đọc dễ hơn nhiều so với việc override to_representation cho một serializer cồng kềnh.
Serializer declares fields manually — use it when input/output does not map 1-to-1 to a model (multi-source, computed fields, non-entity actions).
ModelSerializer generates fields from a model and ships with create(), update() — use it for standard CRUD.
class PostSerializer(serializers.ModelSerializer):
author_name = serializers.CharField(source='author.full_name', read_only=True)
class Meta:
model = Post
fields = ['id', 'title', 'body', 'author', 'author_name', 'created_at']
read_only_fields = ['created_at']
def validate_title(self, value):
if Post.objects.filter(title=value).exists():
raise serializers.ValidationError('Title already exists')
return valueValidation runs in order: built-in field validators → validate_<field> → validate(data) (cross-field).
Pitfall: Never set fields = '__all__' for a public API — same mass-assignment trap as Forms. And split PostReadSerializer vs PostWriteSerializer when responses carry many computed fields (saves you from a heavy to_representation override).