使用自定义 AuthenticationForm 覆盖 Django LoginView 错误消息

Override Django LoginView Error Message With Custom AuthenticationForm

我有一个基于 class 的观点认为 class 是 LoginView.

from django.contrib.auth.views import LoginView

class CustomLoginView(LoginView):

def get_success_url(self):
    url = self.get_redirect_url()
    return url or reverse_lazy('knowledgebase:user_home', kwargs={
        'username':self.request.user.username,
    })

如果用户的电子邮件尚未激活,我想覆盖错误消息,因为他们必须单击发送到他们电子邮件地址的 link。当前的默认消息如下所示:

而不是说:

Please enter a correct email address and password. Note that both fields may be case-sensitive.

我想说的大意是:

Please confirm your email so you can log in.

我试过了:

accounts/forms.py

from django.contrib.auth.forms import AuthenticationForm
from django.utils.translation import gettext as _

class PickyAuthenticationForm(AuthenticationForm):
    def confirm_login_allowed(self, user):
        if not user.is_active:
            raise forms.ValidationError(
                _("Please confirm your email so you can log in."),
                code='inactive',
            )

accounts/views.py

class CustomLoginView(LoginView): # 1. <--- note: this is a class-based view

    form_class = PickyAuthenticationForm # 2. <--- note: define form here?

    def get_success_url(self):
        url = self.get_redirect_url()
        return url or reverse_lazy('knowledgebase:user_home', kwargs={
            'username':self.request.user.username,
        })

当我尝试使用 确实 存在但尚未验证其电子邮件地址的用户登录时,结果绝对无效。

AuthenticationForm docs.

您需要使用AllowAllUsersModelBackend

https://docs.djangoproject.com/en/3.0/ref/contrib/auth/#django.contrib.auth.backends.AllowAllUsersModelBackend

在这里您将获得设置自定义后端的说明

https://docs.djangoproject.com/en/3.0/topics/auth/customizing/#specifying-authentication-backends

希望对您有所帮助。

当我只想覆盖消息时,我不相信设置自定义后端是解决方案。我通过定义 form_invalid 进行了临时修复。是的,它很老套,但就目前而言,它可以解决问题。怀疑这对任何人都有帮助,但发现 form.errors 很有趣。也许有人可以以此为基础来解决他们的具体问题。

def form_invalid(self, form):
    """If the form is invalid, render the invalid form."""
    #TODO: This is EXTREMELY HACKY!
    if form.errors:
        email = form.cleaned_data.get('username')
        if User.objects.filter(email=email, username=None).exists():
            if len(form.errors['__all__']) == 1:
                form.errors['__all__'][0] = 'Please confirm your email to log in.'
    return self.render_to_response(self.get_context_data(form=form))

方法 - 1

Django 使用 ModelBackend as default AUTHENTICATION_BACKENDS 并且不对非活动用户进行身份验证。

Authorization for inactive users 部分中也有说明,

An inactive user is one that has its is_active field set to False. The ModelBackend and RemoteUserBackend authentication backends prohibits these users from authenticating. If a custom user model doesn’t have an is_active field, all users will be allowed to authenticate.

所以,在settings.py[=32=中将AllowAllUsersModelBackend设为你的AUTHENTICATION_BACKENDS ]

# settings.py

AUTHENTICATION_BACKENDS = ['django.contrib.auth.backends.AllowAllUsersModelBackend']

它对我的 Django 应用有多大影响?

除了身份验证,它不会影响任何其他内容。如果我们查看 source code of AllowAllUsersModelBackend class,我们可以看到它只允许 inactive 用户进行身份验证。


方法 - 2

我个人不推荐这种方法,因为 method-1 是 Django 解决这个问题的方法。

覆盖PickyAuthenticationFormclass的clean(...)方法并调用AllowAllUsersModelBackend 后端为,

from django.contrib.auth.backends import AllowAllUsersModelBackend


class PickyAuthenticationForm(AuthenticationForm):
    <b>def clean(self):
        username = self.cleaned_data.get('username')
        password = self.cleaned_data.get('password')

        if username is not None and password:
            backend = AllowAllUsersModelBackend()
            self.user_cache = backend.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</b>

    def confirm_login_allowed(self, user):
        if not user.is_active:
            raise forms.ValidationError(
                "Please confirm your email so you can log in.",
                code='inactive',
            )

结果截图