Chuyển đến nội dung chính

第 7 課:高階序列化器與驗證

SerializerMethodField、SlugRelatedField、自訂欄位。物件級驗證,UniqueTogetherValidator。動態序列化器、序列化器繼承、與 select_lated/prefetch_lated 相關的效能。

💻 程式設計 — 第 7 課 第 7 課:高階序列化器與驗證

Django:從基礎到高級

第 2 部分:Django REST 框架

亞洲開發網

1. 自訂序列化器字段

from rest_framework import serializers

class ProductSerializer(serializers.ModelSerializer):
    category_name = serializers.SlugRelatedField(
        source='category', slug_field='name', read_only=True
    )
    total_reviews = serializers.SerializerMethodField()
    discount_price = serializers.SerializerMethodField()

    class Meta:
        model = Product
        fields = ['id', 'name', 'price', 'category_name',
                  'total_reviews', 'discount_price']

    def get_total_reviews(self, obj):
        return obj.reviews.count()

    def get_discount_price(self, obj):
        if obj.discount_percent:
            return obj.price * (1 - obj.discount_percent / 100)
        return obj.price

2. 物件級驗證

from rest_framework.validators import UniqueTogetherValidator

class OrderSerializer(serializers.ModelSerializer):
    class Meta:
        model = Order
        fields = '__all__'
        validators = [
            UniqueTogetherValidator(
                queryset=Order.objects.all(),
                fields=['customer', 'order_number'],
            )
        ]

    def validate(self, attrs):
        if attrs['start_date'] >= attrs['end_date']:
            raise serializers.ValidationError(
                'Ngày kết thúc phải sau ngày bắt đầu')
        return attrs

3. 動態序列化器

class DynamicFieldsSerializer(serializers.ModelSerializer):
    """Cho phép chọn fields qua query params: ?fields=id,name"""

    def __init__(self, *args, **kwargs):
        fields = kwargs.pop('fields', None)
        super().__init__(*args, **kwargs)
        if fields is not None:
            allowed = set(fields)
            for field_name in set(self.fields) - allowed:
                self.fields.pop(field_name)

class ProductSerializer(DynamicFieldsSerializer):
    class Meta:
        model = Product
        fields = '__all__'

# Sử dụng
serializer = ProductSerializer(products, many=True, fields=['id', 'name', 'price'])

4. 效能優化

class ProductViewSet(viewsets.ModelViewSet):
    serializer_class = ProductSerializer

    def get_queryset(self):
        return Product.objects.select_related(
            'category', 'brand'
        ).prefetch_related(
            'tags', 'images',
            Prefetch('reviews', queryset=Review.objects.select_related('user'))
        ).annotate(
            avg_rating=Avg('reviews__rating'),
            review_count=Count('reviews'),
        )

    def get_serializer_class(self):
        if self.action == 'list':
            return ProductListSerializer
        return ProductDetailSerializer

5.可寫入嵌套序列化器

class ProductSerializer(serializers.ModelSerializer):
    images = ProductImageSerializer(many=True, required=False)

    class Meta:
        model = Product
        fields = '__all__'

    def create(self, validated_data):
        images_data = validated_data.pop('images', [])
        product = Product.objects.create(**validated_data)
        for img in images_data:
            ProductImage.objects.create(product=product, **img)
        return product

    def update(self, instance, validated_data):
        images_data = validated_data.pop('images', None)
        instance = super().update(instance, validated_data)
        if images_data is not None:
            instance.images.all().delete()
            for img in images_data:
                ProductImage.objects.create(product=instance, **img)
        return instance

下一篇: 版本控制、限制和 API 文檔。