Skip to content

Commit

Permalink
Added first draft of custom User docs.
Browse files Browse the repository at this point in the history
Thanks to Greg Turner for the initial text.
  • Loading branch information
freakboy3742 committed Sep 8, 2012
1 parent 75118bd commit e6aaf65
Showing 1 changed file with 237 additions and 1 deletion.
238 changes: 237 additions & 1 deletion docs/topics/auth.txt
Expand Up @@ -57,6 +57,10 @@ API reference
Fields
~~~~~~

TODO document which attributes/methods come from AbstractBaseUser
TODO tone down references to get_profile - it's not the best way of doing things
any more.

.. class:: models.User

:class:`~django.contrib.auth.models.User` objects have the following
Expand Down Expand Up @@ -1714,7 +1718,239 @@ Fields
Customizing the User model
==========================

TODO
.. versionadded:: 1.5

Some kinds of projects may have authentication requirements for which Django's
built-in :class:`~django.contrib.auth.models.User` model is not always
appropriate. For instance, on some sites it makes more sense to use an email
address as your identification token instead of a username.

Django allows you to override the default User model by providing a value for
the :setting:`AUTH_USER_MODEL` setting that references a custom model::

AUTH_USER_MODEL = 'myapp.MyUser'

This dotted pair describes the name of the Django app, and the name of the Django
model that you wish to use as your User model.

.. admonition:: Warning

Changing :setting:`AUTH_USER_MODEL` has a big effect on your database
structure. It changes the tables that are available, and it will affect the
construction of foreign keys and many-to-many relationships. If you intend
to set :setting:`AUTH_USER_MODEL`, you should set it before running
``manage.py syncdb`` for the first time.

Referencing the User model
--------------------------

If you reference :class:`~django.contrib.auth.models.User` directly (for
example, by referring to it in a foreign key), your code will not work in
projects where the :setting:`AUTH_USER_MODEL` setting has been changed to a
different User model.

Instead of referring to :class:`~django.contrib.auth.models.User` directly,
you should reference the user model using
:meth:`~django.contrib.auth.get_user_model()`. This method will return the
currently active User model -- the custom User model if one is specified, or
:class:`~django.contrib.auth.User` otherwise.

In relations to the User model, you should specify the custom model using
the :setting:`AUTH_USER_MODEL` setting. For example::

from django.conf import settings
from django.db import models

class Article(models.Model)
author = models.ForeignKey(settings.AUTH_USER_MODEL)

Specifying a custom User model
------------------------------

.. admonition:: Model design considerations

Think carefully before handling information not directly related to
authentication in your custom User Model.

It may be better to store app-specific user information in a model
that has a relation with the User model. That allows each app to specify
its own user data requirements without risking conflicts with other
apps. On the other hand, queries to retrieve this related information
will involve a database join, which may have an effect on performance.

Django expects your custom User model to meet some minimum requirements. The
easiest way to construct a compliant custom User model is to inherit from
:class:`~django.contrib.auth.models.AbstractBaseUser` and provide some key
definitions:

.. attribute:: User.USERNAME_FIELD

A string describing the name of the field on the User model that is
used as the unique identifier. This will usually be a username of
some kind, but it can also be an email address, or any other unique
identifier.

.. method:: User.get_full_name():

A longer formal identifier for the user. A common interpretation
would be the full name name of the user, but it can be any string that
identifies the user.

.. method:: User.get_short_name():

A short, informal identifier for the user. A common interpretation
would be the first name of the user, but it can be any string that
identifies the user in an informal way. It may also return the same
value as :meth:`django.contrib.auth.User.get_full_name()`.

You should also define a custom manager for your User model. If your User
model defines `username` and `email` fields the same as Django's default User,
you can just install Django's
:class:`~django.contrib.auth.models.UserManager`; however, if your User model
defines different fields, you will need to define a custom manager with 2
methods.

.. method:: UserManager.create_user(username, password=None, **other_fields)

The prototype of `create_user()` should accept all required fields
as arguments. For example, if your user model defines `username`,
and `date_of_birth` as required fields, then create_user should be
defined as::

def create_user(self, username, date_of_birth, password=None):
# create user here

.. method:: UserManager.create_superuser(username, password, **other_fields)

The prototype of `create_superuser()` should accept all required fields
as arguments. For example, if your user model defines `username`,
and `date_of_birth` as required fields, then create_user should be
defined as::

def create_superuser(self, username, date_of_birth, password):
# create superuser here

Unlike `create_user()`, `create_superuser()` *must* require the caller
to provider a password.

Custom users and django.contrib.admin
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

If you want your custom User model to also work with Admin, your User model must
define some additional attributes and methods. These methods allow the admin to
control access of the User to admin content:

.. attribute:: User.is_staff

Returns True if the user is a member of staff.

.. attribute:: User.is_active

Returns True if the user account is currently active.

.. method:: User.has_perm(perm, obj=None):

Returns True if the user has the named permission. If `obj` is
provided, the permission needs to be checked against a specific object
instance.

.. method:: User.has_module_perms(app_label):

Returns True if the user has permission to access models in
the given app.


Worked Example
~~~~~~~~~~~~~~

As a worked example, here is a full models.py for an admin-compliant custom
user app. This user model uses an email address as the username, and has a
required date of birth; it provides no permission checking, beyond a simple
`admin` flag on the user account::

from django.db import models
from django.contrib.auth.models import (
BaseUserManager, AbstractBaseUser
)


class MyUserManager(BaseUserManager):
def create_user(self, email, date_of_birth, password=None):
"""
Creates and saves a User with the given email, date of
birth and password.
"""
if not email:
raise ValueError('Users must have an email address')

user = self.model(
email=MyUserManager.normalize_email(email),
date_of_birth=date_of_birth,
)

user.set_password(password)
user.save(using=self._db)
return user

def create_superuser(self, username, date_of_birth, password):
"""
Creates and saves a superuser with the given email, date of
birth and password.
"""
u = self.create_user(username,
password=password,
date_of_birth=date_of_birth
)
u.is_admin = True
u.save(using=self._db)
return u


class MyUser(AbstractBaseUser):
email = models.EmailField(
verbose_name='email address',
max_length=255
)
date_of_birth = models.DateField()
is_admin = models.BooleanField(default=False)

objects = MyUserManager()

USERNAME_FIELD = 'email'

def get_full_name(self):
# The user is identified by their email address
return self.email

def get_short_name(self):
# The user is identified by their email address
return self.email

def __unicode__(self):
return self.email

def has_perm(self, perm, obj=None):
"Does the user have a specific permission?"
# Simplest possible answer: Yes, always
return True

def has_module_perms(self, app_label):
"Does the user have permissions to view the app `app_label`?"
# Simplest possible answer: Yes, always
return True

@property
def is_staff(self):
"Is the user a member of staff?"
# Simplest possible answer: All admins are staff
return self.is_admin

@property
def is_active(self):
"Is the user account currently active?"
# Simplest possible answer: User is always active
return True


.. _authentication-backends:

Expand Down

0 comments on commit e6aaf65

Please sign in to comment.