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

Django集成Djoser时用户资料接口未返回关联Worker信息

问题

我正在开发一个Django应用,用户分为普通应用用户和带有额外详情的Worker两类,用Djoser实现用户认证与注册。现在调用api/v1/auth/users/me/接口时,响应只返回用户基础信息,关联的Worker信息没返回,但已经确认该用户对应的Worker实例存在且关联正确。

已做的排查:

  • 验证Worker实例确实存在
  • 请求携带有效JWT令牌
  • 数据库关联关系无误

相关代码:

models.py

from django.db import models
from django.contrib.auth.models import AbstractUser, PermissionsMixin
from django.utils.translation import gettext_lazy as _
from .managers import CustomUserManager

class User(AbstractUser, PermissionsMixin):
    username = None
    first_name = models.CharField(_('First name'), max_length=100)
    last_name = models.CharField(_('last name'), max_length=100)
    email = models.EmailField(_('Email Address'), max_length=254, unique=True)
    is_staff = models.BooleanField(default=False)
    is_active = models.BooleanField(default=False)
    date_joined = models.DateTimeField(auto_now_add=True)
    USERNAME_FIELD = "email"
    REQUIRED_FIELDS = ["first_name", "last_name"]
    objects = CustomUserManager()
    class Meta:
        verbose_name = _("User")
        verbose_name_plural = _("Users")
    def __str__(self):
        return self.email
    @property
    def get_full_name(self):
        return f"{self.first_name} {self.last_name}"


class Worker(models.Model):
    user = models.OneToOneField(User, on_delete=models.CASCADE)
    categories = models.ManyToManyField('Category', related_name='workers')
    rating = models.DecimalField(max_digits=12, decimal_places=2, null=True, blank=True)
    numReviews = models.IntegerField(null=True, blank=True, default=0)
    pricePerHour = models.DecimalField(max_digits=12, decimal_places=2, null=True, blank=True)
    gender_choices = [
        ('M', 'Male'),
        ('F', 'Female'),
    ]
    gender = models.CharField(max_length=1, choices=gender_choices)
    profile_photo = models.ImageField(upload_to='profile_photos/', null=True, blank=True)
    num_tel = models.CharField(max_length=20)
    small_description = models.TextField(null=True, blank=True)
    description = models.TextField(null=True, blank=True)
    _id = models.AutoField(primary_key=True, editable=False)

    def __str__(self):
        return f"{self.user.email} - {self.num_tel}"

serializers.py(注:原代码标注为views.py,实际是序列化器文件)

from django.contrib.auth import get_user_model
from rest_framework.serializers import ModelSerializer
from .models import Category, Worker
from djoser.serializers import UserCreateSerializer
from djoser.serializers import UserSerializer as DjoserUserSerializer
from rest_framework import serializers

User = get_user_model()

class UserSerializer(serializers.ModelSerializer):
    class Meta:
        model = User
        fields = ['id', 'email', 'first_name', 'last_name']

class WorkerSerializer(serializers.ModelSerializer):
    user = UserSerializer()  # Serialize user details

    class Meta:
        model = Worker
        fields = '__all__' 

class CustomUserSerializer(DjoserUserSerializer):
    worker = WorkerSerializer(read_only=True)

    class Meta(DjoserUserSerializer.Meta):
        model = User
        fields = ['id', 'email', 'first_name', 'last_name', 'worker']

class CreateUserSerializer(UserCreateSerializer):
    class Meta(UserCreateSerializer.Meta):
        model = User
        fields = ['id', 'email', 'first_name', 'last_name', 'password']

class CategorySerializer(ModelSerializer):
    class Meta:
        model = Category
        fields = '__all__'

class ProvidersSerializer(ModelSerializer):
    user = UserSerializer()  # Include detailed user information

    class Meta:
        model = Worker
        fields = '__all__'

Djoser配置

