登录过程中带有 (user.is_active =False) 标志的用户的消息
Messages for users with (user.is_active =False) flag during the login process
我正在尝试在登录过程中为拥有帐户但已停用的用户添加消息,如果他想进入,他必须激活它。
我使用 LoginView 控制器,它使用称为 AuthenticationForm 的内置标准表单
AuthenticationForm 有以下方法:
def confirm_login_allowed(self, user):
"""
Controls whether the given User may log in. This is a policy setting,
independent of end-user authentication. This default behavior is to
allow login by active users, and reject login by inactive users.
If the given user cannot log in, this method should raise a
``forms.ValidationError``.
If the given user may log in, this method should return None.
"""
if not user.is_active:
raise forms.ValidationError(
self.error_messages['inactive'],
code='inactive',
# and list of error messages within this class
error_messages = {
'invalid_login': _(
"Please enter a correct %(username)s and password. Note that both "
"fields may be case-sensitive."
),
'inactive': _("This account is inactive."),
}
所以从技术上讲,如果不是 user.is_active – 它应该显示消息 'inactive' 但在我的情况下,对于 is_active = False DB table 的未激活用户,它会显示消息'invalid_login' 代替。
我正在尝试 100% 正确的登录名和密码,但用户未激活,但它显示 'invalid_login' 消息。然后我只需将 DB 中的 is_active 标志切换为 True,它让我轻松进入。
你知道为什么会这样吗?
最终目标是向拥有帐户但已停用的用户显示此消息“'inactive': _("This account is inactive.")”。 (或自定义消息)
从技术上讲,它应该可以工作,但事实并非如此。
在此先感谢您,如果您发现这个问题很初级或很愚蠢,我们深表歉意。
尝试过:
class AuthCustomForm(AuthenticationForm):
def clean(self):
AuthenticationForm.clean(self)
user = ExtraUser.objects.get(username=self.cleaned_data.get('username'))
if not user.is_active and user:
messages.warning(self.request, 'Please Activate your account',
extra_tags="", fail_silently=True)
# return HttpResponseRedirect(' your url'))
最终有什么帮助:
class AuthCustomForm(AuthenticationForm):
def get_invalid_login_error(self):
user = ExtraUser.objects.get(username=self.cleaned_data.get('username'))
if not user.is_active and user:
raise forms.ValidationError(
self.error_messages['inactive'],
code='inactive',)
else:
return forms.ValidationError(
self.error_messages['invalid_login'],
code='invalid_login',
params={'username': self.username_field.verbose_name},
)
这是一种奇怪的方法,因为 DJANGO 内置代码应该可以工作。我不确定我是否没有修正自己之前在这里犯的错误。也许我让事情变得更糟了。
你可以试试这个。
user = authenticate(username=username, password=password)
if user and user.is_active==False:
messages.warning(request, 'Please Activate your account', extra_tags="")
return HttpResponseRedirect(' your url'))
class AuthCustomForm(AuthenticationForm):
def get_invalid_login_error(self):
user = ExtraUser.objects.get(username=self.cleaned_data.get('username'))
if not user.is_active and user:
raise forms.ValidationError(
self.error_messages['inactive'],
code='inactive',)
else:
return forms.ValidationError(
self.error_messages['invalid_login'],
code='invalid_login',
params={'username': self.username_field.verbose_name}, )
这是一个很长的答案,但希望它会有所帮助,并提供一些关于幕后工作方式的见解。
要了解为什么 'inactive'
ValidationError
没有为不活跃的用户引发,我们必须首先查看 LoginView
的实现方式,特别是它的 post
方法。
def post(self, request, *args, **kwargs):
"""
Handle POST requests: instantiate a form instance with the passed
POST variables and then check if it's valid.
"""
form = self.get_form()
if form.is_valid():
return self.form_valid(form)
else:
return self.form_invalid(form)
当 LoginView
收到包含表单数据的 POST
请求时调用此方法。 get_form
使用来自请求的 POST
数据填充 AuthenticationForm
,然后检查表单,return 根据它是否有效给出不同的响应。我们关心的是表单检查,所以让我们深入研究一下 is_valid
方法在做什么。
Django docs 很好地解释了表单和字段验证的工作原理,因此我不会详细介绍。基本上,我们需要知道的是,当调用表单的 is_valid
方法时,表单首先单独验证其所有字段,然后调用其 clean
方法进行任何表单范围的验证.
这里是我们需要查看 AuthenticationForm
是如何实现的,因为它定义了自己的 clean
方法。
def clean(self):
username = self.cleaned_data.get('username')
password = self.cleaned_data.get('password')
if username is not None and password:
self.user_cache = authenticate(self.request, username=username, password=password)
if self.user_cache is None:
raise self.get_invalid_login_error()
else:
self.confirm_login_allowed(self.user_cache)
return self.cleaned_data
这就是您确定的 confirm_login_allowed
方法发挥作用的地方。我们看到用户名和密码被传递给 authenticate
函数。这会根据 AUTHENTICATION_BACKENDS
设置定义的所有身份验证后端检查给定的凭据(有关更多信息,请参阅 Django docs),如果成功,return 将验证用户的 User
模型None
如果没有。
然后检查authenticate
的结果。如果它是 None
,则无法对用户进行身份验证,并且会按预期引发 'invalid_login'
ValidationError
。如果不是,则用户已通过身份验证,如果用户处于非活动状态,confirm_login_allowed
会引发 'inactive'
ValidationError
。
那么为什么 'inactive'
ValidationError
没有被提升?
因为非活动用户认证失败,所以authenticate
returns None
,这意味着get_invalid_login_error
被调用而不是confirm_login_allowed
。
为什么非活动用户无法验证?
为了看到这一点,我假设您没有使用自定义身份验证后端,这意味着您的 AUTHENTICATION_BACKENDS
设置设置为默认值:['django.contrib.auth.backends.ModelBackend']
。这意味着 ModelBackend
是唯一使用的身份验证后端,我们可以查看它的 authenticate
方法,这是之前看到的 authenticate
函数在内部调用的方法。
def authenticate(self, request, username=None, password=None, **kwargs):
if username is None:
username = kwargs.get(UserModel.USERNAME_FIELD)
if username is None or password is None:
return
try:
user = UserModel._default_manager.get_by_natural_key(username)
except UserModel.DoesNotExist:
# Run the default password hasher once to reduce the timing
# difference between an existing and a nonexistent user (#20760).
UserModel().set_password(password)
else:
if user.check_password(password) and self.user_can_authenticate(user):
return user
我们对最后的 if
声明感兴趣。
if user.check_password(password) and self.user_can_authenticate(user):
return user
对于我们的非活跃用户,我们知道密码是正确的,所以check_password
会returnTrue
。这意味着它必须是 user_can_authenticate
方法 returning False
并导致非活动用户未通过身份验证。等一下,因为我们快到了...
def user_can_authenticate(self, user):
"""
Reject users with is_active=False. Custom user models that don't have
that attribute are allowed.
"""
is_active = getattr(user, 'is_active', None)
return is_active or is_active is None
Aha! user_can_authenticate
returns False
如果 user.is_active
是 False
这会导致用户未验证。
解决方法
我们可以继承 ModelBackend
,覆盖 user_can_authenticate
,并将 AUTHENTICATION_BACKENDS
设置指向这个新的子类。
app/backends.py
from django.contrib.auth import backends
class CustomModelBackend(backends.ModelBackend):
def user_can_authenticate(self, user):
return True
settings.py
AUTHENTICATION_BACKENDS = [
'app.backends.CustomModelBackend',
]
我认为这个解决方案比改变 get_invalid_login_error
.
的逻辑更干净
然后您可以通过继承 AuthenticationForm
、覆盖 error_messages
并设置 [=20] 的 authentication_form
属性来覆盖 'inactive'
ValidationError
消息=] 到这个新的子类。
from django.contrib.auth import forms as auth_forms, views as auth_views
from django.utils.translation import gettext_lazy as _
class CustomAuthenticationForm(auth_forms.AuthenticationForm):
error_messages = {
'invalid_login': _(
"Please enter a correct %(username)s and password. Note that both "
"fields may be case-sensitive."
),
'inactive': _("CUSTOM INACTIVE MESSAGE."),
}
class LoginView(auth_views.LoginView):
authentication_form = CustomAuthenticationForm
我正在尝试在登录过程中为拥有帐户但已停用的用户添加消息,如果他想进入,他必须激活它。
我使用 LoginView 控制器,它使用称为 AuthenticationForm 的内置标准表单
AuthenticationForm 有以下方法:
def confirm_login_allowed(self, user):
"""
Controls whether the given User may log in. This is a policy setting,
independent of end-user authentication. This default behavior is to
allow login by active users, and reject login by inactive users.
If the given user cannot log in, this method should raise a
``forms.ValidationError``.
If the given user may log in, this method should return None.
"""
if not user.is_active:
raise forms.ValidationError(
self.error_messages['inactive'],
code='inactive',
# and list of error messages within this class
error_messages = {
'invalid_login': _(
"Please enter a correct %(username)s and password. Note that both "
"fields may be case-sensitive."
),
'inactive': _("This account is inactive."),
}
所以从技术上讲,如果不是 user.is_active – 它应该显示消息 'inactive' 但在我的情况下,对于 is_active = False DB table 的未激活用户,它会显示消息'invalid_login' 代替。 我正在尝试 100% 正确的登录名和密码,但用户未激活,但它显示 'invalid_login' 消息。然后我只需将 DB 中的 is_active 标志切换为 True,它让我轻松进入。 你知道为什么会这样吗?
最终目标是向拥有帐户但已停用的用户显示此消息“'inactive': _("This account is inactive.")”。 (或自定义消息) 从技术上讲,它应该可以工作,但事实并非如此。 在此先感谢您,如果您发现这个问题很初级或很愚蠢,我们深表歉意。
尝试过:
class AuthCustomForm(AuthenticationForm):
def clean(self):
AuthenticationForm.clean(self)
user = ExtraUser.objects.get(username=self.cleaned_data.get('username'))
if not user.is_active and user:
messages.warning(self.request, 'Please Activate your account',
extra_tags="", fail_silently=True)
# return HttpResponseRedirect(' your url'))
最终有什么帮助:
class AuthCustomForm(AuthenticationForm):
def get_invalid_login_error(self):
user = ExtraUser.objects.get(username=self.cleaned_data.get('username'))
if not user.is_active and user:
raise forms.ValidationError(
self.error_messages['inactive'],
code='inactive',)
else:
return forms.ValidationError(
self.error_messages['invalid_login'],
code='invalid_login',
params={'username': self.username_field.verbose_name},
)
这是一种奇怪的方法,因为 DJANGO 内置代码应该可以工作。我不确定我是否没有修正自己之前在这里犯的错误。也许我让事情变得更糟了。
你可以试试这个。
user = authenticate(username=username, password=password)
if user and user.is_active==False:
messages.warning(request, 'Please Activate your account', extra_tags="")
return HttpResponseRedirect(' your url'))
class AuthCustomForm(AuthenticationForm):
def get_invalid_login_error(self):
user = ExtraUser.objects.get(username=self.cleaned_data.get('username'))
if not user.is_active and user:
raise forms.ValidationError(
self.error_messages['inactive'],
code='inactive',)
else:
return forms.ValidationError(
self.error_messages['invalid_login'],
code='invalid_login',
params={'username': self.username_field.verbose_name}, )
这是一个很长的答案,但希望它会有所帮助,并提供一些关于幕后工作方式的见解。
要了解为什么 'inactive'
ValidationError
没有为不活跃的用户引发,我们必须首先查看 LoginView
的实现方式,特别是它的 post
方法。
def post(self, request, *args, **kwargs):
"""
Handle POST requests: instantiate a form instance with the passed
POST variables and then check if it's valid.
"""
form = self.get_form()
if form.is_valid():
return self.form_valid(form)
else:
return self.form_invalid(form)
当 LoginView
收到包含表单数据的 POST
请求时调用此方法。 get_form
使用来自请求的 POST
数据填充 AuthenticationForm
,然后检查表单,return 根据它是否有效给出不同的响应。我们关心的是表单检查,所以让我们深入研究一下 is_valid
方法在做什么。
Django docs 很好地解释了表单和字段验证的工作原理,因此我不会详细介绍。基本上,我们需要知道的是,当调用表单的 is_valid
方法时,表单首先单独验证其所有字段,然后调用其 clean
方法进行任何表单范围的验证.
这里是我们需要查看 AuthenticationForm
是如何实现的,因为它定义了自己的 clean
方法。
def clean(self):
username = self.cleaned_data.get('username')
password = self.cleaned_data.get('password')
if username is not None and password:
self.user_cache = authenticate(self.request, username=username, password=password)
if self.user_cache is None:
raise self.get_invalid_login_error()
else:
self.confirm_login_allowed(self.user_cache)
return self.cleaned_data
这就是您确定的 confirm_login_allowed
方法发挥作用的地方。我们看到用户名和密码被传递给 authenticate
函数。这会根据 AUTHENTICATION_BACKENDS
设置定义的所有身份验证后端检查给定的凭据(有关更多信息,请参阅 Django docs),如果成功,return 将验证用户的 User
模型None
如果没有。
然后检查authenticate
的结果。如果它是 None
,则无法对用户进行身份验证,并且会按预期引发 'invalid_login'
ValidationError
。如果不是,则用户已通过身份验证,如果用户处于非活动状态,confirm_login_allowed
会引发 'inactive'
ValidationError
。
那么为什么 'inactive'
ValidationError
没有被提升?
因为非活动用户认证失败,所以authenticate
returns None
,这意味着get_invalid_login_error
被调用而不是confirm_login_allowed
。
为什么非活动用户无法验证?
为了看到这一点,我假设您没有使用自定义身份验证后端,这意味着您的 AUTHENTICATION_BACKENDS
设置设置为默认值:['django.contrib.auth.backends.ModelBackend']
。这意味着 ModelBackend
是唯一使用的身份验证后端,我们可以查看它的 authenticate
方法,这是之前看到的 authenticate
函数在内部调用的方法。
def authenticate(self, request, username=None, password=None, **kwargs):
if username is None:
username = kwargs.get(UserModel.USERNAME_FIELD)
if username is None or password is None:
return
try:
user = UserModel._default_manager.get_by_natural_key(username)
except UserModel.DoesNotExist:
# Run the default password hasher once to reduce the timing
# difference between an existing and a nonexistent user (#20760).
UserModel().set_password(password)
else:
if user.check_password(password) and self.user_can_authenticate(user):
return user
我们对最后的 if
声明感兴趣。
if user.check_password(password) and self.user_can_authenticate(user):
return user
对于我们的非活跃用户,我们知道密码是正确的,所以check_password
会returnTrue
。这意味着它必须是 user_can_authenticate
方法 returning False
并导致非活动用户未通过身份验证。等一下,因为我们快到了...
def user_can_authenticate(self, user):
"""
Reject users with is_active=False. Custom user models that don't have
that attribute are allowed.
"""
is_active = getattr(user, 'is_active', None)
return is_active or is_active is None
Aha! user_can_authenticate
returns False
如果 user.is_active
是 False
这会导致用户未验证。
解决方法
我们可以继承 ModelBackend
,覆盖 user_can_authenticate
,并将 AUTHENTICATION_BACKENDS
设置指向这个新的子类。
app/backends.py
from django.contrib.auth import backends
class CustomModelBackend(backends.ModelBackend):
def user_can_authenticate(self, user):
return True
settings.py
AUTHENTICATION_BACKENDS = [
'app.backends.CustomModelBackend',
]
我认为这个解决方案比改变 get_invalid_login_error
.
然后您可以通过继承 AuthenticationForm
、覆盖 error_messages
并设置 [=20] 的 authentication_form
属性来覆盖 'inactive'
ValidationError
消息=] 到这个新的子类。
from django.contrib.auth import forms as auth_forms, views as auth_views
from django.utils.translation import gettext_lazy as _
class CustomAuthenticationForm(auth_forms.AuthenticationForm):
error_messages = {
'invalid_login': _(
"Please enter a correct %(username)s and password. Note that both "
"fields may be case-sensitive."
),
'inactive': _("CUSTOM INACTIVE MESSAGE."),
}
class LoginView(auth_views.LoginView):
authentication_form = CustomAuthenticationForm