#python #django #django-rest-framework
#python #django #django-rest-framework
Вопрос:
Я пытаюсь реализовать активацию учетной записи по ссылке электронной почты. Моя User
модель очень проста, наследуется от django.contrib.auth.models.AbstractUser
, поэтому по умолчанию в ней есть is_active
поле. После регистрации создается новый пользователь с is_active=False
параметром, и я хочу обработать случай, когда пользователь пытается войти в систему, и даже если учетные данные в порядке, не следует входить в систему, потому что учетная запись не активирована. Я использую аутентификацию токеном Knox. Мой сериализатор:
from django.contrib.auth import authenticate
from rest_framework import serializers, exceptions
class LoginUserSerializer(serializers.ModelSerializer):
class Meta:
model = UserModel
fields = ('username', 'password')
def validate(self, data):
user = authenticate(**data)
if user:
if user.is_active:
return user
raise exceptions.AuthenticationFailed('Account is not activated')
raise exceptions.AuthenticationFailed()
И просмотрите:
from django.contrib.auth import login
from rest_framework.permissions import AllowAny
from rest_framework.authtoken.serializers import AuthTokenSerializer
from knox.views import LoginView
from .serializers import LoginUserSerializer
class LoginUserView(LoginView):
serializer_class = LoginUserSerializer
permission_classes = [AllowAny]
def post(self, request, *args, **kwargs):
serializer = AuthTokenSerializer(data=request.data)
serializer.is_valid(raise_exception=True)
user = serializer.validated_data['user']
login(request, user)
return super(LoginUserView, self).post(request)
И с этим кодом я наткнулся на проблему: когда я пытаюсь войти в систему с уже активированной учетной записью, все выглядит нормально, но когда я пытаюсь использовать неактивированную, вместо Account is not activated
я получаю:
{
"non_field_errors": [
"Unable to log in with provided credentials."
]
}
Я думаю, это происходит скорее из view, чем из сериализатора.
Комментарии:
1. Django не может не авторизовать неактивную учетную запись. Вот почему вы получаете эту ошибку
2. Вы правы, спасибо! Я добавил
AUTHENTICATION_BACKENDS = [ 'django.contrib.auth.backends.AllowAllUsersModelBackend']
вsettings.py
, и теперь я могу войти в систему с неактивной учетной записью. Но теперь я столкнулся с другой проблемой — это каким-то образом пропускает проверки вvalidate()
в serializer, что означает, что даже если учетная запись неактивна, но вошла в систему, она проходитuser.is_active
проверку, и исключения, определенные мной, вообще не создаются (в обоих случаях, если пользователь и если ошибка user.is_active). Что еще более странно, я заменил исключения наreturn status.HTTP_401_UNAUTHORIZED
и по-прежнему ничего.3. Попробуйте выполнить отладку. Достигнут ли ваш запрос на данный момент
user = authenticate(**data)
?
Ответ №1:
Итак, благодаря предложению Шафикура Рахмана я смог заставить это работать. После того, как я попытался отладить это с помощью pdb
и установить trace внутри LoginUserSerializer
, но ничего не произошло, я понял, что в своих представлениях я указываю не на сериализатор, который я написал, а на AuthTokenSerializer
. Даже после этого это все еще не сработало из-за моего непонимания того, как работают django login()
и DRF validate()
. Ниже исправлен код для справки:
Вид:
class LoginUserView(LoginView):
serializer_class = LoginUserSerializer
permission_classes = [AllowAny]
def post(self, request, *args, **kwargs):
serializer = LoginUserSerializer(data=request.data) # changed to desired serializer
serializer.is_valid(raise_exception=True)
user = serializer.validated_data['user']
login(request, user)
return super(LoginUserView, self).post(request)
и сериализатор:
class LoginUserSerializer(serializers.ModelSerializer):
username = serializers.CharField() # added missing fields for serializer
password = serializers.CharField()
class Meta:
model = UserModel
fields = ('username', 'password')
def validate(self, data):
user = authenticate(**data)
if user:
if user.is_active:
data['user'] = user # added user model to OrderedDict that serializer is validating
return data # and in sunny day scenario, return this dict, as everything is fine
raise exceptions.AuthenticationFailed('Account is not activated')
raise exceptions.AuthenticationFailed()
Кроме того, чтобы иметь возможность authenticate()
неактивному пользователю, мне пришлось добавить
AUTHENTICATION_BACKENDS = [
'django.contrib.auth.backends.AllowAllUsersModelBackend'
]
в настройках проекта.