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方法两者或其一。
相关推荐
lifallen5 分钟前
Paimon vs. HBase:全链路开销对比
java·大数据·数据结构·数据库·算法·flink·hbase
wt_cs18 分钟前
银行回单ocr api集成解析-图像文字识别-文字识别技术
开发语言·python
_WndProc40 分钟前
【Python】Flask网页
开发语言·python·flask
互联网搬砖老肖42 分钟前
Python 中如何使用 Conda 管理版本和创建 Django 项目
python·django·conda
测试者家园1 小时前
基于DeepSeek和crewAI构建测试用例脚本生成器
人工智能·python·测试用例·智能体·智能化测试·crewai
大模型真好玩1 小时前
准确率飙升!Graph RAG如何利用知识图谱提升RAG答案质量(四)——微软GraphRAG代码实战
人工智能·python·mcp
Brookty1 小时前
【MySQL】JDBC编程
java·数据库·后端·学习·mysql·jdbc
前端付豪1 小时前
11、打造自己的 CLI 工具:从命令行到桌面效率神器
后端·python
前端付豪1 小时前
12、用类写出更可控、更易扩展的爬虫框架🕷
后端·python
江太翁1 小时前
Pytorch torch
人工智能·pytorch·python