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方法两者或其一。
相关推荐
村口蹲点的阿三6 分钟前
Spark SQL 中对 Map 类型的操作函数
javascript·数据库·hive·sql·spark
小兜全糖(xdqt)42 分钟前
python中单例模式
开发语言·python·单例模式
Python数据分析与机器学习1 小时前
python高级加密算法AES对信息进行加密和解密
开发语言·python
noravinsc1 小时前
python md5加密
前端·javascript·python
暮湫1 小时前
MySQL(1)概述
数据库·mysql
唯余木叶下弦声1 小时前
PySpark之金融数据分析(Spark RDD、SQL练习题)
大数据·python·sql·数据分析·spark·pyspark
fajianchen1 小时前
记一次线上SQL死锁事故:如何避免死锁?
数据库·sql
chengpei1471 小时前
实现一个自己的spring-boot-starter,基于SQL生成HTTP接口
java·数据库·spring boot·sql·http
程序媛徐师姐2 小时前
Python基于Django的社区爱心养老管理系统设计与实现【附源码】
python·django·社区爱心养老·社区爱心养老管理系统·python社区养老管理系统·社区养老·社区养老管理系统
叫我:松哥2 小时前
基于Python django的音乐用户偏好分析及可视化系统设计与实现
人工智能·后端·python·mysql·数据分析·django