使用Django rest框架生成自定义令牌

时间:2018-08-28 12:58:53

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

我目前正在为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

我将非常感谢您的帮助,以及对此代码的任何其他评论(毕竟我仍在学习中)。我猜我缺少方法,但不确定在哪里。

1 个答案:

答案 0 :(得分: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的{​​{1}}用作AuthTokenSerializer,然后向其中添加此字段:

    coords = Coordinates(write_only=True)

并将其设置为只写

您还可以使用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设为只读,因为您不想从用户输入中获取它们的值,所以只想在创建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)