Navigation

This page is a work in progress; errors may exist, and additional
content is forthcoming.

So far we’ve built a simple contact manager, and added support for a
related model (Addresses). This has shown how to use many of the
basics, but there are a few more things you’d want before exposing
this to the outside world. One of those is authentication and
authorization. Django includes support that works for many projects,
which is what we’ll use.

In order to use the included authentication support, the
django.contrib.auth and django.contrib.sessions applications
needs to be included in your project.

Django enables thes by default when you create a project, as you can
see in addressbook/settings.py.

INSTALLED_APPS=('django.contrib.auth','django.contrib.contenttypes','django.contrib.sessions','django.contrib.sites','django.contrib.messages','django.contrib.staticfiles',# Uncomment the next line to enable the admin:# 'django.contrib.admin',# Uncomment the next line to enable admin documentation:# 'django.contrib.admindocs','contacts',)

In addition to installing the application, the middleware needs to be
installed, as well.

MIDDLEWARE_CLASSES=('django.middleware.common.CommonMiddleware','django.contrib.sessions.middleware.SessionMiddleware','django.middleware.csrf.CsrfViewMiddleware','django.contrib.auth.middleware.AuthenticationMiddleware','django.contrib.messages.middleware.MessageMiddleware',# Uncomment the next line for simple clickjacking protection:# 'django.middleware.clickjacking.XFrameOptionsMiddleware',)

If you’ll recall, during the first run of syncdb, Django asked if
we wanted to create a superuser account. It did so because we had the
application installed already.

The stock Django auth model supports Users, Groups, and
Permissions. This is usually sufficient unless you’re integrating
with an existing authentication backend.

django.contrib.auth provides a set of views to support the basic
authentication actions such as login, logout, password reset, etc.
Note that it includes views, but not templates. We’ll need to
provide those for our project.

For this example we’ll just add support for login and logout views in
our project. First, add the views to addressbook/urls.py.

Both the login and logout view have default template names
(registration/login.html and registration/logged_out.html,
respectively). Because these views are specific to our project and not
our re-usable Contacts application, we’ll create a new
templates/registration directory inside of addressbook:

$ mkdir -p addressbook/templates/registration

And tell Django to look in that directory for templates by setting
TEMPLATE_DIRS in addressbook/settings.py.

TEMPLATE_DIRS=(# Put strings here, like "/home/html/django_templates" or "C:/www/django/templates".# Always use forward slashes, even on Windows.# Don't forget to use absolute paths, not relative paths.'addressbook/templates',)

The login template inherits from our base.html template, and shows
the login form provided by the view. The hidden next field allows
the view to redirect the user to the page requested, if the login
request was triggered by a permission failure.

All it needs to do is provide a message to let the user know the
logout was successful.

Creating an Admin User

XXX

If you run your development server now using runserver and visit
http://localhost:8000/login, you’ll see the login page. If you
login with bogus credentials, you should see an error message. So
let’s try logging in with the super user credential you created earlier.

Wait, what? Why is it visiitng /accounts/profile? We never typed
that. The login view wants to redirect the user to a fixed URL after a
successful login, and the default is /accounts/profile. To
override that, we’ll set the LOGIN_REDIRECT_URL value in
addressbook/settings.py so that once a user logs in they’ll be
redirected to the list of contacts.

LOGIN_REDIRECT_URL='/'

Now that we can log in and log out, it’d be nice to show the logged in
user in the header and links to login/logout in the header. We’ll add
that to our base.html template, since we want that to show up
everywhere.

Having support for login and logout is nice, but we’re not actually
using it right now. So we want to first make our Contact views only
available to authenticated users, and then we’ll go on to associated
contacts with specific Users, so the application could be used for
multiple users.

Django includes a suite a functions and decorators that help you guard
a view based on authentication/authorization. One of the most commonly
used is login_required. Unfortunately, applying view decorators to
class based views remains a little cumbersome. There are
essentially two methods: decorating the URL configuration, and
decorating the class. I’ll show how to decorate the class.

Class based views have a dispatch() method that’s called when an
URL pattern matches. The dispatch() method looks up the
appropriate method on the class based on the HTTP method and then
calls it. Because we want to protect the views for all HTTP methods,
we’ll override and decorate that.

In contacts/views.py we’ll create a class mixin that ensures the
user is logged in.

This is a mixin because it doesn’t provide a full implementation of
a view on its own; it needs to be mixed with another view to have an
effect.

Once we have it, we can add it to the class declarations in
contacts/views.py. Each view will have our new LoggedInMixin
added as the first superclass. For example, ListContactView will
look as follows.

Just as LOGIN_REDIRECT_URL tells Django where to send people
after they log in, there’s a setting to control where to send them
when they need to login. However, this can also be a view name, so
we don’t have to bake an explicit URL into the settings.

The remaining views are responsible for showing only a single object
– the Contact (or its addresses). For those we’ll create another
mixin that enforces authorization.

fromdjango.core.exceptionsimportPermissionDenied...classContactOwnerMixin(object):defget_object(self,queryset=None):"""Returns the object the view is displaying. """ifquerysetisNone:queryset=self.get_queryset()pk=self.kwargs.get(self.pk_url_kwarg,None)queryset=queryset.filter(pk=pk,owner=self.request.user,)try:obj=queryset.get()exceptObjectDoesNotExist:raisePermissionDeniedreturnobj

ContactOwnerMixin overrides the get_object() method, which is
responsible for getting the object for a view to operate on. If it
can’t find one with the specified primary key and owner, it raises the
PermissionDenied exception.

Note

This implementation will return HTTP 403 (Forbidden) whenever it
cannot find the a Contact with the requested ID and owner. This
will mask legitimate 404 (Not Found) errors.

We’ll use the ContactOwnerMixin in all of our views. For example,
ContactView will look as follows:

Note that the order of inheritance is important: the superclasses
(LoggedInMixin, ContactOwnerMixin, DetailView) will be
checked in the order listed for methods. By placing LoggedInMixin
first, you’re guaranteed that by the time execution reaches
ContactOwnerMixin and DetailView, you have a logged in,
authenticated user.