DRF:通过单个Serializer更新一对一关联双模型的问题求解
问题:单个Serializer更新一对一关联的两个模型失败
现象描述
- GET请求可正常返回合并后的嵌套数据,但执行更新操作时抛出错误:
'User has no field named 'doctor_type'' - 传入的请求JSON为嵌套结构:
{ "name": "Dr. Strange updated twice", "profile" : { "doctor_type": "PSYCHIATRIST" } }
- 但Serializer接收的
validated_data是扁平化结构,丢失了profile键:
{ "name": "Maruf updated trice", "doctor_type": "PSYCHIATRIST" }
- 当前使用
source='*'处理嵌套字段,移除该参数后GET请求触发AttributeError
问题根源
source='*'会将父模型(CustomUser)的整个实例传递给子序列化器DoctorProfileFields,序列化(GET)时虽然能通过关联关系取到DoctorProfile的数据,但反序列化时子序列化器会直接将自身字段平铺到父级的validated_data中,导致嵌套结构丢失,进而让Serializer尝试在CustomUser模型中查找doctor_type字段,最终报错。
解决方案
修正Serializer代码
调整子序列化器的source参数,明确指向关联字段的related_name,同时优化更新逻辑:
from django.db import transaction from rest_framework import serializers from .models import CustomUser, DoctorProfile class DoctorProfileFields(serializers.ModelSerializer): """嵌套使用的医生资料字段序列化器""" class Meta: model = DoctorProfile fields = ('doctor_type', 'title', 'date_of_birth', 'registration_number', 'gender', 'city', 'country', ) # 根据业务需求设置字段是否必填,支持部分更新 extra_kwargs = { 'doctor_type': {'required': False}, 'title': {'required': False}, 'date_of_birth': {'required': False}, 'registration_number': {'required': False}, 'gender': {'required': False}, } class DoctorProfileSerializer(serializers.ModelSerializer): """查询、更新和删除医生用户及关联资料的序列化器""" # 使用模型中定义的related_name作为source,替代原来的'*' profile = DoctorProfileFields(source='doctor_profile', required=False) class Meta: model = CustomUser fields = ('name', 'avatar', 'profile', ) @transaction.atomic def update(self, instance, validated_data): # 提取关联的医生资料数据,键名对应source设置的'doctor_profile' profile_data = validated_data.pop('doctor_profile', {}) # 更新CustomUser实例 for attr, value in validated_data.items(): setattr(instance, attr, value) instance.save() # 更新或创建DoctorProfile实例(确保关联关系存在) if profile_data: doctor_profile, _ = DoctorProfile.objects.get_or_create(owner=instance) for attr, value in profile_data.items(): setattr(doctor_profile, attr, value) doctor_profile.save() return instance
关键修正点
- 修正source参数:将
source='*'改为source='doctor_profile',对应DoctorProfile模型中owner字段的related_name='doctor_profile',明确关联关系,确保序列化和反序列化时都能正确识别嵌套结构。 - 添加required=False:避免用户仅更新
CustomUser字段时,因缺少profile字段而触发验证错误。 - 优化更新逻辑:
- 使用
setattr+save()替代update()方法,触发模型的验证逻辑和信号(若有定义)。 - 用
get_or_create确保即使医生资料不存在也能创建(可根据业务需求调整为直接get,若要求每个医生必须有资料)。
- 使用
- 事务保障:保留
@transaction.atomic,确保两个模型的更新操作原子性,避免数据不一致。
方案合理性
这种嵌套序列化器的实现方式是DRF中处理一对一关联模型更新的标准方案,具备以下优势:
- 逻辑清晰,明确关联关系,避免原方案中
source='*'带来的模糊性和反序列化问题。 - 支持灵活的部分更新,用户可选择仅更新用户信息或仅更新医生资料。
- 原子性操作保证数据一致性,符合业务场景需求。
内容的提问来源于stack exchange,提问作者Imtiaz Ahmed
相关产品推荐
相关产品推荐

