In a previous post Customizing the Django admin - Branding I discuss a few simple tips to provide custom branding in the admin. It's a basic customization that takes a few minutes to implement but provides the essential concepts behind getting personal. This time, getting your hands dirty will ask a little bit more from us.

The plumbing is documented in a post I stumbled upon titled Password Reset Django 1.0. This is the often requested, and more often neglected, forgot password functionality that leaves developers and system admins searching through internal email chains, sharing plain text passwords over IM, etc. You've all been received these before - "hey forgot my password can you create a new one for me?" requests that ask us to stop what we're doing, log into the admin, find the user, reset their password and then somehow send the user their new password (email, napkin, IM, scrap paper, etc.). This is both a productivity roadblock and it's insecure. That's no good. What is good is that we've had forgot password functionality (aka "Reset your password") since Django 1.0, it's just been hidden from us... not any more.

Quick refresh

Just to refresh our memories, since we're all forgetting our passwords, here are a few websites I visit every day and their login forms with "forgot password" calls to action.


Bitbucket asks users if they "Lost your password?" before following the same process Django Admin uses to reset the users password. I'm going to guess Bitbucket actually uses the same exact reusable Django application as well. Can anyone confirm?


Twitter follows a similar reset password pattern, asking the user for their email address, but also offers an alternative option of providing their usersname which is nice. Might as well take the opportunity to ask follow me on twitter - @montylounge.

Django forgot password workflow

The Django admin forgot password workflow follows this path:

  • User visits forgot password form which requests the user's email.
  • Application send an email to the user asking them to click on a link to confirm and complete the password reset request.
  • User enters in new password, confirms password, and submits.
  • Application registers the new password and the User can progress with their brand new password.

All the above are simple, intuitive and secure. So let' get this thing wired up.

Adding Django forgot password

Before we dive into the unbelievably simple implementation of this awesome feature we should know what's going on behind the scenes first, at least at a high level.


As expected the auth module contains the views and urls related to the logic for handling the forgot password functionality. The templates are located in the admin module's templates directory.


If you really want to customize the look and feel, then the above html files are the template you're interested in – they handle all the steps of the entire process, including the email. Today we're going to do the bare minimum to get this feature implemented.

The 6 minute Forgot Password

First, we're need to add the "Forgot Password?" link to the login template we all know and love.

We don't want to edit the login template included with Django source because we don't want to run any chances of merge conflicts if we upgrade Django source. Thanks to Django's TEMPLATE_LOADERS we can very easily customize an applications template by creating a local copy of that file in our project's template directory. Django knows to look for the template in your project's template directory structure first and if it doesn't locate the template there then defaults to the calling application's template directory – in this case Django itself.

cp [path to]/django/contrib/admin/templates/admin/login.html [yourProjectPath /templates/admin/login.html

Now that we have the template local to our project, let's add the link to wire up the forgot password feature. Open up the login.html file and search for:

<input type="hidden" name="this_is_the_login_form" value="1" />

Directly below the above input element add this markup:

<p style="margin-left:114px;">
    <a href="{% url password_reset %}">Forgot password?</a>

The above markup places the forgot password text link on your login form, and you're done. I told you this was easy. Here's an example of that it looks like.

The Django-Mingus reusable apps powered blog engine asks "Forgot password?".

I realize the inline style margin-left:114px isn't the cleanest solution for alignment, and inline styles overall isn't a best practice, but it looks good enough for me on the browsers I support that I can live with it. To make this solution even cleaner we really should implement the {{ block extrastyle }} block to add the positioning via a custom CSS style element. I've implemented the inline here for the sake of isolating the example in this post. As always, if anyone has a better solution than margin-left please post it in the comments!

Next is wiring up the url/view dispatcher, otherwise (and you may have already experienced this if you tried to view the login template) the admin login template with raise a template exception when trying to reference the url dispatcher name used by the url template tag {% url password_reset %}. Plus, if we skip this step none of this will work :) So, to wire up the views just append the below to your applications file.

urlpatterns = patterns('',
    url(r'^admin/password_reset/$', 'django.contrib.auth.views.password_reset', name='password_reset'),
    (r'^password_reset/done/$', 'django.contrib.auth.views.password_reset_done'),
    (r'^reset/(?P[0-9A-Za-z]+)-(?P.+)/$', 'django.contrib.auth.views.password_reset_confirm'),
    (r'^reset/done/$', 'django.contrib.auth.views.password_reset_complete'),

urlpatterns += patterns('', other project url patterns...

An important note here - you'll see that I create a separate patterns list just for my admin urls. Beneath this list I create another one for all my project's urls patterns. Depending on your current implementation, and how you implement this, the last list item (r'^admin/(.*)',, may not be needed since it's often used as the default admin url dispatcher, and often found as the last list item in the urlpatterns variable.

And that's it! These simple steps wire up the entire workflow discussed above - from the email form, to the sending of the email, the reset password form, etc. You're all set to move along.

Customize the email

Before we go there's one final piece you should know about. Remember the workflow we discussed above? In step #2 the application sends an email to the user asking them to reset their password. This email uses the Django Sites application that comes with Django to provide further application flexibility. For the email to properly pass along domain url and domain name you at least need to make sure you set those correctly in the admin.

The Django contrib sites application UI in the admin.

If you want to customize the email further, to borrow the slogan from Apple IPhone commercials, there's a template for that...


Please note that customizing the email is not a requirement since the default email template works perfectly fine, but if you must then you should copy this template to to your application specifc templates directory.

cp [path to]/django/contrib/admin/templates/registration/password_reset_email.html [yourProjectPath]/templates/registration/password_reset_email.html

Once that's complete feel free to customize to your hearts content.


I receive reset password requests often enough that for me taking the little time needed to implement this feature is well worth the effort. In fact, I'm not 100% sure why it's not available by default on the login template in the Django admin.

I'm pretty sure most of the credit for the new password reset functionality stems from James's Bennet's popular django-registration reusable app being recently added as the most latest addition to the contrib namespace in Django proper (if not I apologize to the author of the solution). It's tremendously useful application, but sadly the above functionality is not documented in the official Django documentation (unless I missed it). I realize patches are welcome, so I plan on submitting a documentation patch and maybe it'll help someone out down the road.

With a little legwork we can very easily add "forgot password?" functionality to the Django admin login page. Do you have any Django admin customization tricks? Post them in the comments!