如何使Django密码重置邮件美化为HTML?

19

我的Django应用目前使用本地注册包来处理用户认证和管理。

我在 myApp/templates/registration 中创建了一些文件,用于发送密码重置令牌,如这些文档所述。

它运行良好。但是密码重置电子邮件只是一个丑陋的纯文本怪物。我希望它与应用程序发送的所有其他电子邮件的外观和感觉相匹配。也就是说:我希望它是HTML格式的,并包含图像、样式和链接。我该怎么做?

我按照这里的详细说明操作。但是,那段代码中有一个错误,我不知道该怎么办: 'CustomPasswordResetForm' object has no attribute 'users_cache'

有人可以向我展示一个详细的工作示例如何实现这一点吗?我希望它不要太难。

11个回答

15

针对django 2.0版本,我想要分享我的发现,因为我发现这个问题的其它答案已经过时了。

在2.0版本中,将URL添加到您的urls.py文件的正确方法是使用path()函数:

from django.urls import path
from django.contrib.auth import views as auth_views

path('accounts/password_reset/', auth_views.PasswordResetView.as_view(
  html_email_template_name='registration/password_reset_html_email.html'
)),
下一个需要强调的代码片段是.as_view()函数。Django 2.0使用类实现auth视图,你可以在Authentication Views documentation阅读更多相关信息。
然后,你可以使用`.as_view()`将类转换为视图,并将源代码中定义的任何类属性作为命名参数传递。
默认情况下为None的html_email_template_name参数将自动发送HTML邮件。
你可以按照python路径django.contrib.auth.views查看PasswordResetView的源代码。
在此处,你可以查看可以传递给PasswordResetView和其他auth视图的其他类属性。这对于将extra_context传递到django模板中非常有帮助。

11
[cross-posted from Does Django password_reset support html email templates? ]
在试错了一段时间后,我发现在Django的最新版本(1.8)中提供自定义模板化密码重置电子邮件的方法更加简洁。在您的project/urls.py文件中,添加以下导入:
from django.contrib.auth import views as auth_views
from django.core.urlresolvers import reverse_lazy

在通常的Django贡献认证URL路由包含之前,在您的urlpatterns中添加以下路由:
url(r'^accounts/password/reset/$',
  auth_views.password_reset,
  {
    'post_reset_redirect': reverse_lazy('auth_password_reset_done'),
    'html_email_template_name': 'registration/password_reset_html_email.html'
  },
  name='auth_password_reset'),


url('^', include('django.contrib.auth.urls')),

然后,在你的应用程序的templates/registration文件夹中,创建password_reset_html_email.html,并使用任何你想要的HTML模板。

这样做的原因在于django/contrib/auth/views.py的源代码中,它具有原始URL路由映射到的视图函数:

147 def password_reset(request, is_admin_site=False,
148                    template_name='registration/password_reset_form.html',
149                    email_template_name='registration/password_reset_email.html',
150                    subject_template_name='registration/password_reset_subject.txt',
151                    password_reset_form=PasswordResetForm,
152                    token_generator=default_token_generator,
153                    post_reset_redirect=None,
154                    from_email=None,
155                    current_app=None,
156                    extra_context=None,
157                    html_email_template_name=None):
158

"html_email_template_name的默认值为None,似乎没有办法分配它的值,除了像我上面提到的那样为这种情况重写特定路由之外。希望这可以帮助,而不需要像其他答案建议的那样复制粘贴大量几乎相同的代码 - 当然欢迎反馈!"

这对我在Django 1.11上有效。url函数允许kwargs。这是文档:https://docs.djangoproject.com/en/1.11/ref/urls/#url - Fermin Arellano

10

Django认证的默认辅助视图不能发送多部分(HTML)电子邮件,因为底层的send_mail方法暂时不支持HTML电子邮件。

这将在下一个版本中通过添加html_message标志来解决。

最简单的解决方法是创建自己的自定义密码重置表单,并使用EmailMultiAlternatives发送邮件,从而允许您的HTML在电子邮件客户端中正确呈现。

您可以使用现有的表单并进行更改:

class HTMLPasswordResetForm(forms.Form):
    email = forms.EmailField(label=_("Email"), max_length=254)

    def save(self, domain_override=None,
             subject_template_name='registration/password_reset_subject.txt',
             email_template_name='registration/password_reset_email.html',
             use_https=False, token_generator=default_token_generator,
             from_email=None, request=None):
        """
        Generates a one-use only link for resetting password and sends to the
        user.
        """
        # from django.core.mail import send_mail
        from django.core.mail import EmailMultiAlternatives
        UserModel = get_user_model()
        email = self.cleaned_data["email"]
        active_users = UserModel._default_manager.filter(
            email__iexact=email, is_active=True)
        for user in active_users:
            # Make sure that no email is sent to a user that actually has
            # a password marked as unusable
            if not user.has_usable_password():
                continue
            if not domain_override:
                current_site = get_current_site(request)
                site_name = current_site.name
                domain = current_site.domain
            else:
                site_name = domain = domain_override
            c = {
                'email': user.email,
                'domain': domain,
                'site_name': site_name,
                'uid': urlsafe_base64_encode(force_bytes(user.pk)),
                'user': user,
                'token': token_generator.make_token(user),
                'protocol': 'https' if use_https else 'http',
            }
            subject = loader.render_to_string(subject_template_name, c)
            # Email subject *must not* contain newlines
            subject = ''.join(subject.splitlines())
            email = loader.render_to_string(email_template_name, c)

            msg = EmailMessage(subject, email, from_email, [user.email])
            msg.content_subtype = "html"  # Main content is now text/html
            msg.send()

            #send_mail(subject, email, from_email, [user.email])

