Django REST Framework的ModelSerializer因缺失user字段引发KeyError
解决ModelSerializer调用.data时的KeyError问题
问题场景
调用AuthTokenResponseSerializer的.data方法时触发KeyError,提示无法找到user字段,传入的数据中仅包含user_id键,而非Serializer期望的user键。
相关代码片段
AuthToken模型
class AuthToken(models.Model): objects = AuthTokenManager() digest = models.CharField( max_length=CONSTANTS.DIGEST_LENGTH, primary_key=True) token_key = models.CharField( max_length=CONSTANTS.TOKEN_KEY_LENGTH, db_index=True) user = models.ForeignKey(User, null=False, blank=False, related_name='auth_token_set', on_delete=models.CASCADE) created = models.DateTimeField(auto_now_add=True) expiry = models.DateTimeField(null=True, blank=True) def __str__(self): return '%s : %s' % (self.digest, self.user)
自定义Serializer
class AuthTokenResponseSerializer(serializers.ModelSerializer): class Meta: model = AuthToken fields = "__all__"
错误信息
>>> token_serializer.data *** KeyError: "Got KeyError when attempting to get a value for field `user` on serializer `AuthTokenResponseSerializer`. The serializer field might be named incorrectly and not match any attribute or key on the `dict` instance. Original exception text was: 'user'."
传入的数据结构
AuthTokenResponseSerializer(context={'request': <rest_framework.request.Request: POST '/auth/token/'>, data={'_state': <django.db.models.base.ModelState object>, 'digest': '6b82ac8776113db376d8938a641c3ebdea573b4ab49cb648c171fccc7f4a517e6450cebb21682e26f36562cf7b659f5089957ba49e99c096ebc9eaaa93ab0e53', 'token_key': '872832f8', 'user_id': 34, 'created': None, 'expiry': datetime.datetime(2024, 5, 11, 9, 17, 2, 540546, tzinfo=datetime.timezone.utc)}, partial=True): digest = CharField(max_length=128, validators=[<UniqueValidator(queryset=AuthToken.objects.all())>]) token_key = CharField(max_length=8) created = DateTimeField(read_only=True) expiry = DateTimeField(allow_null=True, required=False) user = PrimaryKeyRelatedField(queryset=User.objects.all())
User模型
from django.db import models from django.utils import timezone from django.contrib.auth.models import ( AbstractBaseUser, PermissionsMixin, BaseUserManager, ) from django.utils.translation import gettext_lazy as _ from phonenumber_field.modelfields import PhoneNumberField class User(AbstractBaseUser, PermissionsMixin): email = models.EmailField( verbose_name=_("email address"), unique=True, max_length=255 ) password = models.CharField( max_length=128, null=True ) first_name = models.CharField(max_length=128, null=True) last_name = models.CharField(max_length=128, null=True) phone_number = PhoneNumberField(null=True, blank=True) is_staff = models.BooleanField(default=False) is_active = models.BooleanField(default=True) created_at = models.DateTimeField(default=timezone.now) updated_at = models.DateTimeField(default=timezone.now)
修复方案
方案1:修改传入数据的键名
在初始化Serializer前,将数据中的user_id重命名为user,直接传入用户ID即可:
# 假设原始数据存放在data变量中 data['user'] = data.pop('user_id') token_serializer = AuthTokenResponseSerializer(data=data)
方案2:自定义Serializer字段映射
如果不想修改原始数据,可在Serializer中显式指定user字段的取值来源为user_id:
class AuthTokenResponseSerializer(serializers.ModelSerializer): user = serializers.PrimaryKeyRelatedField( queryset=User.objects.all(), source='user_id' ) class Meta: model = AuthToken fields = "__all__"
若仅用于序列化输出,可将user字段设为只读,Serializer会自动关联用户实例:
class AuthTokenResponseSerializer(serializers.ModelSerializer): user = serializers.PrimaryKeyRelatedField(read_only=True) class Meta: model = AuthToken fields = "__all__"
方案3:传入AuthToken模型实例而非字典
如果是从数据库获取的AuthToken对象,直接传入实例而非字典,Serializer会自动处理关联字段:
token_instance = AuthToken.objects.get(digest='xxx') token_serializer = AuthTokenResponseSerializer(instance=token_instance)
内容的提问来源于stack exchange,提问作者Liondancer
相关产品推荐
相关产品推荐

