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

时间:2020-04-28 00:42:43

标签: django forms django-authentication loginview

我有一个基于类的视图,该视图将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,
    })

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

override LoginView error message

与其说:

请输入正确的电子邮件地址和密码。注意两者 字段可能区分大小写。

我想说些什么:

请确认您的电子邮件,以便您登录。<​​/ p>

我尝试过:

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

3 个答案:

答案 0 :(得分:2)

答案 1 :(得分:2)

方法-1

Django使用ModelBackend作为默认AUTHENTICATION_BACKENDS,并且不对不活动的用户进行身份验证。

Authorization for inactive users部分中也对此进行了说明,

非活动用户是指其is_active字段设置为False的用户。的 ModelBackendRemoteUserBackend身份验证后端禁止 这些用户无法进行身份验证。如果自定义用户模型没有 is_active字段,将允许所有用户进行身份验证。

因此,在 AUTHENTICATION_BACKENDS

中将AllowAllUsersModelBackend设置为您的 settings.py
# settings.py

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

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

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


方法-2

我个人不建议使用此方法,因为 method-1 是解决此问题的Django方法。

覆盖 clean(...) 类的 PickyAuthenticationForm 方法,并以如下方式调用 AllowAllUsersModelBackend 后端,

from django.contrib.auth.backends import AllowAllUsersModelBackend


class PickyAuthenticationForm(AuthenticationForm):
    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

    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',
            )

结果屏幕截图

Screenshot

答案 2 :(得分:0)

当我只是想覆盖一条消息时,我不认为设置自定义后端是解决方案。我通过定义form_invalid进行了临时修复。是的,它很hacky,但现在就可以解决问题。怀疑这将帮助任何人,但发现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))