DRF:在序列化程序验证期间访问 SerializerMethodField

Tim*_*ony 4 django django-rest-framework

我正在使用 Django Rest Framework 3.0 并且我有一个模型:

class Vote(models.Model):
    name = ...
    token = models.CharField(max_length=50)
Run Code Online (Sandbox Code Playgroud)

token我从requestIP 信息生成的唯一标识符在哪里,以防止同一用户投票两次

我有一个序列化程序:

class VoteSerializer(serializers.ModelSerializer):
    name = ...
    token = serializers.SerializerMethodField()

    class Meta:
        model = Vote
        fields = ("id", "name", "token")

    def validate(self, data):
        if Rating.objects.filter(token=data['token'], name=data['name']).exists():
            raise serializers.ValidationError("You have already voted for this")
        return data

    def get_token(self, request):
        s = ''.join((self.context['request'].META['REMOTE_ADDR'], self.context['request'].META.get('HTTP_USER_AGENT', '')))
        return md5(s).hexdigest()
Run Code Online (Sandbox Code Playgroud)

和一个 CreateView

但我得到了一个

KeyError: 'token' 
Run Code Online (Sandbox Code Playgroud)

当我尝试发布并创建一个新的Vote. 为什么token验证时字段不包含在数据中?

文档提到

它可用于将任何类型的数据添加到对象的序列化表示中。

所以我会认为它也可以在validate?

Tim*_*ony 7

调查,似乎SerializerMethodField在验证发生后调用字段(没有深入研究代码,我不知道这是为什么 - 这似乎违反直觉)。

相反,我将相关代码移到了视图中(老实说,这在概念上实际上更有意义)。

为了让它工作,我需要做以下事情:

class VoteCreateView(generics.CreateAPIView):
    serializer_class = VoteSerializer

    def get_serializer(self, *args, **kwargs):
        # kwarg.data is a request MergedDict which is immutable so we have
        # to copy the data to a dict first before inserting our token
        d = {}
        for k, v in kwargs['data'].iteritems():
            d[k] = v
        d['token'] = self.get_token()
        kwargs['data'] = d
        return super(RatingCreateView, self).get_serializer(*args, **kwargs)

    def get_token(self):
        s = ''.join((self.request.META['REMOTE_ADDR'], self.request.META.get('HTTP_USER_AGENT', '')))
        return md5(s).hexdigest()
Run Code Online (Sandbox Code Playgroud)

我真的希望这不是正确的方法,因为对于看起来非常简单的情况来说,这似乎完全令人费解。希望其他人可以发布更好的方法。