DRF的ModelSerializer
序列化器与Django的Model
模型紧密映射,本文将通过简单的示例介绍几种处理关联关系的方法。
1. 创建模型和初始数据
创建模型
python
from django.db import models
class 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}'
创建数据
python
>>> 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
python
from rest_framework import serializers
from .models import Product
class ProductSerializer(serializers.ModelSerializer):
components = serializers.StringRelatedField(many=True)
class Meta:
model = Product
fields = ['product_name', 'quantity', 'components']
执行查询和序列化
python
>>> 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-密封条']}
效果如下:
python
{
'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
python
from rest_framework import serializers
from .models import Product
class ProductSerializer(serializers.ModelSerializer):
components = serializers.PrimaryKeyRelatedField(many=True, read_only=True)
class Meta:
model = Product
fields = ['product_name', 'quantity', 'components']
效果如下:
python
{
'product_name': '盖子',
'quantity': 30,
'components': [
1,
2,
3
]
}
4. 指定关系字段
使用SlugRelatedField
可以指定字段来表示关联关系:
python
from rest_framework import serializers
from .models import Product
class ProductSerializer(serializers.ModelSerializer):
components = serializers.SlugRelatedField(
many=True,
read_only=True,
slug_field='code'
)
class Meta:
model = Product
fields = ['product_name', 'quantity', 'components']
效果如下:
python
{
'product_name': '盖子',
'quantity': 30,
'components': [
'XYB',
'LS',
'MFT'
]
}
5. 嵌套序列化器
python
from rest_framework import serializers
from .models import Product, Component
class ComponentSerializer(serializers.ModelSerializer):
class Meta:
model = Component
fields = ['component_name', 'code', 'quantity']
class ProductSerializer(serializers.ModelSerializer):
components = ComponentSerializer(many=True, read_only=True)
class Meta:
model = Product
fields = ['product_name', 'quantity', 'components']
效果如下:
python
{
"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
两者或其一:
python
from rest_framework import serializers
from .models import Product, Component
class ComponentSerializer(serializers.ModelSerializer):
class Meta:
model = Component
fields = ['component_name', 'code', 'quantity']
class ProductSerializer(serializers.ModelSerializer):
components = ComponentSerializer(many=True)
class Meta:
model = Product
fields = ['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
可以使用此序列化器创建实例:
python
>>> 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
方法两者或其一。