DRF中SprintSerializer报错:'links'字段未纳入fields选项的调试求助
调试DRF序列化器AssertionError问题
我在复现Julia Elman书籍中的示例时遇到了一个AssertionError,相关代码和错误信息如下:
相关代码
models.py
class Sprint(models.Model): name = models.CharField(max_length=100, blank=True, default='') description = models.TextField(blank=True, default='') end = models.DateField(unique=True) def __str__(self): return self.name or _('Sprint ending %s') % self.end
serializer.py
from rest_framework import serializers from django.contrib.auth import get_user_model from rest_framework.reverse import reverse from .models import Sprint, Task User = get_user_model() class SprintSerializer(serializers.ModelSerializer): links = serializers.SerializerMethodField('get_links') class Meta: model = Sprint fields = ('id', 'name', 'description', 'end', 'links',) def get_links(self, obj): request = self.context['request'] return { 'self': reverse('sprint-detail',kwargs={'pk': obj.pk},request=request), }
views.py
from django.contrib.auth import get_user_model from rest_framework import authentication, permissions, viewsets from .models import Sprint,Task from .serializers import SprintSerializer,TaskSerializer, UserSerializer User = get_user_model() class DefaultsMixin(object): authentication_classes = (authentication.BasicAuthentication,authentication.TokenAuthentication,) permission_classes = (permissions.IsAuthenticated,) paginate_by = 25 paginate_by_param = 'page_size' max_paginate_by = 100 class SprintViewSet(DefaultsMixin, viewsets.ModelViewSet): queryset = Sprint.objects.order_by('end') serializer_class = SprintSerializer
操作步骤与错误
我尝试在Django shell中查看序列化器的repr:
from board.serializers import SprintSerializer >>> s = SprintSerializer() >>> print(repr(s))
触发了如下错误:
AssertionError: The field 'links' was declared on serializer SprintSerializer, but has not been included in the 'fields' option.
我的DRF版本是:3.8.2
问题分析与解决方案
这个错误看起来有点矛盾——明明我们已经把links加入了fields列表,为什么DRF还说没包含?其实问题出在序列化器初始化时的上下文(context)缺失。
当你在shell中直接实例化SprintSerializer()而不传入context时,get_links方法里尝试访问self.context['request']会失败,但DRF 3.8.x版本中,这种情况下的错误提示被误导成了字段未包含的AssertionError(后续版本修复了这个错误提示的问题)。
调试与解决步骤:
在shell中手动传入request上下文:
你可以用Django的RequestFactory构造一个request对象传入序列化器的context,示例代码如下:from board.serializers import SprintSerializer from django.test.client import RequestFactory factory = RequestFactory() request = factory.get('/') s = SprintSerializer(context={'request': request}) print(repr(s))这样就能正常生成序列化器的repr了。
验证视图中的序列化器工作情况:
你的视图集已经通过DefaultsMixin处理了认证逻辑,且视图集在实例化序列化器时会自动传入request上下文,所以实际接口请求时,这个序列化器应该能正常工作,不会出现该错误。可选:给
get_links方法增加容错处理:
为了避免类似的上下文缺失问题,你可以在方法里先检查request是否存在:def get_links(self, obj): request = self.context.get('request') if not request: return {} return { 'self': reverse('sprint-detail',kwargs={'pk': obj.pk},request=request), }这样即使上下文里没有request,也不会抛出错误,序列化器能正常初始化。
内容的提问来源于stack exchange,提问作者Richard Rublev
相关产品推荐
相关产品推荐

