如何在Django REST中实现带through的多对多模型POST操作
解决Django REST框架中多对多Through模型的读写与嵌套返回问题
我之前刚好踩过这个坑,给你一步步拆解怎么实现需求:
1. 实现GET请求返回嵌套的Through关联信息
首先需要为你的CaseICD10Connection模型写一个序列化器,用来序列化中间表的关联信息;然后在KeyDateCase的序列化器中嵌套这个序列化器,指定对应的反向关联源。
from rest_framework import serializers from .models import KeyDateCase, CaseICD10Connection, ICD10 # 先定义中间表的序列化器 class CaseICD10ConnectionSerializer(serializers.ModelSerializer): # 如果需要展示ICD10的详情,可以嵌套ICD10的序列化器 icd10 = serializers.ModelSerializer(read_only=True, fields=['id', 'code', 'name']) class Meta: model = CaseICD10Connection fields = ['id', 'icd10', '其他需要展示的中间表字段'] # 替换成你的实际字段 # 再定义主模型的序列化器 class KeyDateCaseSerializer(serializers.ModelSerializer): # 嵌套中间表的序列化器,source是反向关联的默认名称(模型小写_set) diagnoses_all_icd_10_details = CaseICD10ConnectionSerializer( source='caseicd10connection_set', many=True, read_only=True ) # 原多对多字段设为只读,因为我们通过中间表处理关联逻辑 diagnoses_all_icd_10 = serializers.PrimaryKeyRelatedField( queryset=ICD10.objects.all(), many=True, read_only=True ) class Meta: model = KeyDateCase fields = ['id', '其他主模型字段', 'diagnoses_all_icd_10_details'] # 替换成你的实际字段
这样当你发送GET请求时,就能在返回结果中看到嵌套的中间表关联信息了。
2. 实现Through模型的写入功能
默认的ModelSerializer对带through的多对多字段是只读的,所以需要重写序列化器的create和update方法,手动处理中间表的创建和更新。
class KeyDateCaseSerializer(serializers.ModelSerializer): # 保留上面的嵌套序列化器定义 diagnoses_all_icd_10_details = CaseICD10ConnectionSerializer( source='caseicd10connection_set', many=True, read_only=True ) # 新增一个字段用来接收前端传来的中间表数据,仅用于写入 icd_connections = serializers.ListField( child=serializers.DictField(), write_only=True ) class Meta: model = KeyDateCase fields = ['id', '其他主模型字段', 'diagnoses_all_icd_10_details', 'icd_connections'] def create(self, validated_data): # 先取出传入的中间表数据,从validated_data中移除 icd_connections_data = validated_data.pop('icd_connections') # 创建主模型实例 key_date_case = KeyDateCase.objects.create(**validated_data) # 逐个创建中间表关联 for connection_data in icd_connections_data: # 确保connection_data包含icd10_id和中间表的其他必填字段 CaseICD10Connection.objects.create( case=key_date_case, **connection_data ) return key_date_case def update(self, instance, validated_data): # 取出传入的中间表数据(如果有的话) icd_connections_data = validated_data.pop('icd_connections', None) # 更新主模型字段 for attr, value in validated_data.items(): setattr(instance, attr, value) instance.save() # 如果有传入新的中间表数据,先删除旧关联,再创建新的 if icd_connections_data is not None: CaseICD10Connection.objects.filter(case=instance).delete() for connection_data in icd_connections_data: CaseICD10Connection.objects.create( case=instance, **connection_data ) return instance
前端传入数据示例
发送POST/PUT请求时,前端需要按照下面的格式传入数据:
{ "其他主模型字段": "对应值", "icd_connections": [ { "icd10": 1, // ICD10的ID "中间表其他字段": "对应值" }, { "icd10": 2, "中间表其他字段": "对应值" } ] }
注意事项
- 确保中间表的字段(除了外键)都有合理的默认值或者前端会传入对应数据,避免创建时出错。
- 如果中间表有唯一约束(比如一个病例不能重复关联同一个ICD10),要在序列化器或者模型层做校验,防止重复创建。
- 嵌套序列化器中的
source要对应正确的反向关联名称,如果你在ForeignKey里设置了related_name,就用你设置的名称,而不是默认的模型名小写_set。
内容的提问来源于stack exchange,提问作者Gurkenkönig
相关产品推荐
相关产品推荐

