Генерация пользовательских токенов с Django rest framework - PullRequest
0 голосов
/ 28 августа 2018

В настоящее время я работаю над созданием API для универа. Я столкнулся с проблемой генерации токена аутентификации. Позвольте мне объяснить: я должен получить запрос POST в конечной точке со следующим:

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

Идея состоит в том, что, учитывая university_id и coords, бэкэнд проверяет его (проверяет, находятся ли координаты внутри допустимой области), а затем возвращает токен, например:

{
    "token": asdfsagag23214,
}

Как видите, учетные данные для входа не используются (и модель User для клиентского приложения), поэтому я предположил, что мне потребуется создать собственный токен. Я посмотрел документацию по 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)

Я не уверен, что мне не хватает, но я не могу обернуть голову тем, что мне нужно сделать, чтобы сделать эту работу должным образом. Прямо сейчас я использую сваггер, чтобы проверить его, и он не работает ни в каком виде, форме или форме. Нет параметров для ввода, и даже использование cURL через терминал не дает ожидаемых результатов.

Для записи я использую Django 2.1 и Django Rest framework 3.8.2.

Буду признателен за любую помощь, а также за любые дальнейшие комментарии об этом коде (я все еще учусь в конце концов). Я предполагаю, что мне не хватает методов, но я не уверен, где.

1 Ответ

0 голосов
/ 28 августа 2018

Я иду шаг за шагом.

Во-первых, вам нужно определить сериализатор для координат, потому что стандартный способ получения и проверки пользовательского ввода - через сериализаторы. так:

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

так что установка минимального и максимального значения для полей делает достаточно проверки, я думаю. если вам нужна дополнительная проверка этих полей, вы можете использовать проверка на уровне полей .

секунду, используйте 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')

на следующем шаге вы должны сделать key и created read_only , потому что вы не хотите получать их значения из пользовательского ввода, вы только хотите показать их значение пользователь после создания экземпляра AuthToken. так что добавьте их к read_only_fields в классе Meta.

на следующем шаге вы должны переопределить метод сериализатора 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)
...