完成以上步骤后,改变你的password_reset方法调用,并传入你的新表单类:

password_reset(request, password_reset_form=HTMLPasswordResetForm)

Burhan,我该如何更改现有的password_reset调用?我找不到任何地方调用它。请告诉我文件名。我已在我的forms.py中添加了您的表单。但我仍然不确定我的views.py和urls.py应该放什么(顺便说一句,你知道有两个urls.py。我已经在我的应用程序的一个中进行了更改——而不是全局的)。 - Saqib Ali
创建您自己的自定义视图以重置密码。 - Burhan Khalid

7

Django 2

您可以在 templates 文件夹中创建自定义模板: templates/registration/password_reset_email.html ,并将其添加到主要的 urls.py 中。

# add this import
from django.contrib.auth import views as auth_views

...


# add this path
path('accounts/password_reset/', auth_views.PasswordResetView.as_view(
    html_email_template_name='registration/password_reset_email.html'
)),
# just before this line (to take priority over the default one)
path('accounts/', include('django.contrib.auth.urls')),

代码中的注释很混乱。你能改正一下吗?# just before 应该替换为 # just after。或者你可以将 最后一行 剪切并粘贴到 path('accounts/password_reset/', ...) 上方,这样会更好。 - Xfce4

3

对于django2.2,使用以下方法在密码重置邮件模板中添加自定义电子邮件模板:

在urls.py文件中添加以下行:

urlpatterns = [
path('password-reset', 
            auth_views.PasswordResetView.as_view(
            template_name='accounts/password_reset.html', 
            html_email_template_name="accounts/email_reset_template.html",
            email_template_name='accounts/password_reset_email.html', 
            subject_template_name="accounts/password_reset_subject.txt",
            ),
             name="password_reset"),

    path('password-reset/done', 
        auth_views.PasswordResetDoneView.as_view(
            template_name='accounts/password_reset_done.html'),
             name="password_reset_done"),

    path('password-reset-complete/', 
        auth_views.PasswordResetCompleteView.as_view(
            template_name='accounts/password_reset_complete.html'),
             name="password_reset_complete"),

    path('password-reset-confirm/<uidb64>/<token>/', 
        auth_views.PasswordResetConfirmView.as_view(
            template_name='accounts/password_reset_confirm.html'),
             name="password_reset_confirm"),

#Add more urls you want
]

在添加了上述行后,如果您提供了html模板名称,则可以省略“password_reset”中的email_template名称,但要在通过电子邮件发送的电子邮件中使用模板,则需要html模板,现在选择您自己想要的不同网站的html模板,并将该文件放置在所有密码重置文件所在的相同文件夹中。
现在重要的部分是在您的自定义HTML模板中添加链接,为此,您需要像这样在自定义HTML模板中添加以下行:
<a  href="{{ protocol }}://{{ domain }}{% url 'password_reset_confirm' uidb64=uid token=token %}" style=".."> Reset Password </a>

根据您的需求,您可以将以下行添加到html模板的任何位置

     {% load i18n %}
{% autoescape off %}  You're receiving this e-mail because you requested a password reset for your Xandar account. {% trans "Thanks for using our site!" %} {% blocktrans %}The {{ site_name }} team{% endblocktrans %} {% endautoescape %} 

如果您还有任何疑问,请告知我。这绝对有效,我在我的项目中使用过它。

我无法让它工作。我的问题是 https://stackoverflow.com/questions/65335440/django-auth-password-reset-not-working-on-localhost - gujaratiraja

1

您尝试过编辑此模板吗? registration/password_reset_email.html

您可以将password_reset_email.html文件添加到项目中的templates/registration文件夹中,然后添加相关部分/HTML以获得漂亮的模板。 默认模板为空。


1
是的。我试过了。但是当你把HTML代码放在这个文件中时,HTML代码会显示在电子邮件的正文中。 - Saqib Ali
请参考Burhan Halid的答案 - 为了支持多部分/HTML发送,您需要重写该函数。 - johnbarr

1

我通过更改django.contrib.auth视图文件中password_reset函数的参数来解决了这个问题。默认情况下,html_email_template_name为None。 如果您将其指向'registration/password_reset_email.html'文件,则会发送带有适当HTML的电子邮件。

def password_reset(request,
               template_name='registration/password_reset_form.html',
               email_template_name='registration/password_reset_email.html',
               subject_template_name='registration/password_reset_subject.txt',
               password_reset_form=PasswordResetForm,
               token_generator=default_token_generator,
               post_reset_redirect=None,
               from_email=None,
               extra_context=None,
              html_email_template_name='registration/password_reset_email.html',
               extra_email_context=None):

0
你可以执行以下操作。
将两者都添加到password_reset中:
html_email_template_name='YOUR TEMPLATE PATH',
email_template_name='YOUR TEMPLATE PATH'

这对我有用(Django 1.11)


0

0

您需要覆盖默认的HTML。为此,请转到Django安装文件夹中的libs/site-packages/django,并从Django模板中复制password_reset_email.html,然后将其粘贴到[templates]/registration/password_reset_email.html中。接下来定义您的CSS并编辑默认的HTML,如果您的HTML代码显示在正文中,请关闭Django模板管理器自动转义,但这不是推荐的做法。


2
模板位于django/contrib/admin/templates/registration/... 我认为答案必须更加详细解释。 - ratata

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接