继承Djoser的UserCreatePasswordRetypeSerializer并关联Customer模型的实现疑问
咱们先理清楚你现在遇到的问题:你直接继承UserCreatePasswordRetypeSerializer后,把父类的字段加到CustomerSerializer里,结果报错,核心原因是这个父序列化器的字段都是针对User模型的,而你的Customer模型只有customer_name和关联User的外键,根本没有User那些字段(比如username、password),Django自然找不到对应字段,肯定会报错。
接下来聊聊你提到的两种解决方案:
方案一:把Customer改成继承用户抽象模型(推荐用AbstractUser)
如果你的项目还没做过数据库迁移(也就是还没运行过migrate),这个方案是可行的,但有个关键细节要注意:别直接继承User具体模型,而是继承django.contrib.auth.models.AbstractUser,然后在项目的settings.py里配置AUTH_USER_MODEL = "你的app名.Customer"。
这么做之后,Djoser的所有序列化器都会自动适配你的Customer模型(因为Djoser默认使用AUTH_USER_MODEL指定的用户模型)。这时候你写序列化器就简单了:
from djoser.serializers import UserCreatePasswordRetypeSerializer from .models import Customer class CustomerSerializer(UserCreatePasswordRetypeSerializer): class Meta: model = Customer fields = UserCreatePasswordRetypeSerializer.Meta.fields + ('customer_name',)
因为此时Customer已经继承了AbstractUser的所有字段(用户名、密码等),再加上你自己的customer_name,完全能匹配父序列化器的字段要求,不会报错。
但如果你的项目已经迁移过数据库,这种方法就不适用了——因为数据库里已经存在User表和Customer表,强行改继承会导致数据库结构冲突,风险很高。
方案二:保留原Customer外键关联的结构,重写序列化器的create方法
如果是已经上线或做过迁移的老项目,咱们就用这种更稳妥的方式:
咱们要做的是在一个序列化器里同时处理User和Customer的创建逻辑,具体代码如下:
from rest_framework import serializers from djoser.serializers import UserCreatePasswordRetypeSerializer from .models import Customer class CustomerCreateSerializer(UserCreatePasswordRetypeSerializer): # 新增customer_name字段 customer_name = serializers.CharField(max_length=255) class Meta(UserCreatePasswordRetypeSerializer.Meta): # 把customer_name加到父类的字段列表里 fields = UserCreatePasswordRetypeSerializer.Meta.fields + ('customer_name',) def create(self, validated_data): # 先从验证后的数据里取出customer_name customer_name = validated_data.pop('customer_name') # 调用父类的create方法创建User实例 user = super().create(validated_data) # 用刚创建的User实例关联创建Customer Customer.objects.create(user=user, customer_name=customer_name) return user
这样前端提交包含用户注册字段(比如用户名、密码)和customer_name的请求时,这个序列化器会先创建User,再自动创建对应的Customer,完美解决字段不匹配的问题。
最后再给你提个小建议:如果是新项目,优先用方案一的自定义用户模型,后续扩展用户字段会更方便;老项目就用方案二,兼容现有数据库结构。
备注:内容来源于stack exchange,提问作者Evren Bingøl

