DRF的ModelSerializer
序列化器与Django的Model
模型紧密映射,本文将通过简单的示例介绍几种处理关联关系的方法。
1. 创建模型和初始数据
创建模型
from django.db import modelsclass Product(models.Model):product_name = models.CharField(max_length=255)quantity = models.IntegerField()class Component(models.Model):product = models.ForeignKey(Product, on_delete=models.CASCADE, related_name='components')component_name = models.CharField(max_length=255)code = models.CharField(max_length=255)quantity = models.IntegerField()class Meta:ordering = ['product']def __str__(self):return f'{self.code}-{self.component_name}'
创建数据
>>> from api.models import Product, Component
>>> p1 = Product.objects.create(product_name="盖子", quantity=30)
>>> Component.objects.create(product=p1, component_name='纤维板', code='XYB', quantity=5)
>>> Component.objects.create(product=p1, component_name='螺丝', code='LS', quantity=10)
>>> Component.objects.create(product=p1, component_name='密封条', code='MFT', quantity=20)
2. 字符串关系字段
使用StringRelatedField
from rest_framework import serializers
from .models import Productclass ProductSerializer(serializers.ModelSerializer):components = serializers.StringRelatedField(many=True)class Meta:model = Productfields = ['product_name', 'quantity', 'components']
执行查询和序列化
>>> from api.models import Product
>>> p1 = Product.objects.prefetch_related('components').get(pk=1)
>>> from api.serializers import ProductSerializer
>>> ps = ProductSerializer(p1)
>>> ps.data
# {'product_name': '盖子', 'quantity': 30, 'components': ['XYB-纤维板', 'LS-螺丝', 'MFT-密封条']}
效果如下:
{'product_name': '盖子', 'quantity': 30, 'components': ['XYB-纤维板', 'LS-螺丝', 'MFT-密封条']
}
注意观察获取查询集的代码:Product.objects.prefetch_related('components').get(pk=1)
不难发现我们使用了prefetch_related
来获取查询集,如果一次查询大量的Product
,不使用prefetch_related
将会导致严重的性能问题(N+1问题)。DRF的序列化器不会为你自动优化。当然我们的示例中影响不大,因为只获取了一个查询集。
3. 主键关系字段
使用PrimaryKeyRelatedField
from rest_framework import serializers
from .models import Productclass ProductSerializer(serializers.ModelSerializer):components = serializers.PrimaryKeyRelatedField(many=True, read_only=True)class Meta:model = Productfields = ['product_name', 'quantity', 'components']
效果如下:
{'product_name': '盖子', 'quantity': 30, 'components': [1, 2, 3]
}
4. 指定关系字段
使用SlugRelatedField
可以指定字段来表示关联关系:
from rest_framework import serializers
from .models import Productclass ProductSerializer(serializers.ModelSerializer):components = serializers.SlugRelatedField(many=True,read_only=True,slug_field='code')class Meta:model = Productfields = ['product_name', 'quantity', 'components']
效果如下:
{'product_name': '盖子', 'quantity': 30, 'components': ['XYB', 'LS', 'MFT']
}
5. 嵌套序列化器
from rest_framework import serializers
from .models import Product, Componentclass ComponentSerializer(serializers.ModelSerializer):class Meta:model = Componentfields = ['component_name', 'code', 'quantity']class ProductSerializer(serializers.ModelSerializer):components = ComponentSerializer(many=True, read_only=True)class Meta:model = Productfields = ['product_name', 'quantity', 'components']
效果如下:
{"product_name": "盖子","quantity": 30,"components": [{"component_name": "纤维板","code": "XYB","quantity": 5},{"component_name": "螺丝","code": "LS","quantity": 10},{"component_name": "密封条","code": "MFT","quantity": 20}]
}
6. 可写嵌套
默认上面创建的嵌套序列化器是只读的,可写嵌套需要实现update
、create
两者或其一:
from rest_framework import serializers
from .models import Product, Componentclass ComponentSerializer(serializers.ModelSerializer):class Meta:model = Componentfields = ['component_name', 'code', 'quantity']class ProductSerializer(serializers.ModelSerializer):components = ComponentSerializer(many=True)class Meta:model = Productfields = ['product_name', 'quantity', 'components']def create(self, validated_data):components_data = validated_data.pop('components')product = Product.objects.create(**validated_data)for component_data in components_data:Component.objects.create(product=product, **component_data)return product
可以使用此序列化器创建实例:
>>> data = {"product_name": "电机","quantity": 1,"components": [{"component_name": "铝型材","code": "LXC","quantity": 5},{"component_name": "螺栓","code": "LSHUN","quantity": 10},]
}
>>> from api.serializers import ProductSerializer
>>> s = ProductSerializer(data=data)
>>> s.is_valid()
# True
>>> s.save()
# <Product: Product object (2)>
7. 总结注意事项
- 关联关系注意使用
prefetch_related
获取查询集。 - 可写嵌套序列化器必须自己实现
create
方法update
方法两者或其一。