如何使Django密码重置电子邮件美丽的HTML?

时间:2022-08-13 19:21:59

My Django application currently is setup to use the native registration package to handle user authentication and management.

我的Django应用程序目前设置为使用本机注册包来处理用户身份验证和管理。

I have created a handful of files in myApp/templates/registration that are used to send out the password reset token as described in these docs.

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

It works fine. Except the password reset email is an ugly text-only monstrosity. I would like to make it match the look and feel of all other emails my application sends. IE: I want it to be HTML and contain images, styles and links. How do I do that?

它工作正常。除了密码重置电子邮件是一个丑陋的文本怪物。我想使它与我的应用程序发送的所有其他电子邮件的外观和感觉相匹配。 IE:我希望它是HTML并包含图像,样式和链接。我怎么做?

I followed the detailed instructions here. However, there is an error in that code that I don't know what to do with: 'CustomPasswordResetForm' object has no attribute 'users_cache'

我按照这里的详细说明。但是,该代码中存在一个错误,我不知道该怎么做:'CustomPasswordResetForm'对象没有属性'users_cache'

Can someone show me a detailed working example how to accomplish this? I wish it weren't so hard.

有人能告诉我一个详细的工作示例如何实现这一目标吗?我希望不是那么难。

6 个解决方案

#1


7  

The default helper views for django authentication cannot send multi-part (HTML) emails because the underlying send_mail method does not support HTML emails yet.

django身份验证的默认帮助程序视图无法发送多部分(HTML)电子邮件,因为底层的send_mail方法尚不支持HTML电子邮件。

This will be fixed in the next release, by adding a html_message flag.

这将通过添加html_message标志在下一版本中修复。

The easiest way to fix this is to create your own custom password reset form, and use EmailMultiAlternatives to send your message, thus allowing your HTML to render correctly in the email client.

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

You can use the existing form, and make your changes:

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

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])

Once you have done that, change your password_reset method call and pass in your new form class:

完成后,更改password_reset方法调用并传入新的表单类:

password_reset(request, password_reset_form=HTMLPasswordResetForm)

#2


7  

[cross-posted from Does Django password_reset support html email templates? ]

[交叉发布来自Django password_reset支持HTML电子邮件模板? ]

After some amount of trial and error, I discovered a much, much more terse way to supply a custom templated password reset email in the latest version of Django (1.8).

经过一些试验和错误后,我发现了一种更简洁的方式来在最新版本的Django(1.8)中提供自定义模板化密码重置电子邮件。

In your project/urls.py, add these imports:

在您的project / urls.py中,添加以下导入:

from django.contrib.auth import views as auth_views
from django.core.urlresolvers import reverse_lazy

And add the following route in your urlpatterns before the usual django contrib auth url route inclusion:

并在通常的django contrib auth url route包含之前在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')),

And then, in your app's templates/registration folder, create the password_reset_html_email.html with whatever HTML template you want.

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

The reason this seemed necessary lay in the source for django/contrib/auth/views.py, which has the view function the original URL route is mapped to:

这似乎是必要的原因在于django / contrib / auth / views.py的源代码,它具有原始URL路由映射到的view函数:

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

The html_email_template_name is set to None as default, and there didn't seem to be a way to assign its value, aside from rewriting this specific route for this case as I mentioned above.

默认情况下,html_email_template_name设置为None,除了为此情况重写此特定路由之外,似乎没有办法分配其值,如上所述。

Hopefully this helps without needing to copy-paste a bunch of nearly-identical code like some of the other answers suggested - feedback is welcome, of course!

希望这有助于无需复制粘贴一堆几乎完全相同的代码,如建议的其他一些答案 - 当然欢迎反馈!

#3


1  

Have you tried to edit this template? registration/password_reset_email.html

您是否尝试编辑此模板?登记/ password_reset_email.html

