Django一分钟:DRF模型序列化器处理关联关系的示例与注意事项

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. 可写嵌套

默认上面创建的嵌套序列化器是只读的,可写嵌套需要实现updatecreate两者或其一:

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. 总结注意事项

  1. 关联关系注意使用prefetch_related获取查询集。
  2. 可写嵌套序列化器必须自己实现create方法update方法两者或其一。
相关推荐
qq_161111271 小时前
Jenkins+Python自动化测试持续集成详细教程
自动化测试·python·jenkins·教程·持续集成
爱吃苹果的梨叔5 小时前
训练集群网络中断怎么办?智算机房的 Console 串口排障方案
网络·python·智能路由器·php
李妍.8 小时前
02Numpy基础(上)
开发语言·python
TheBestRucy8 小时前
Python 九阳神功之贰:面向对象(下)
开发语言·python
2601_965958468 小时前
口腔黏膜脱皮超2周未愈建议及时就医
人工智能·python
智购科技智能售货柜8 小时前
2026自动售货机整机可靠性测试:从高低温交变到EMC电磁兼容的认证工程实践~YH
运维·服务器·数据库·人工智能·物联网
ltl9 小时前
RocksDB 架构演进:相对 LevelDB 的 diff 地图
数据库
ltl9 小时前
存储读取性能优化:索引、Bloom Filter 与读放大
数据库
微小冷9 小时前
在不同空间中展示数据(欧式、球面、庞加莱圆盘)
python·双曲空间·微分几何·geomstats·庞加莱圆盘·球面
weixin_4316004410 小时前
Agent Workflow 学习向:Code 节点,比模板更强的变量加工
后端·python·学习·ai·