You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在Django REST Framework视图中批量保存关联模型数据?

Django REST API 多关联模型批量创建方案

一、基础模型对应关系(基于需求补全)

先对齐模型结构,确保关联逻辑清晰:

from django.db import models

class Category(models.Model):
    name = models.CharField(max_length=100)

class Product(models.Model):
    name = models.CharField(max_length=200)
    desc = models.TextField(null=True, blank=True)
    categories = models.ManyToManyField(Category, through='ProductsCategory')

class ProductsCategory(models.Model):
    product = models.ForeignKey(Product, on_delete=models.CASCADE)
    category = models.ForeignKey(Category, on_delete=models.CASCADE)
    created_at = models.DateTimeField(auto_now_add=True)

class Pictures(models.Model):
    product = models.ForeignKey(Product, on_delete=models.CASCADE)
    image = models.ImageField(upload_to='product_pics/')
    alt_text = models.CharField(max_length=100, null=True, blank=True)

二、序列化器编写(处理嵌套关联)

自定义序列化器,单独处理多对多中间表和外键图片的嵌套输入:

from rest_framework import serializers
from django.db import transaction
from .models import Product, ProductsCategory, Pictures, Category

class CategorySerializer(serializers.ModelSerializer):
    class Meta:
        model = Category
        fields = ['id', 'name']

class ProductsCategorySerializer(serializers.ModelSerializer):
    category = serializers.PrimaryKeyRelatedField(queryset=Category.objects.all())

    class Meta:
        model = ProductsCategory
        fields = ['category']

class PicturesSerializer(serializers.ModelSerializer):
    class Meta:
        model = Pictures
        fields = ['image', 'alt_text']

class ProductCreateSerializer(serializers.ModelSerializer):
    categories = ProductsCategorySerializer(many=True)
    pictures = PicturesSerializer(many=True)

    class Meta:
        model = Product
        fields = ['name', 'desc', 'categories', 'pictures']

    def create(self, validated_data):
        # 剥离关联数据,单独处理
        categories_data = validated_data.pop('categories')
        pictures_data = validated_data.pop('pictures')
        
        # 原子事务保证数据一致性
        with transaction.atomic():
            product = Product.objects.create(**validated_data)
            
            # 批量创建多对多关联
            category_relations = [
                ProductsCategory(product=product, category=item['category'])
                for item in categories_data
            ]
            ProductsCategory.objects.bulk_create(category_relations)
            
            # 批量创建附属图片
            pictures = [
                Pictures(product=product, **pic_data)
                for pic_data in pictures_data
            ]
            Pictures.objects.bulk_create(pictures)
            
            return product

三、视图层实现

用ModelViewSet快速实现CRUD,聚焦创建逻辑:

from rest_framework import viewsets
from .models import Product
from .serializers import ProductCreateSerializer

class ProductViewSet(viewsets.ModelViewSet):
    queryset = Product.objects.all()
    serializer_class = ProductCreateSerializer

四、输入数据格式示例

POST请求分两种情况:

  1. 纯JSON(仅用于测试,图片需用文件上传):
{
    "name": "新款无线耳机",
    "desc": "降噪功能,续航24小时",
    "categories": [
        {"category": 1},
        {"category": 3}
    ],
    "pictures": [
        {"alt_text": "耳机正面"},
        {"alt_text": "耳机充电仓"}
    ]
}
  1. 文件上传(生产环境用):
    请求头设为Content-Type: multipart/form-data,图片字段提交文件流,其他字段按表单或JSON提交。

五、核心优化点

  • 原子事务:用transaction.atomic()包裹所有操作,避免主产品创建成功但关联数据保存失败的不一致情况。
  • 批量创建:用bulk_create替代循环创建,大幅提升大数量关联数据的写入性能。
  • 前置验证:在序列化器validate方法中添加自定义规则,比如检查分类ID是否存在、图片格式是否合法,提前拦截无效数据:
    def validate(self, attrs):
        category_ids = [item['category'].id for item in attrs['categories']]
        if not Category.objects.filter(id__in=category_ids).count() == len(category_ids):
            raise serializers.ValidationError("部分分类ID不存在")
        return attrs
    
  • 异常处理:捕获创建过程中的异常,返回友好错误信息,避免服务器崩溃。

内容的提问来源于stack exchange,提问作者Navid Vali

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.08 21:21:00