You can add a password_reset_email.html file to your templates/registration folder in your project, then add the relevant sections / HTML to get a nice template. The default template is empty.

您可以将password_reset_email.html文件添加到项目的模板/注册文件夹中,然后添加相关的部分/ HTML以获得一个不错的模板。默认模板为空。

#4


1  

Adding my findings for django version 2.0 as I found the rest of the answers to this question to be out-of-date.

添加我对django 2.0版的发现,因为我发现这个问题的其余答案已经过时了。

With 2.0, the proper way of adding a URL to your urls.py file is by using path():

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

The next code snippet to highlight here is the .as_view() function. Django 2.0 implements auth views as classes. You can read more about this in the Authentication Views documentation

下面要突出显示的代码片段是.as_view()函数。 Django 2.0将auth视图实现为类。您可以在Authentication Views文档中阅读有关此内容的更多信息

You then "convert" the class to a view using `.as_view() and you are able to pass in any class attributes defined in the source code as named parameters.

然后使用`.as_view()将类“转换”为视图,并且可以将源代码中定义的任何类属性作为命名参数传递。

Passing in html_email_template_name (which defaults to None) automatically sends an html email.

传入html_email_template_name(默认为None)会自动发送html电子邮件。

You can access the source code for PasswordResetView by following this python path: django.contrib.auth.views

您可以通过以下python路径访问PasswordResetView的源代码:django.contrib.auth.views

Here you can see the other class attributes you can pass into PasswordResetView and the other auth views. This is super helpful for passing extra_context into your django templates as well.

在这里,您可以看到可以传递给PasswordResetView和其他身份验证视图的其他类属性。这对于将extra_context传递到django模板也非常有用。

#5


0  

You have to overide the default html. To do that goto you django istallation folder in libs/site-packages/django and copy password_reset_email.html from django templates and paste it in [templates]/registration/password_reset_email.html. Then define your CSS and edit the the default html and if your HTML code shows up in body turn off django template manager auto escaping but it's not recommanded.

你必须覆盖默认的html。为此,请转到libs / site-packages / django中的django istallation文件夹,并从django templates复制password_reset_email.html并将其粘贴到[templates] /registration/password_reset_email.html中。然后定义您的CSS并编辑默认的html,如果您的HTML代码显示在正文中,请关闭django模板管理器自动转义,但不建议使用它。

#6


0  

You can do the following.

您可以执行以下操作。

Add both to the password_reset:

将两者都添加到password_reset:

html_email_template_name='YOUR TEMPLATE PATH',
email_template_name='YOUR TEMPLATE PATH'

It worked for me (Django 1.11)

它对我有用(Django 1.11)

#1


7  

The default helper views for django authentication cannot send multi-part (HTML) emails because the underlying send_mail method does not support HTML emails yet.

django身份验证的默认帮助程序视图无法发送多部分(HTML)电子邮件,因为底层的send_mail方法尚不支持HTML电子邮件。

This will be fixed in the next release, by adding a html_message flag.

这将通过添加html_message标志在下一版本中修复。

The easiest way to fix this is to create your own custom password reset form, and use EmailMultiAlternatives to send your message, thus allowing your HTML to render correctly in the email client.

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

You can use the existing form, and make your changes:

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

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])

Once you have done that, change your password_reset method call and pass in your new form class:

完成后,更改password_reset方法调用并传入新的表单类:

password_reset(request, password_reset_form=HTMLPasswordResetForm)

#2


7  

[cross-posted from Does Django password_reset support html email templates? ]

[交叉发布来自Django password_reset支持HTML电子邮件模板? ]

After some amount of trial and error, I discovered a much, much more terse way to supply a custom templated password reset email in the latest version of Django (1.8).

经过一些试验和错误后,我发现了一种更简洁的方式来在最新版本的Django(1.8)中提供自定义模板化密码重置电子邮件。

In your project/urls.py, add these imports:

在您的project / urls.py中,添加以下导入:

