Django PasswordResetConfirmView 密码重置失败不起作用

Django PasswordResetConfirmView password reset failed not working

当使用 Django3.0 内部 PasswordResetConfirmView PasswordResetForm reset_password.html 并篡改生成的令牌时,会引发表单 "is_bound" 错误。它不会重定向到某些失败 url.

Internal Server Error: /account/password/reset/MQ/akbdyj-e5f18868fas35e748160dd6ef006803a5/
Traceback (most recent call last):
  File "C:\Users\JoyStick\.virtualenvs\django-template-2StZ7f6a\lib\site-packages\django\core\handlers\exception.py", line 47, in inner
    response = get_response(request)
  File "C:\Users\JoyStick\.virtualenvs\django-template-2StZ7f6a\lib\site-packages\django\core\handlers\base.py", line 204, in _get_response
    response = response.render()
  File "C:\Users\JoyStick\.virtualenvs\django-template-2StZ7f6a\lib\site-packages\django\template\response.py", line 108, in render
    self.content = self.rendered_content
  File "C:\Users\JoyStick\.virtualenvs\django-template-2StZ7f6a\lib\site-packages\django\template\response.py", line 86, in rendered_content
    return template.render(context, self._request)
  File "C:\Users\JoyStick\.virtualenvs\django-template-2StZ7f6a\lib\site-packages\django\template\backends\django.py", line 61, in render
    return self.template.render(context)
  File "C:\Users\JoyStick\.virtualenvs\django-template-2StZ7f6a\lib\site-packages\django\template\base.py", line 170, in render
    return self._render(context)
  File "C:\Users\JoyStick\.virtualenvs\django-template-2StZ7f6a\lib\site-packages\django\template\base.py", line 162, in _render
    return self.nodelist.render(context)
  File "C:\Users\JoyStick\.virtualenvs\django-template-2StZ7f6a\lib\site-packages\django\template\base.py", line 938, in render
    bit = node.render_annotated(context)
  File "C:\Users\JoyStick\.virtualenvs\django-template-2StZ7f6a\lib\site-packages\django\template\base.py", line 905, in render_annotated
    return self.render(context)
  File "C:\Users\JoyStick\.virtualenvs\django-template-2StZ7f6a\lib\site-packages\django\template\loader_tags.py", line 150, in render
    return compiled_parent._render(context)
  File "C:\Users\JoyStick\.virtualenvs\django-template-2StZ7f6a\lib\site-packages\django\template\base.py", line 162, in _render
    return self.nodelist.render(context)
  File "C:\Users\JoyStick\.virtualenvs\django-template-2StZ7f6a\lib\site-packages\django\template\base.py", line 938, in render
    bit = node.render_annotated(context)
  File "C:\Users\JoyStick\.virtualenvs\django-template-2StZ7f6a\lib\site-packages\django\template\base.py", line 905, in render_annotated
    return self.render(context)
  File "C:\Users\JoyStick\.virtualenvs\django-template-2StZ7f6a\lib\site-packages\django\template\loader_tags.py", line 62, in render
    result = block.nodelist.render(context)
  File "C:\Users\JoyStick\.virtualenvs\django-template-2StZ7f6a\lib\site-packages\django\template\base.py", line 938, in render
    bit = node.render_annotated(context)
  File "C:\Users\JoyStick\.virtualenvs\django-template-2StZ7f6a\lib\site-packages\django\template\base.py", line 905, in render_annotated
    return self.render(context)
  File "C:\Users\JoyStick\.virtualenvs\django-template-2StZ7f6a\lib\site-packages\crispy_forms\templatetags\crispy_forms_tags.py", line 203, in render
    c = self.get_render(context).flatten()
  File "C:\Users\JoyStick\.virtualenvs\django-template-2StZ7f6a\lib\site-packages\crispy_forms\templatetags\crispy_forms_tags.py", line 112, in get_render
    node_context.update({"is_bound": actual_form.is_bound})
AttributeError: 'NoneType' object has no attribute 'is_bound'

我试过在“extra_context”中传递失败模板名称,但仍然无效。

class UserAccountResetPasswordView(PasswordResetConfirmView):
    template_name = 'user_account/profile/reset_password.html'
    form_class = UserAccountPasswordResetForm
    success_url = reverse_lazy('user_account:login')
    extra_context = {
        'template_name': 'user_account/profile/reset_password_failed.html'
    }

表格

class UserAccountPasswordResetForm(SetPasswordForm):
    new_password1 = forms.CharField(
        label=_("New password"),
        strip=False,
        widget=forms.PasswordInput(
            attrs={
                'class': 'form-control',
                'id': 'toggle-password-type',
                'placeholder': 'New Password'
            }),
    )
    new_password2 = forms.CharField(
        label=_("Confirm new password"),
        strip=False,
        widget=forms.PasswordInput(
            attrs={
                'class': 'form-control',
                'placeholder': 'Confirm New Password'
            }),
    )

    class Meta:
        fields = ['new_password1', 'new_password2']

    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        self.helper = FormHelper(self)
        self.helper.form_show_labels = False
        self.helper.form_tag = False
        self.helper.layout = Layout(
            Field(AppendedText('new_password1',
                  '<i onclick="passwordTypeToggle()" class="fa fa-eye"></i>')),
            'new_password2',
        )

    def clean_new_password1(self):
        new_password1 = self.cleaned_data.get("new_password1")
        if not re.search(
                "^(?=.*[A-Z])(?=.*[a-z])(?=.*\d)[A-Za-z\d@!#$%&*()_+-= ]{8,}$",
                new_password1
        ):
            raise ValidationError(
                'Password should have 8 to 15 characters which contain only characters, numeric digits, underscore and first character must be a letter', code='signup'
            )
        return new_password1

reset_password.html

{% extends 'base.html' %}
{% load static %}
{% load crispy_forms_tags %}

{% block content %}
<div class="content" id="container-fluid">
    <div class="card shadow-sm rounded" style="max-width: 500px; margin: auto;">
        <div class="card-body">
            <h1>Reset Password</h1>
            <hr>
            <form method="post">
                {% crispy form %}
                <div class="row">
                    <div class="col-12 text-right">
                        <button type="submit" class="btn btn-primary">Confirm</button>
                    </div>
                </div>
            </form>
        </div>
    </div>
</div>
{% endblock content %}

extra_context 中传递模板名称没有任何作用。如果令牌无效,则 PasswordResetConfirmViewform 设置为 None 并将其传递给上下文。您尝试呈现该表单时出错。相反,您应该检查上下文中传递的变量 validlink 以指示 link 是否正确并正确呈现:

{% extends 'base.html' %}
{% load static %}
{% load crispy_forms_tags %}

{% block content %}
{% if validlink %}
    <div class="content" id="container-fluid">
        <div class="card shadow-sm rounded" style="max-width: 500px; margin: auto;">
            <div class="card-body">
                <h1>Reset Password</h1>
                <hr>
                <form method="post">
                    {% crispy form %}
                    <div class="row">
                        <div class="col-12 text-right">
                            <button type="submit" class="btn btn-primary">Confirm</button>
                        </div>
                    </div>
                </form>
            </div>
        </div>
    </div>
{% else %}
    <!--Render here as per your preferences to indicate the link is invalid-->
    Looks like you clicked on an invalid password reset link. Please try again.
{% endif %}
{% endblock content %}