[Fixed]-How to make Django Password Reset Email Beautiful HTML?

15👍

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

With 2.0, the proper way of adding a URL to your urls.py file is by using 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

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.

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

You can access the source code for PasswordResetView by following this python path: 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.

11👍

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

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

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

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:

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.

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:

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.

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!

10👍

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.

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

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.

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(request, password_reset_form=HTMLPasswordResetForm)

7👍

Django 2

You can create a custom template in the templates folder: templates/registration/password_reset_email.html and add it to your main 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')),

3👍

For django2.2 use this thing to add a custom email template in the password reset email template

In urls.py file add these lines

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
]

After adding above lines you can omit the email_template name from ‘password_reset’ if you provide the html template name but to use template in the email sent via email you need the html template now choose your own html template
from different webiste you want and place that file inside the same folders where all password reset files are there.

Now important part is to add the link inside your custom html template to add this you need to add the following lines to your custom html template like this:

<a  href="{{ protocol }}://{{ domain }}{% url 'password_reset_confirm' uidb64=uid token=token %}" style=".."> Reset Password </a>

and you can add the below line anywhere in the html template according to your needs

     {% 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 %} 

If you still having any doubt then let me know

This definitely works and I have used this in my project.

1👍

Have you tried to edit this template?
registration/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.

1👍

I solved this by changing the parameter of password_reset function in django.contrib.auth views file. By default html_email_template_name is None.
If you direct it to ‘registration/password_reset_email.html’ file, its sending emails with proper 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👍

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.

0👍

You can do the following.

Add both to the password_reset:

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

It worked for me (Django 1.11)

0👍

The simplest (and honestly only way) to force the email to be sent as HTML was to override the form_valid method.

views.py:

class MyPasswordResetView(PasswordResetView):

  # forcing to use HTML email template (param: html_email_template_name)
  def form_valid(self, form):
      opts = {
          'use_https': self.request.is_secure(),
          'token_generator': self.token_generator,
          'from_email': self.from_email,
          'email_template_name': self.email_template_name,
          'subject_template_name': self.subject_template_name,
          'request': self.request,
          'html_email_template_name': 'registration/password_reset_email.html',
          'extra_email_context': self.extra_email_context,
      }
      form.save(**opts)
      return HttpResponseRedirect(self.get_success_url())

urls.py

path('password_reset/', myapp.MyPasswordResetView.as_view(), name='password_reset'),

0👍

1) Add a password_reset_email.html file to your templates/registration folder in your project “myApp”.
2) Move ‘django.contrib.admin’ below ‘myApp’ in your INSTALLED_APPS.
https://github.com/macropin/django-registration/issues/50#issuecomment-142637807

Not the best solution, but it worked for me!

Leave a comment