DJOSER = {
    'LOGIN_FIELD': 'email',
    "USER_CREATE_PASSWORD_RETYPE": True,
    "USERNAME_CHANGED_EMAIL_CONFIRMATION": True,
    "PASSWORD_CHANGED_EMAIL_CONFIRMATION": True,
    "SEND_CONFIRMATION_EMAIL": True,
    "PASSWORD_RESET_CONFIRM_URL": "password/reset/confirm/{uid}/{token}",
    "SET_PASSWORD_RETYPE": True,
    "PASSWORD_RESET_CONFIRM_RETYPE": True,
    'USERNAME_RESET_CONFIRM_URL': 'username/reset/confirm/{uid}/{token}',
    'ACTIVATION_URL': 'activate/{uid}/{token}',
    'SEND_ACTIVATION_EMAIL': True,
    'SERIALIZERS': {
        'user_create': 'base.serializers.CreateUserSerializer',
        'user': 'base.serializers.CustomUserSerializer',
        'user_delete': 'djoser.serializers.UserDeleteSerializer',
    },  
}

疑问

导致Worker信息未出现在个人资料响应中的原因可能是什么?如何结合Djoser解决该问题?


原因分析与解决方案

核心原因

你遇到的问题主要有两个关键点:

  1. 循环序列化导致字段被忽略:CustomUserSerializer嵌套了WorkerSerializer,而WorkerSerializer又嵌套了UserSerializer,形成循环引用。Django REST Framework为防止无限递归,会自动跳过这类嵌套字段的序列化,导致Worker信息无法显示。
  2. 未预取关联数据(潜在性能问题):即使没有循环引用,默认查询逻辑不会预取Worker数据,会触发额外数据库查询,虽不影响显示但会降低接口性能。

解决步骤

1. 修复循环序列化问题

直接修改WorkerSerializer,移除嵌套的UserSerializer——在users/me/接口中,我们已经从User视角返回数据,无需反向嵌套User信息:

class WorkerSerializer(serializers.ModelSerializer):
    # 移除这一行:user = UserSerializer()
    class Meta:
        model = Worker
        fields = '__all__'
        # 也可明确排除user字段彻底避免循环:
        # exclude = ['user']

如果业务需要保留用户关联信息,可改用主键或仅返回必要字段:

class WorkerSerializer(serializers.ModelSerializer):
    user = serializers.PrimaryKeyRelatedField(read_only=True)
    class Meta:
        model = Worker
        fields = '__all__'

2. 让Djoser预取Worker数据

重载Djoser的UserViewSet,添加预取逻辑避免N+1查询,同时确保序列化器能获取到关联数据:
在views.py中添加:

from djoser.views import UserViewSet

class CustomUserViewSet(UserViewSet):
    def get_queryset(self):
        # 调用父类查询集并添加预取worker的逻辑
        return super().get_queryset().select_related('worker')

然后在urls.py中替换默认的users/me/路由:

from django.urls import path, include
from .views import CustomUserViewSet

urlpatterns = [
    # 替换默认me接口路由
    path('auth/users/me/', CustomUserViewSet.as_view({'get': 'me', 'put': 'me', 'patch': 'me'}), name='user-me'),
    # 保留其他Djoser路由
    path('auth/', include('djoser.urls')),
    path('auth/', include('djoser.urls.jwt')),
]

3. 确认序列化器字段配置

你的CustomUserSerializer已经正确包含worker字段,这一步无需修改:

class CustomUserSerializer(DjoserUserSerializer):
    worker = WorkerSerializer(read_only=True)

    class Meta(DjoserUserSerializer.Meta):
        model = User
        fields = ['id', 'email', 'first_name', 'last_name', 'worker']

4. 测试验证

重启服务后,再次调用api/v1/auth/users/me/接口,即可看到返回的Worker信息。

额外优化建议

  • 如果需要返回Worker关联的分类信息,可在WorkerSerializer中添加categories = CategorySerializer(many=True, read_only=True),注意避免与Category模型形成新的循环引用。
  • 给CustomUserSerializer的worker字段加上required=False,适配普通用户没有Worker实例的场景。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.20 16:24:51