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

如何在DRF中编写与更新嵌套序列化器(无需第三方包)

Django序列化器与JWT认证问题解决方案

针对你提出的三个问题,逐个给出解决方案:

1. 不借助第三方包编写并更新UserProfile的嵌套序列化器

由于UserProfile与Account是外键关联,我们可以在UserProfileSerializer中通过嵌套序列化器实现关联操作,同时处理创建和更新逻辑:

基础实现代码(关联已有用户)

class UserProfileSerializer(ModelSerializer):
    # 嵌套Account序列化器,仅用于返回用户信息(前端无需传入)
    user = AccountSerializer(read_only=True)

    class Meta:
        model = UserProfile
        fields = [
            "user",
            "address_line_1",
            "address_line_2",
            "city",
            "state",
            "country",
            "gender",
            "phone",
            "date_of_birth",
        ]

    def create(self, validated_data):
        # 从上下文获取当前登录用户(下文会讲如何传递上下文)
        user = self.context.get('request').user
        return UserProfile.objects.create(user=user, **validated_data)

    def update(self, instance, validated_data):
        # 逐个更新档案字段
        instance.address_line_1 = validated_data.get('address_line_1', instance.address_line_1)
        instance.address_line_2 = validated_data.get('address_line_2', instance.address_line_2)
        instance.phone = validated_data.get('phone', instance.phone)
        # 其他字段同理补充
        instance.save()
        return instance

扩展:同时创建Account和UserProfile

如果需要在一次请求中同时创建用户和档案,修改create方法即可:

def create(self, validated_data):
    # 提取嵌套的Account数据
    user_data = validated_data.pop('user')
    # 调用AccountSerializer的create方法创建用户
    user = AccountSerializer.create(AccountSerializer(), validated_data=user_data)
    # 创建关联的UserProfile
    return UserProfile.objects.create(user=user, **validated_data)

此时需要将user = AccountSerializer(required=True),前端需传入嵌套的user数据。

2. JWT认证时在序列化器中获取request.user

JWT认证通过后,request对象会包含当前登录用户,只需将request传入序列化器的上下文(context),即可在序列化器方法中获取:

步骤1:在视图中传递上下文

方法一:基于GenericView重写get_serializer_context

from rest_framework.generics import CreateAPIView, UpdateAPIView

class UserProfileCreateView(CreateAPIView):
    serializer_class = UserProfileSerializer

    def get_serializer_context(self):
        context = super().get_serializer_context()
        # 将request传入上下文
        context['request'] = self.request
        return context

方法二:手动实例化序列化器时传入上下文

from rest_framework import status
from rest_framework.response import Response

def post(self, request):
    serializer = UserProfileSerializer(data=request.data, context={'request': request})
    if serializer.is_valid():
        serializer.save()
        return Response(serializer.data, status=status.HTTP_201_CREATED)
    return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)

步骤2:在序列化器中获取用户

def create(self, validated_data):
    # 从上下文的request中提取当前用户
    user = self.context.get('request').user
    return UserProfile.objects.create(user=user, **validated_data)

3. 为什么设置user = AccountSerializer(required=False)才能正常运行

核心原因是你的UserProfile创建逻辑依赖上下文的当前用户,而非前端传入的user序列化数据:

  • UserProfile模型的user字段是外键,默认属于必填项。如果序列化器中user = AccountSerializer()不设置required=False,DRF会强制要求前端传入完整的user嵌套数据(如first_name、email等),否则就会抛出user: ["This field is required"]的错误。
  • 而你的实际逻辑是从上下文获取当前登录用户来创建档案,根本不需要前端传user数据,所以更合理的做法是将user设为read_only=True(只用于返回数据,不接收前端输入):
user = AccountSerializer(read_only=True)

修正后的完整模型代码(修正笔误+补充必要配置)

from django.db import models
from django.contrib.auth.models import AbstractBaseUser

class Account(AbstractBaseUser):
    first_name = models.CharField(max_length=50)
    last_name = models.CharField(max_length=50)
    email = models.EmailField(max_length=100, unique=True)

    # 继承AbstractBaseUser必须定义以下字段
    USERNAME_FIELD = 'email'
    REQUIRED_FIELDS = ['first_name', 'last_name']

class UserProfile(models.Model): 
    user = models.ForeignKey(Account, on_delete=models.CASCADE)
    address_line_1 = models.CharField(max_length=100)
    address_line_2 = models.CharField(max_length=100)
    phone = models.IntegerField(null=True)
    # 补充序列化器中用到的其他字段
    city = models.CharField(max_length=50)
    state = models.CharField(max_length=50)
    country = models.CharField(max_length=50)
    gender = models.CharField(max_length=10)
    date_of_birth = models.DateField(null=True)

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.12 09:01:43