Create your user authentication frontend in django
Django follows the python philosophy “batteries included”, but also tries to be unobtrusive as possible, so while it bundles with a powerful authentication system, it does not provide user registration forms and activation right out of the box, mainly because this is something that each website handles this situation a bit differently from others, but it does offer some tools to get the job done quickly and efficiently.
On this tutorial I’ll show you how you can create your own registration form with email based authentication, and the login / logout functionality. Also, the email messages will be sent in a different thread, so the after registration page can be rendered quickly without having to wait for the email to be delivered.
First thing to do is to start a new project and applications, set up the database, enable the auth contrib package and set up the templates folder(s). It might be useful to enable the admin site as well just for testing purposes. I assume you’re familiar with the process, if not you can take a look at my previous tutorial for a step by step guide. For the purpose of this tutorial, I named the app as my_auth, and added a url pattern in the main project urls.py which redirects all urls starting by accounts/ to the my_auth app. Here’s how it looks:
from django.conf.urls.defaults import * from django.contrib import admin admin.autodiscover() from django.views.generic.simple import direct_to_template urlpatterns = patterns('', url(r'^$', direct_to_template, {'template': 'base.html'}, name="index"), (r'^admin/', include(admin.site.urls)), (r'accounts/', include('my_auth.register.urls')), )
I also created a base.html template (a very basic one), and a url pattern pointing directly to the base template through the direct_to_template generic view. The template just contains a bunch of links so we can navigate between the different parts of the app, and a content block to be overridden by sub templates. Here’s my template:
<html>
<head>
</head>
<body>
{% block content %}
<p>Index page</p>
<p><a href="/accounts/register/">Register</a></p>
<p><a href="/accounts/login/">Login</a></p>
<p><a href="/accounts/logout/">Logout</a></p>
{% endblock %}
</body>
</html>The urls in the links are hardcoded, because while we don’t effectively create the views to handle them, if we used the template url tag, every time we try to load a page, django would complain.
Usually the first step in the “authentication workflow” is the registration. We’ll follow along and start by creating the registration page. There is one very basic form (form as in a subclass of django.forms.Form) available through the contrib.auth package, it’s the UserCreationForm. On our first draft we are going to use it unmodified. First step, create a url pattern in the my_auth/urls.py file (you’ll probably have to create the file):
from django.conf.urls.defaults import * from views import * urlpatterns = patterns('', url(r'register/$', register1, name="register"), )
The register view will serve two purposes, display the registration form itself and process the registration, all through the same url, and we’re going to distinguish both actions by the request method. This is a common procedure on django development. Here’s the view:
from django.contrib.auth.forms import UserCreationForm from django.shortcuts import render_to_response from django.http import HttpResponseRedirect def register(request): if request.method == 'POST': form = UserCreationForm(request.POST) if form.is_valid(): new_user = form.save() return HttpResponseRedirect("/") else: form = UserCreationForm() return render_to_response("register.html", {'form': form, })
If the request method is POST (i.e. the registration form was submitted), we instantiate a UserCreationForm using the POST variables passed in the request, and if the form is valid, we call the save method, which saves the new user to the database, and redirect to the front page. If the request method is GET (i.e. the url was accessed by a link or written directly in the address bar), we create a plain vanilla UserCreationForm. By the end of the method, unless the form was valid and we were redirected, we should have UserCreationForm stored in the form variable, so we use it to render the registration template:
{% extends "base.django" %}
{% block content %}
<h1>Be a proud member of our super site</h1>
<form action="" method="post">
{{ form.as_p }}
<input type="submit" value="Register" />
</form>
{% endblock %}This is very simple, we use the method as_p of the django Form class, from which the UserCreationForm inherits, that basically renders the entire form into html. The method name stands for “As P Tags”, so as you may have guessed by now, the form elements will be wrapped in p tags. There are other methods like this one, to render the form as list items or table rows, see the django docs for more info. You can now access your registration page and try to register some users. You can make some errors on purpose; try omitting the username or password, or insert an invalid username or two distinct passwords. See what happen? Courtesy of the forms api… I can read your mind now, and I know what you’re thinking, and yes I agree, we have done nothing special yet, but we’re just starting.
If you never done so, now it’s a perfect opportunity to peek inside the django sources, open the <django source folder>/contrib./auth/forms.py file, you can also see it online here (1.1.x branch), and let’s take a look at the UserCreationForm class definition. As you can see, it’s very simple, it’s a subclass of ModelForm, which is itself a subclass of BaseForm (the mother of all form classes) and is specially suited to build forms that are somewhat tied to a particular mode (if you’re already familiar with the django forms API you can skip the rest of this chapter). The UserCreationForm class defines three properties username, password1 and password2, each is initialized using django form fields, which basically define how we want a particular field presented in the form and then validated. The username property uses a RegexField, probably one of the most useful fields in django forms, it takes a regular expression and validates user input against that regular expression, so when it’s time to work with the values they are already validated for us, in this case we just want alphabet characters, digits and underscores to consider a username as valid. Sweet uh? But don’t start imagining right now how you can use this to validate email addresses, because there is a form field dedicated to that case, as you’re going to see in a moment. The other arguments on the field are pretty straight forward. The Meta subclass defines which model we are tying this ModelForm class and which fields are to be “tied”, so for example, if we reuse this form to allow users to change their settings the password already stored in the database is not displayed. The clean_ methods are used to add some extra validation to our form fields, they are just called if the values pass the first validation provided by form fields itself. Clean_username checks if the username does not exist already, and clean_password2 checks if the value matches the value of password1. We just need to define those methods; django takes care of the rest. Finally, the save method overrides the ModelForm one just to set the password since the meta class instructs that only the username field is to be automatically processed. But notice there is a special argument in the save method signature, commit, which defaults to True. This UserCreationForm class (just like the ModelForm class) is particularly suited to be subclassed, and we can use the commit argument to specify if we want to save the user right now or not, since we may be calling this save method from a child class save method, and want to perform some more extra work before really saving the user object.
So as you can see, we have two options, either we create a brand new Form class or we just subclass this one. I vote for the second option. So we can start by creating a new file under my_auth folder named forms.py (or whatever you want as long as it uses the .py extension) start typing:
from django.contrib.auth.forms import UserCreationForm from django import forms from django.contrib.auth.models import User class RegisterForm(UserCreationForm): email = forms.EmailField(label="E-Mail") class Meta: model = User fields = ("username", "email", )
We just added an email field, and override the Meta class to make the base ModelForm class aware of the new field. But we need to do a bit more; we need to make sure that there are no duplicate emails in the database as well. So we can create a clean_email method to make this verification:
class RegisterForm(UserCreationForm): ... def clean_email(self): email = self.cleaned_data["email"] try: User.objects.get(email=email) except User.DoesNotExist: return email raise forms.ValidationError("A user with that email address already exists.") ...
Starting to get in shape, we just need to make some changes in the view to use this class instead:
... from forms import RegisterForm def register1(request): if request.method == 'POST': form = RegisterForm(request.POST) if form.is_valid(): new_user = form.save() return HttpResponseRedirect("/") else: form = RegisterForm() return render_to_response("register1.html", {'form': form, })
You can start creating some users now. Now, before we take care of validation let’s work on a small issue. If you tested the form you probably noticed that the email field is on the bottom of the form. That usually is not what we want; normally it should appear right bellow the username field. That’s because the order in which the field are shown reflect the order in which the fields are defined. There is not much we can do regarding this, since the email field is defined in a subclass it will always appear after the base class fields. Our only option is to define the correct order in the register template. That is not such a bad thing; it’s probably what you would do anyway. Here’s the new template file:
{% extends "base.django" %}
{% block content %}
<h1>Be a proud member of our super site</h1>
<form action="" method="post">
<div>
{{ form.username.errors }}
<label for="id_username">Username: </label>
{{ form.username }}
</div>
<div>
{{ form.email.errors }}
<label for="id_email">E-Mail: </label>
{{ form.email }}
</div>
<div>
{{ form.password1.errors }}
<label for="id_password1">Password: </label>
{{ form.password1 }}
</div>
<div>
{{ form.password2.errors }}
<label for="id_password2">Repeat password: </label>
{{ form.password2 }}
</div>
<input type="submit" value="Register" />
</form>
{% endblock %}See, is not hard at all, although now the help text does not show, but it’s all available through the form object, you can use form.username.help_text to display it for example.
Ok, now the fun part, we want users to activate their account through email. First thin, we need to setup django to send emails. On your settings.py file you must define EMAIL_HOST, EMAIL_PORT , EMAIL_USE_TLS, EMAIL_HOST_USER and EMAIL_HOST_PASSWORD. Not all are mandatory, if your smtp server uses port 25, you don’t need to specify a port since 25 is the default, id you don’t to authenticate to the server you are not required to define username and password, basically, you just need to define the server, and only if is not localhost (which is the default). Actually for most hosts out there you don’t even need to specify nothing here. But if you’re developing in your home or work computer, is probably best to put something that works here, and python has a great built in server for this (great for testing purposes). If you want to use it, just open a shell and type the following:
python -m smtpd -n -c DebuggingServer localhost:1025
Leave it open, since now all email sent through this simulated server are displayed in the shell. The server uses port 1025 and runs on localhost so you can define your settings like this:
EMAIL_HOST = "localhost" EMAIL_PORT = 1025
Now we’re prepared to send and receive emails, so let’s start building our validation system. First of all we must make sure that a newly created user is not immediately active. Django user model has a boolean field name is_active, which is true by default. So let’s override the save method in our RegisterForm class:
class RegisterForm(UserCreationForm): ... def save(self): user = super(RegisterForm, self).save(commit=False) user.is_active = False user.save() ...
It’s almost identical to the parent save form, so there’s nothing new here. Now we must create some functions to handle the activation. Usually there’s some sort of activation code, either some random value which is then stored in the database and compared later, or there is some algorithm that builds a code based on user name or email, etc… We are going to do something very simple, we are just going to create an md5 hash against the username. We are also going use a template as the base for the email. So let’s start by creating it:
Hello {{ username }},
To activate your account follow this url:
{{ url }}Now let’s create a file named activation.py or something like that under the my_auth folder. And create a send_activation function:
from django.core.mail import send_mail from hashlib import md5 from django.template import loader, Context def send_activation(user): code = md5(user.username).hexdigest() url = "http://127.0.0.1:8000/activate/?user=%s&code=%s" % (user.username, code) print(url) template = loader.get_template('registration/activation.html') context = Context({ 'username': user.username, 'url': url, }) send_mail('Activate account at super site', template.render(context), 'no-reply@example.com', [user.email])
Perefect… And it shouldn’t be hard to understand: we create the code and the url, load the template and create the template context, then we use django send_mail function, which takes the title, body (the rendered template), sender mail, and a list of recipients, in this case it has just a single element, the user email. Now we just need to plug it somewhere, and I guess that the save method of the RegisterForm class would be a perfect place:
... from activation import send_activation class RegisterForm(UserCreationForm): ... def save(self): user = super(RegisterForm, self).save(commit=False) user.is_active = False send_activation(user) user.save() ...
Try registering a user now. If you’re using a real mail server you must use a valid email address as well, so you can test it, but if you’re using the fake python server, just use any email you want, you can even use Obama’s email and pretend that he just registered on your site
. But there’s something you should know when using the fake mail server, it might show some extra character that are not supposed to be there, so the validation code might not be valid. You can use a print statement after you define the url to print it to the same console as the development server, so you can use it later.
But imagine the following situation. Someone decides to register on your site, he fills the registration form and submit it. Meanwhile, back on your server, it processes the request and sends the activation email, but the mail server is too busy to process your request as fast as you would like, especially if the mail server is on a different machine than the web server. What happens is that your new user won’t get a response until the email is sent. This is the same if for example you have to do some heavy database operations, but luckily, python has thread support, so you can send your email for example using a different process while the main process continues to handle the http response. And is not that hard as it might look, take a look at the new “threaded” save method:
... from threading import Thread class RegisterForm(UserCreationForm): ... def save(self): user = super(RegisterForm, self).save(commit=False) user.is_active = False thread = Thread(target=send_activation, args=[user]) thread.setDaemon(True) thread.start() user.save() ...
See, couldn’t be simpler. The thread “constructor” takes the function and a list of arguments to pass on to the function, then we call setDaemon to true, otherwise it wouldn’t work, and start the thread, then we continue happily with what we’re doing before, while the send_activation function does its job in a different process. You can make it “sleep” for a while using python time module to to simulate a busy server:
... import time def send_activation(user): time.sleep(10) code = md5(user.username).hexdigest() ...
Now try to register a user, the response should be almost immediate, but your email should take 10 seconds to be sent. Nice uh?
Now is time to create a view to process the activation. First let’s create a function to do this, it should get the username and the activation code as arguments and live in the same file as send_activation:
from django.contrib.auth.models import User ... def activate_user(username, code): if code == md5(username).hexdigest(): user = User.objects.get(username=username) user.is_active = True user.save() return True else: return False
Simple, we just check the code against an md5 hash based on the username, just like we did to generate it in send_activation, and if that test succeeds we retrieve the user from the database and activate it. Then we return True to flag the success of the operation, or false if the check fails. Notice that ideally we should check if the user really exists as well, but for the purpose of this tutorial we’ll leave it this way. Now let’s create the url pattern:
urlpatterns = patterns('', ... url(r'activate/$', activate, name="activate"), )
And the view:
from activation import activate_user from django.http import HttpResponseRedirect, Http404 ... def activate(request): user = request.GET.get('user') code = request.GET.get('code') if activate_user(user, code): return HttpResponseRedirect("/") else: raise Http404
Again, this is very basic, we get the username and the code from the GET variables, and in case the activation succeeds we redirect the user to the front page, otherwise we raise an Http404 exception, which displays a 404 error page.
Now that we’ve created and activated some users is time to allow them to login and logout. This is the simplest part of all. We’ll use some bundled views to achieve that, we just need to create a simple template to display the login form. This template will receive a Form object in the context, and by default django expects it to live in registration/login.html (but we can change that), so we can do just this:
{% extends "base.django" %}
{% block content %}
{% if form.errors %}
<p class="error">Oops, invalid username or password</p>
{% endif %}
<form action="{% url login %}?next=/" method="post">
{{ form.as_p }}
<input type="submit" value="login" />
</form>
{% endblock %}The only tricky part is the form action url. We can use a next GET variable to tell django where to redirect the user id the login succeeds, in this case the index page, otherwise it will just use the default which is /accounts/profile/. The rest is old stuff by now.
Now we just need to define the url pattern, and since we’re here we can define the pattern for the logout page as well:
... from django.contrib.auth.views import login, logout urlpatterns = patterns('', ... url(r'logout/$', logout, name="logout"), url(r'activate/$', activate, name="activate"), )
Now just go to the base template and add a next GET variable to the logout url as well.
<p><a href="/accounts/logout/?next=/">Logout</a></p>
And that’s pretty much it. Now you should be able to create your own authentication frontend easily.
Hope you like this tutorial.
Cheers