from django.contrib.auth import views as auth_views
from django.core.urlresolvers import reverse_lazy

And add the following route in your urlpatterns before the usual django contrib auth url route inclusion:

并在通常的django contrib auth url route包含之前在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')),

And then, in your app's templates/registration folder, create the password_reset_html_email.html with whatever HTML template you want.

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

The reason this seemed necessary lay in the source for django/contrib/auth/views.py, which has the view function the original URL route is mapped to:

这似乎是必要的原因在于django / contrib / auth / views.py的源代码,它具有原始URL路由映射到的view函数:

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

The html_email_template_name is set to None as default, and there didn't seem to be a way to assign its value, aside from rewriting this specific route for this case as I mentioned above.

默认情况下,html_email_template_name设置为None,除了为此情况重写此特定路由之外,似乎没有办法分配其值,如上所述。

Hopefully this helps without needing to copy-paste a bunch of nearly-identical code like some of the other answers suggested - feedback is welcome, of course!

希望这有助于无需复制粘贴一堆几乎完全相同的代码,如建议的其他一些答案 - 当然欢迎反馈!

#3


1  

Have you tried to edit this template? registration/password_reset_email.html

您是否尝试编辑此模板?登记/ password_reset_email.html

You can add a password_reset_email.html file to your templates/registration folder in your project, then add the relevant sections / HTML to get a nice template. The default template is empty.

您可以将password_reset_email.html文件添加到项目的模板/注册文件夹中,然后添加相关的部分/ HTML以获得一个不错的模板。默认模板为空。

#4


1  

Adding my findings for django version 2.0 as I found the rest of the answers to this question to be out-of-date.

添加我对django 2.0版的发现,因为我发现这个问题的其余答案已经过时了。

With 2.0, the proper way of adding a URL to your urls.py file is by using path():

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

The next code snippet to highlight here is the .as_view() function. Django 2.0 implements auth views as classes. You can read more about this in the Authentication Views documentation

下面要突出显示的代码片段是.as_view()函数。 Django 2.0将auth视图实现为类。您可以在Authentication Views文档中阅读有关此内容的更多信息

You then "convert" the class to a view using `.as_view() and you are able to pass in any class attributes defined in the source code as named parameters.

然后使用`.as_view()将类“转换”为视图,并且可以将源代码中定义的任何类属性作为命名参数传递。

Passing in html_email_template_name (which defaults to None) automatically sends an html email.

传入html_email_template_name(默认为None)会自动发送html电子邮件。

You can access the source code for PasswordResetView by following this python path: django.contrib.auth.views

您可以通过以下python路径访问PasswordResetView的源代码:django.contrib.auth.views

Here you can see the other class attributes you can pass into PasswordResetView and the other auth views. This is super helpful for passing extra_context into your django templates as well.

在这里,您可以看到可以传递给PasswordResetView和其他身份验证视图的其他类属性。这对于将extra_context传递到django模板也非常有用。

#5


0  

You have to overide the default html. To do that goto you django istallation folder in libs/site-packages/django and copy password_reset_email.html from django templates and paste it in [templates]/registration/password_reset_email.html. Then define your CSS and edit the the default html and if your HTML code shows up in body turn off django template manager auto escaping but it's not recommanded.

你必须覆盖默认的html。为此,请转到libs / site-packages / django中的django istallation文件夹,并从django templates复制password_reset_email.html并将其粘贴到[templates] /registration/password_reset_email.html中。然后定义您的CSS并编辑默认的html,如果您的HTML代码显示在正文中,请关闭django模板管理器自动转义,但不建议使用它。

#6


0  

You can do the following.

您可以执行以下操作。

Add both to the password_reset:

将两者都添加到password_reset:

html_email_template_name='YOUR TEMPLATE PATH',
email_template_name='YOUR TEMPLATE PATH'

It worked for me (Django 1.11)

它对我有用(Django 1.11)