14. Django 1.8 Server Build - CentOS 7 hosted on VPS - User Authentication (register & forms)

The Django authentication system handles both authentication and authorization.
- authentication verifies a user is who they claim to be.
- authorization determines what an authenticated user is allowed to do.
Let's create an app called driver:
$ python manage.py startapp driver

First, let's look at models:
from django.db import models from django.contrib.auth.models import User from django.db.models.signals import post_save class Driver(models.Model): user = models.OneToOneField(User) birthday = models.DateField() name = models.CharField(max_length=100) def __unicode__(self): return self.name # create user object to attach to driver object def create_driver_user_callback(sender, instance, **kargs): driver, new = Driver.objects.get_or_create(user=instance) post_save.connect(create_driver_user_callback, User)
In the code, we imported User:
from django.contrib.auth.models import User
User objects are the core of the authentication system. They typically represent the people interacting with our site and are used to enable things like restricting access, registering user profiles, associating content with creators etc. Only one class of user exists in Django's authentication framework, i.e., superusers or admin staff users are just user objects with special attributes set, not different classes of user objects (Using the Django authentication system).
Note that we haven't created any user yet.
Then, we create Driver class for user profile. It has user, birthdate, and name.
driver, new = Driver.objects.get_or_create(user=instance)
get_or_create(defaults=None, **kwargs) is convenience method for looking up an object with the given kwargs (may be empty if our model has defaults for all fields), creating one if necessary. It returns a tuple of (object, created), where object is the retrieved or created object and created is a boolean specifying whether a new object was created:
Now we did setup a model for our user profile.
We want to add driver to INSTALLED_APPS, and add AUTH_PROFILE_MODULE setting to have the get_profile() method on the User model:
# provide our get_profile() AUTH_PROFILE_MODULE = 'driver.Driver' INSTALLED_APPS = ( 'django.contrib.admin', 'django.contrib.auth', 'django.contrib.contenttypes', 'django.contrib.sessions', 'django.contrib.messages', 'django.contrib.staticfiles', 'car', 'tinymce', 'pages', 'driver', )
Note that starting Django 1.5, AUTH_PROFILE_MODULE has been deprecated in favour of custom User models. Though our code should still work, we better use custom User model.
It's time to create a new table for driver app that's just added to INSTALLED_APPS of settings.py. Note that syncdb will only create tables for models which have not yet been installed. It will never issue ALTER TABLE statements to match changes made to a model class after installation.
$ python manage.py syncdb
However, as we can see from the file list below, the directory migrations keeps from creating a new driver table:
$ pwd /srv/www/django/djangotest/driver $ ls admin.py __init__.py migrations models.py tests.py views.py
syncdb is depricated from django 1.7, and migration will reduce the hassle of using syncdb and migration separately.
$ python manage.py makemigrations Migrations for 'driver': 0001_initial.py: - Create model Driver $ python manage.py migrate Operations to perform: Synchronize unmigrated apps: staticfiles, car, tinymce, messages, pages Apply all migrations: admin, contenttypes, driver, auth, sessions Synchronizing apps without migrations: Creating tables... Running deferred SQL... Installing custom SQL... Running migrations: Rendering model states... DONE Applying driver.0001_initial... OK
Migrations are Django's way of propagating changes we make to our models (adding a field, deleting a model, etc.) into our database schema.
They're designed to be mostly automatic, but we'll need to know when to make migrations, when to run them, and the common problems we might run into.
Let's check if the driver table has been created.

Let's register our driver app in driver/admin.py:
from django.contrib import admin from driver.models import Driver admin.site.register(Driver)
Then, restart our web server:
$ sudo apachectl restart
Now we have admin page with Driver app:



We can create users from the admin, however, we'll build a registration form for users be able to register themselves.
Let's create the form for users to fill out.
from django import forms from django.contrib.auth.models import User from django.forms import ModelForm from driver.models import Driver class RegistrationForm(ModelForm): username = forms.CharField(label=(u'User Name')) email = forms.EmailField(label=(u'Email Address')) password = forms.CharField(label=(u'Password'), widget=forms.PasswordInput(render_value=False)) password1 = forms.CharField(label=(u'Verify Password'), widget=forms.PasswordInput(render_value=False)) class Meta: model = Driver exclude = ('user',) def clean_username(self): username = self.cleaned_data['username'] try: User.objects.get(username=username) except User.DoesNotExist: return username raise forms.ValidationError("That username is already taken, please select another.") def clean(self): if self.cleaned_data['password'] != self.cleaned_data['password1']: raise forms.ValidationError("The passwords did not match. Please try again.") return self.cleaned_data
from django.http import HttpResponseRedirect from django.shortcuts import render_to_response from django.template import RequestContext from driver.forms import RegistrationForm def DriverRegistration(request): if request.user.is_authenticated(): return HttpResponseRedirect('/profile/') if request.method == 'POST': pass else: ''' user is not submitting the form, show them a blank registration form ''' form = RegistrationForm() context = {'form': form} return render_to_response('register.html', context, context_instance=RequestContext(request))
If users hit the registration line:
return HttpResponseRedirect('/profile/')
and already logged in, we're going to send them to profile because we don not want them to register.
{% extends "base.html" %} {% block content %} {{ form }} {% endblock %}
from django.conf.urls import include, url from django.contrib import admin from django.conf import settings urlpatterns = [ url(r'^admin/', include(admin.site.urls)), url(r'^tinymce/', include('tinymce.urls')), url(r'^$', 'pages.views.MainHomePage'), url(r'^cars/$', 'car.views.CarsAll'), url(r'^cars/(?P<carslug>.*)/$', 'car.views.SpecificCar'), url(r'^makes/(?P<makeslug>.*)/$', 'car.views.SpecificMake'), url(r'^media/(?P<path>.*)$', 'django.views.static.serve', { 'document_root': settings.MEDIA_ROOT, }), url(r'^register/$', 'driver.views.DriverRegistration'), ]
Restart web server:
$ sudo apachectl restart
If we type in http://djangotest.sfvue.com/register/, we get the following error, "...The current URL, profile/, didn't match any of these."

That's because as we can see from the views.py:
... def DriverRegistration(request): if request.user.is_authenticated(): return HttpResponseRedirect('/profile/') ...
Actually, because the user, in this case, sfvue, is already logged in, we're redirected to the /profile/ which is not implemented yet.

So, we may want to be logged out:

Now, finally, we get the register form properly:

There are other output options though for the <label>/<input> pairs:
- {{ form.as_table }} will render them as table cells wrapped in <tr> tags
- {{ form.as_p }} will render them wrapped in <p> tags
- {{ form.as_ul }} will render them wrapped in <li> tags
So, let's try {{ form.as_p }}:
{% extends "base.html" %} {% block content %} {{ form.as_p }} {% endblock %}

Actual code looks like this:
<html> <head> <link rel="stylesheet" type="text/css" href="/static/css/car.css" /> </head> <body> <div id="pageContainer"> <p><label for="id_birthday">Birthday:</label> <input id="id_birthday" name="birthday" type="text" /></p> <p><label for="id_name">Name:</label> <input id="id_name" maxlength="100" name="name" type="text" /></p> <p><label for="id_username">User Name:</label> <input id="id_username" name="username" type="text" /></p> <p><label for="id_email">Email Address:</label> <input id="id_email" name="email" type="email" /></p> <p><label for="id_password">Password:</label> <input id="id_password" name="password" type="password" /></p> <p><label for="id_password1">Verify Password:</label> <input id="id_password1" name="password1" type="password" /></p> </div> </body> </html>
Ph.D. / Golden Gate Ave, San Francisco / Seoul National Univ / Carnegie Mellon / UC Berkeley / DevOps / Deep Learning / Visualization