【问题标题】:Custom token generation with Django rest framework使用 Django REST 框架生成自定义令牌
【发布时间】:2019-02-03 03:02:47
【问题描述】:

我目前正在为一个 uni 项目构建 API。我遇到了以自定义方式生成身份验证令牌的问题。让我解释一下:我应该在端点中收到一个 POST 请求,内容如下:

{
   "university_id": 1,
    "coords":{
        "latitude": 0.0,
        "longitude": 0.0
    }
}

想法是,给定university_idcoords,后端对其进行验证(检查坐标是否在有效区域内),然后返回一个令牌,如下所示:

{
    "token": asdfsagag23214,
}

如您所见,没有涉及登录凭据(也没有客户端应用程序的用户模型),所以我猜我需要创建一个自定义令牌。我查阅了 Django REST Framework 文档,并为我的令牌模型提出了类似的内容:

class AuthToken(models.Model):
    key = models.CharField(verbose_name='Key', max_length=40, primary_key=True)
    created = models.DateTimeField(
        verbose_name='Creation date', auto_now_add=True)

    class Meta:
        verbose_name = 'Token'
        verbose_name_plural = 'Tokens'

    def save(self, *args, **kwargs):
        if not self.key:
            self.key = self.generate_key()
        return super().save(*args, **kwargs)

    def generate_key(self):
        return binascii.hexlify(os.urandom(20)).decode()

    def __str__(self):
        return self.key

然后是序列化器:

class AuthTokenSerializer(serializers.Serializer):
    class Meta:
        model = AuthToken
        fields = ('key', 'created', )

    def to_internal_value(self, data):
        university = data.get('university')
        coords = data.get('coords')

        if not university:
            raise serializers.ValidationError({
                'university': 'This field is required.'
            })
        if not coords:
            raise serializers.ValidationError({
                'coords': 'This field is required.'
            })

        # coordinate validation goes here

        return {
            'university': int(university),
            'coords': coords
        }

    def create(self, validated_data):
        return AuthToken.objects.create(**validated_data)

最后,views.py

@api_view(['POST'])
def generate_token(request):
    if request.method == 'POST':
        serializer = AuthTokenSerializer(data=request.data)
        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)

我不确定我错过了什么,但我无法完全理解我需要做些什么才能使其正常工作。现在我正在使用 swagger 对其进行测试,它无法以任何方式工作,形状或形式,没有参数可输入,甚至通过终端使用 cURL 似乎也没有给我预期的结果。

为了记录,我使用的是Django 2.1Django Rest framework 3.8.2

我将不胜感激任何帮助,以及有关此代码的任何进一步的 cmets(毕竟我仍在学习)。我猜我缺少方法,但不确定在哪里。

【问题讨论】:

    标签: django django-rest-framework token python-3.6


    【解决方案1】:

    我一步一步来。

    首先,您需要为坐标定义一个序列化程序,因为获取和验证用户输入的标准方法是通过序列化程序。所以:

    class Coordinates(serializers.Serializer):
        latitude = serializers.FloatField(min_value=-90, max_value=90)
        longitude = serializers.FloatField(min_value=-180, max_value=180)
    

    所以我猜为字段设置最小值和最大值可以进行足够的验证。如果您想对该字段进行更多验证,可以使用 field level validation

    其次,将serializer.ModelSerializer 用于AuthTokenSerializer,然后添加此字段:

        coords = Coordinates(write_only=True)
    

    并使其write_only

    您也可以使用PrimaryKeyRelatedField 获得这样的大学:

        university = serializers.PrimaryKeyRelatedField(queryset=University.objects.all(), write_only=True)
    

    到目前为止,您的序列化程序应该是这样的:

    class AuthTokenSerializer(serializers.ModelSerializer):
        university = serializers.PrimaryKeyRelatedField(queryset=University.objects.all(), write_only=True)
        coords = Coordinates(write_only=True)
    
        class Meta:
            model = AuthToken
            fields = ('key', 'created', 'coords', 'university')
    

    在下一步中,您应该将keycreated 设为read_only,因为您不想从用户输入中获取它们的值,您只想将它​​们的值显示给创建AuthToken 实例后的用户。所以将它们添加到 Meta 类中的read_only_fields

    在下一步中,您应该重写序列化程序validate 方法,并基于多个字段进行验证(

    检查坐标是否在有效区域内

    )

        def validate(self, attrs):
            ### the the validation here, something like this
            ### you can access the selected university instance: attrs['university']
    
            if attrs['coords']['latitude'] < attrs['university']:# blah blah blah, anyway you want to validate
                raise serializers.ValidationError({'coords': ['invalid coords']})
    
            return super().validate(attrs)
    

    所以在最后一步中,您应该覆盖 create 方法并弹出模型中没有它们的字段(大学和坐标):

        def create(self, validated_data):
            ###  your AuthToken does not have this field, you should pop university and coords
            ###  out before creating an instance of Authtoken
            validated_data.pop('university')
            validated_data.pop('coords')
    
            return super().create(validated_data)
    

    最后你的序列化器会变成这样:

    class AuthTokenSerializer(serializers.ModelSerializer):
        university = serializers.PrimaryKeyRelatedField(queryset=University.objects.all(), write_only=True)
        coords = Coordinates(write_only=True)
    
        class Meta:
            model = AuthToken
            fields = ('key', 'created', 'coords', 'university')
            read_only_fields = ('key', 'created')
    
    
        def validate(self, attrs):
            ### the the validation here, something like this
            ### you can access the selected university instance: attrs['university']
    
            if attrs['coords']['latitude'] < attrs['university']:# blah blah blah, anyway you want to validate
                raise serializers.ValidationError({'coords': ['invalid coords']})
    
            return super().validate(attrs)
    
    
        def create(self, validated_data):
            ###  your AuthToken does not have this field, you should pop university and coords
            ###  out before createing an instance of Authtoken
            validated_data.pop('university')
            validated_data.pop('coords')
    
            return super().create(validated_data)
    

    【讨论】:

    • 成功了。我试图确切地找到如何插入所需的验证,但我不知道我需要为坐标创建一个单独的序列化程序。非常感谢。
    猜你喜欢
    • 1970-01-01
    • 2016-06-23
    • 2016-05-03
    • 2018-08-19
    • 2016-01-17
    • 1970-01-01
    • 2013-01-28
    • 2023-03-29
    • 2020-07-16
    相关资源
    最近更新 更多