我目前正在为uni项目构建API。我面临以自定义方式生成身份验证令牌的问题。让我解释一下:我应该在端点上收到带有以下内容的POST请求:
{
"university_id": 1,
"coords":{
"latitude": 0.0,
"longitude": 0.0
}
}
这个想法是,在给定university_id
和coords
的情况下,后端对其进行验证(检查坐标是否在有效区域内),然后返回这样的令牌:
{
"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.1
和Django Rest framework 3.8.2
。
我将非常感谢您的帮助,以及对此代码的任何其他评论(毕竟我仍在学习中)。我猜我缺少方法,但不确定在哪里。
答案 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')
下一步,您应该将key
和created
设为只读,因为您不想从用户输入中获取它们的值,所以只想在创建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)