Django 1.8.2 文档

在Django中自定义身份验证

Django 自带的认证系统足够应付大多数情况,但你或许不打算使用现成的认证系统。定制自己的项目的权限系统需要了解哪些一些关键点,即Django中哪些部分是能够扩展或替换的。这个文档提供了如何定制权限系统的细节。

“认证”后端 在以下情形时可被扩展:当一个 User 模型对象带有用户名和密码时,且需要有别于 Django 默认的认证功能。

你可为你的模型提供可通过 Django 权限系统检查的 定制的权限

你能够扩展 默认的 User 模型,或实现 一个完全定制的模型。

其他认证源

有时候你需要挂接到其他认证资源 -- 另一包含用户名,密码的数据源或者其他认证方法。

例如,您的公司可能已经有一个LDAP设置存储了每一位员工的用户名和密码。 对于一个在LDAP和Django网站都拥有账号的用户来说,如果他/她不能使用LDAP账号登录Django网站,对他/她以及网站管理员来说都是一件麻烦事。

为了解决类似情形,django的认证系统允许你添加其他认证方式。您可以覆盖Django的基于数据库的默认方案,也可以连接使用其他系统的认证服务。

See the authentication backend reference for information on the authentication backends included with Django.

指定认证后端

在底层,Django 维护一个“authentication backends”的列表。当调用django.contrib.auth.authenticate() 时 —— 如何登入一个用户 中所描述的 —— Django 会尝试所有的认证后台进行认证。如果第一个认证方法失败,Django 将尝试第二个,以此类推,直至试完所有的认证后台。

使用的认证后台通过AUTHENTICATION_BACKENDS 设置指定。它应该是一个包含Python 路径名称的元组,它们指向的Python 类知道如何进行验证。这些类可以位于Python 路径上任何地方。

默认情况下,AUTHENTICATION_BACKENDS 设置为:

('django.contrib.auth.backends.ModelBackend',)

这个基本的认证后台会检查Django 的用户数据库并查询内建的权限。它不会通过任何的速率限制机制防护暴力破解。你可以在自定义的认证后端中实现自己的速率控制机制,或者使用大部分Web 服务器提供的机制。

AUTHENTICATION_BACKENDS 的顺序很重要,所以如果用户名和密码在多个后台中都是合法的,Django 将在第一个匹配成功后停止处理。

如果后台引发PermissionDenied 异常,认证将立即失败。Django 不会检查后面的认证后台。

一旦用户被认证过,Django会在用户的session中存储他使用的认证后端,然后在session有效期中一直会为该用户提供此后端认证。这种高效意味着验证源被缓存基于per-session基础, 所以如果你改变 AUTHENTICATION_BACKENDS, 如果你需要迫使用户重新认证,需要清除掉 session 数据.一个简单的方式是使用这个方法: Session.objects.all().delete().

编写一个认证后端

一个认证后端是个实现两个方法的类: get_user(user_id) and authenticate(**credentials),以及一组相关的可选权限的认证方法

get_user 方法要求一个参数 user_id –这个参数可以是用户名,数据库中的ID或其它标识 User 对象的主键– 方法返回一个 User 对象.

身份验证 方法使用凭据作为关键字参数。大多数情况下,代码如下︰

class MyBackend(object):
    def authenticate(self, username=None, password=None):
        # Check the username/password and return a User.
        ...

当然,它也可以接收token的方式作为参数,例如:

class MyBackend(object):
    def authenticate(self, token=None):
        # Check the token and return a User.
        ...

不管怎样, authenticate 至少应该检查凭证, 如果凭证合法,它应该返回一个匹配于登录信息的 User 实例。如果不合法,则返回 None.

正如文档开头所描述的,Django的 admin 与Django User  对象是紧耦合的。到目前为止, 最好的解决方法是给每一个在你后台的用户创建一个 User 对象 (e.g., in your LDAP directory, your external SQL database, etc.) 你可以先写一个脚本来做这件事, 或者用你的 authenticate 方法在用户登陆的时候完成这件事。

这里有一个例子,后台对你定义在 settings.py 文件里的用户和密码进行验证,并且在用第一次验证的时候创建一个 User 对象:

from django.conf import settings
from django.contrib.auth.models import User, check_password

class SettingsBackend(object):
    """
    Authenticate against the settings ADMIN_LOGIN and ADMIN_PASSWORD.

    Use the login name, and a hash of the password. For example:

    ADMIN_LOGIN = 'admin'
    ADMIN_PASSWORD = 'sha1$4e987$afbcf42e21bd417fb71db8c66b321e9fc33051de'
    """

    def authenticate(self, username=None, password=None):
        login_valid = (settings.ADMIN_LOGIN == username)
        pwd_valid = check_password(password, settings.ADMIN_PASSWORD)
        if login_valid and pwd_valid:
            try:
                user = User.objects.get(username=username)
            except User.DoesNotExist:
                # Create a new user. Note that we can set password
                # to anything, because it won't be checked; the password
                # from settings.py will.
                user = User(username=username, password='get from settings.py')
                user.is_staff = True
                user.is_superuser = True
                user.save()
            return user
        return None

    def get_user(self, user_id):
        try:
            return User.objects.get(pk=user_id)
        except User.DoesNotExist:
            return None

在定制后端中处理授权

自定义验证后端能提供自己的权限。

当认证后端完成了这些功能 (get_group_permissions(), get_all_permissions(), has_perm(), and has_module_perms()) 那么user model就会给它授予相对应的许可。

提供给用户的权限将是所有后端返回的所有权限的超集。也就是说,只要任意一个backend授予了一个user权限,django就给这个user这个权限。

New in Django 1.8:

如果后端在has_perm()has_module_perms()中引发PermissionDenied异常,授权将立即失败,Django不会检查接下来的后端认证。

上述的简单backend可以相当容易的完成授予admin权限。

class SettingsBackend(object):
    ...
    def has_perm(self, user_obj, perm, obj=None):
        if user_obj.username == settings.ADMIN_LOGIN:
            return True
        else:
            return False

在上例中,授予了用户所有访问权限。注意, 由于django.contrib.auth.models.User 同名函数将接收同样的参数,认证后台接收到的 user_obj,有可能是匿名用户 anonymous

一个完整的认证过程,可以参考 ModelBackend类,它位于django/contrib/auth/backends.py,ModelBackend是默认的认证后台,并且大多数情况下会对auth_permission表进行查询。  如果你想对后台API提供自定义行为,你可以利用Python继承的优势,继承ModelBackend并自定义后台API

授权匿名用户

匿名用户是指不经过身份验证即他们有没有提供有效的身份验证细节。然而,这并不一定意味着他们不被授权做任何事情。At the most basic level, most Web sites authorize anonymous users to browse most of the site, and many allow anonymous posting of comments etc.

Django 的权限框架没有一个地方来存储匿名用户的权限。然而,传递给身份验证后端的用户对象可能是 django.contrib.auth.models.AnonymousUser 对象,该对象允许后端指定匿名用户自定义的授权行为。这对可重用应用的作者是很有用的, 因为他可以委托所有的请求, 例如控制匿名用户访问,给这个认证后端, 而不需要设置它

Authorization for inactive users

An inactive user is a one that is authenticated but has its attribute is_active set to False. 然而这并不意味着他们无权做任何事情。例如他们可以被允许激活他们的帐户。

The support for anonymous users in the permission system allows for a scenario where anonymous users have permissions to do something while inactive authenticated users do not.

Do not forget to test for the is_active attribute of the user in your own backend permission methods.

操作对象权限

django的权限框架对对象权限有基础的支持, 尽管在它的核心没有实现它.这意味着对象权限检查将始终返回 False 或空列表 (取决于检查的行为)。一个认证后端将传递关键字参数objuser_obj 给每一个对象相关的认证方法, 并且能够返回适当的对象级别的权限.

自定义权限

要为给定模型对象创建自定义权限,请使用权限 模型元属性

此示例任务模型创建三个自定义权限,即用户是否可以对您的应用程序任务实例执行操作:

class Task(models.Model):
    ...
    class Meta:
        permissions = (
            ("view_task", "Can see available tasks"),
            ("change_task_status", "Can change the status of tasks"),
            ("close_task", "Can remove a task by setting its status as closed"),
        )

唯一需要做的就是在运行manage.py migrate时创建这些额外的权限。当用户尝试访问应用程序提供的功能(查看任务,更改任务状态,关闭任务)时,您的代码负责检查这些权限的值。继续上面的示例,以下检查用户是否可以查看任务:

user.has_perm('app.view_task')

扩展已有的用户模型

有两种方法来扩展默认的User模型,而不用替换你自己的模型。 如果你需要的只是行为上的改变,而不需要对数据库中存储的内容做任何改变,你可以创建基于User代理模型代理模型提供的功能包括默认的排序、自定义管理器以及自定义模型方法。

如果你想存储新字段到已有的User里,那么你可以选择one-to-one relationship来扩展用户信息。这种 one-to-one 模型一般被称为资料模型(profile model),它通常被用来存储一些有关网站用户的非验证性( non-auth )资料。例如,你可以创建一个员工模型 (Employee model):

from django.contrib.auth.models import User

class Employee(models.Model):
    user = models.OneToOneField(User)
    department = models.CharField(max_length=100)

假设一个员工Fred Smith 既有User 模型又有Employee 模型,你可以使用Django 标准的关联模型访问相关联的信息:

>>> u = User.objects.get(username='fsmith')
>>> freds_department = u.employee.department

要将个人资料模型的字段添加到管理后台的用户页面中,请在应用程序的admin.py定义一个InlineModelAdmin(对于本示例,我们将使用StackedInline )并将其添加到UserAdmin类并向User类注册的:

from django.contrib import admin
from django.contrib.auth.admin import UserAdmin
from django.contrib.auth.models import User

from my_user_profile_app.models import Employee

# Define an inline admin descriptor for Employee model
# which acts a bit like a singleton
class EmployeeInline(admin.StackedInline):
    model = Employee
    can_delete = False
    verbose_name_plural = 'employee'

# Define a new User admin
class UserAdmin(UserAdmin):
    inlines = (EmployeeInline, )

# Re-register UserAdmin
admin.site.unregister(User)
admin.site.register(User, UserAdmin)

这些Profile models在任何方面都不特殊,它们就是和User model多了一个一对一链接的普通Django models。 这种情形下,它们不会在一名用户创建时自动创建, 但是 django.db.models.signals.post_save 可以在适当的时候用于创建或更新相关模型。

注意使用相关模型的成果需另外的查询或者联结来获取相关数据,基于你的需求替换用户模型并添加相关字段可能是你更好的选择。但是,在你项目应用程序中,指向默认用户模型的链接可能带来额外的数据库负载。

重写用户模型

Django 内建的User 模型可能不适合某些类型的项目。例如,在某些网站上使用邮件地址而不是用户名作为身份的标识可能更合理。

Django 允许你通过AUTH_USER_MODEL 设置覆盖默认的User 模型, 其值引用一个自定义的模型。

AUTH_USER_MODEL = 'myapp.MyUser'

上面的值表示Django 应用的名称(必须位于INSTALLED_APPS 中) 和你想使用的User 模型的名称。

注意

改变 AUTH_USER_MODEL 对你的数据库结构有很大的影响。它改变了一些会使用到的表格,并且会影响到一些外键和多对多关系的构造。如果你打算设置AUTH_USER_MODEL, 你应该在创建任何迁移或者第一次运行 manage.py migrate 前设置它。

在你有表格被创建后更改此设置是不被makemigrations支持的,并且会导致你需要手动修改数据库结构,从旧用户表中导出数据,可能重新应用一些迁移。

警告

由于Django的可交换模型的动态依赖特性的局限, 你必须确保 AUTH_USER_MODEL引用的模型在所属app中第一个迁移文件中被创建(通常命名为 0001_initial); 否则, 你会碰到错误.

此外,在运行迁移时可能会遇到CircularDependencyError,因为Django由于动态依赖性而无法自动断开依赖性循环。If you see this error, you should break the loop by moving the models depended on by your User model into a second migration (you can try making two normal models that have a ForeignKey to each other and seeing how makemigrations resolves that circular dependency if you want to see how it’s usually done)

Reusable apps and AUTH_USER_MODEL

Reusable apps shouldn’t implement a custom user model. A project may use many apps, and two reusable apps that implemented a custom user model couldn’t be used together. If you need to store per user information in your app, use a ForeignKey or OneToOneField to settings.AUTH_USER_MODEL as described below.

引用User 模型

AUTH_USER_MODEL 设置改成其它用户模型的项目中,如果你直接引用User(例如,通过一个外键引用它),你的代码将不能工作。

get_user_model()[source]

你应该使用django.contrib.auth.get_user_model() 来引用用户模型,而不要直接引用User这个方法将返回当前正在使用的用户模型 —— 指定的自定义用户模型或者User

当你定义一个外键或者到用户模型的多对多关系时,你应该使用 AUTH_USER_MODEL 设置来指定自定义的模型。例如:

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

class Article(models.Model):
    author = models.ForeignKey(settings.AUTH_USER_MODEL)
New in Django 1.7:

连接用户模型发出的信号时, 应该使用AUTH_USER_MODEL 设置指定自定义的模型。例如:

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

def post_save_receiver(sender, instance, created, **kwargs):
    pass

post_save.connect(post_save_receiver, sender=settings.AUTH_USER_MODEL)

一般来说,在导入时候执行的代码中,你应该使用AUTH_USER_MODEL 设置引用用户模型。get_user_model() 只在Django 已经导入所有的模型后才工作。

指定自定义的用户模型

模型设计考虑

处理不直接相关的认证在自定义用户模型信息之前,应仔细考虑。

这可能是更好的存储应用程序特定的用户信息在与用户模式的关系的典范。这使得每一个应用,而不用担心与其他应用程序冲突指定自己的用户数据需求.另一方面,查询来检索此相关的信息将涉及的数据库连接,这可能对性能有影响。

Django 期望你自定义的  User model 满足一些最低要求

  1. 模型必须有一个唯一的字段可被用于识别目的。可以是一个用户名,电子邮件地址,或任何其它独特属性。
  2. 你的模型必须提供一种方法可以在"short"and"long"form可以定位到用户。最普遍的方法是用用户的名来作为简称,用用户的全名来作为全称。然而,对这两种方式没有特定的要求,如果你想,他们可以返回完全相同的值。
Changed in Django 1.8:

Django的旧版本要求你的模型有一个整数主键也是如此。

创建一个规范的自定义模型最简单的方法是继承AbstractBaseUserAbstractBaseUser provides the core implementation of a User model, including hashed passwords and tokenized password resets. You must then provide some key implementation details:

class models.CustomUser
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. The field must be unique (i.e., have unique=True set in its definition).

In the following example, the field identifier is used as the identifying field:

class MyUser(AbstractBaseUser):
    identifier = models.CharField(max_length=40, unique=True)
    ...
    USERNAME_FIELD = 'identifier'
New in Django 1.8.

USERNAME_FIELD now supports ForeignKeys. Since there is no way to pass model instances during the createsuperuser prompt, expect the user to enter the value of to_field value (the primary_key by default) of an existing instance.

REQUIRED_FIELDS

当通过createsuperuser管理命令创建一个用户时,用于提示的一个字段名称列表.。将会提示给列表里面的每一个字段提供一个值。 它包含的必须是 为 False 或者blank未定义的字段, 也可包含你想要在交互地创建一个新的用户时想要展示的其他字段。REQUIRED_FIELDS has no effect in other parts of Django, like creating a user in the admin.

For example, here is the partial definition for a User model that defines two required fields - a date of birth and height:

class MyUser(AbstractBaseUser):
    ...
    date_of_birth = models.DateField()
    height = models.FloatField()
    ...
    REQUIRED_FIELDS = ['date_of_birth', 'height']

Note

REQUIRED_FIELDS必须包含User模型中的所有必填字段,但不应包含USERNAME_FIELDpassword,因为将始终提示输入这些字段。

New in Django 1.8.

REQUIRED_FIELDS now supports ForeignKeys. Since there is no way to pass model instances during the createsuperuser prompt, expect the user to enter the value of to_field value (the primary_key by default) of an existing instance.

is_active

A boolean attribute that indicates whether the user is considered “active”. This attribute is provided as an attribute on AbstractBaseUser defaulting to True. How you choose to implement it will depend on the details of your chosen auth backends. See the documentation of the is_active attribute on the built-in user model for details.

get_full_name()

用户更长且正式的标识.常见的解释会是用户的完整名称,但它可以是任何字符串,用于标识用户。

get_short_name()

一个短的且非正式用户的标识符。常见的解释会是第一个用户的名称,但它可以是任意字符串,用于以非正式的方式标识用户。它也可能会返回与django.contrib.auth.models.User.get_full_name()相同的值。

The following methods are available on any subclass of AbstractBaseUser:

class models.AbstractBaseUser
get_username()

Returns the value of the field nominated by USERNAME_FIELD.

is_anonymous()

Always returns False. This is a way of differentiating from AnonymousUser objects. Generally, you should prefer using is_authenticated() to this method.

is_authenticated()

Always returns True. This is a way to tell if the user has been authenticated. This does not imply any permissions, and doesn’t check if the user is active - it only indicates that the user has provided a valid username and password.

set_password(raw_password)

Sets the user’s password to the given raw string, taking care of the password hashing. Doesn’t save the AbstractBaseUser object.

When the raw_password is None, the password will be set to an unusable password, as if set_unusable_password() were used.

check_password(raw_password)

Returns True if the given raw string is the correct password for the user. (This takes care of the password hashing in making the comparison.)

set_unusable_password()

Marks the user as having no password set. This isn’t the same as having a blank string for a password. check_password() for this user will never return True. Doesn’t save the AbstractBaseUser object.

You may need this if authentication for your application takes place against an existing external source such as an LDAP directory.

has_usable_password()

Returns False if set_unusable_password() has been called for this user.

get_session_auth_hash()
New in Django 1.7.

Returns an HMAC of the password field. Used for Session invalidation on password change.

你应该再为你的User模型自定义一个管理器。 如果你的User模型定义了这些字段:username, email, is_staff, is_active, is_superuser, last_login, and date_joined跟默认的User的字段是一样的话, 那么你就使用Django的UserManager就行了;总之, 如果你的User定义了不同的字段, 你就要去自定义一个管理器,它继承自BaseUserManager并提供两个额外的方法:

class models.CustomUserManager
create_user(*username_field*, password=None, **other_fields)

 create_user() 原本接受username,以及其它所有必填字段作为参数。例如,如果你的user模型使用 email 作为username字段, 并且使用 date_of_birth 作为必填字段, 那么create_user 应该定义为:

def create_user(self, email, date_of_birth, password=None):
    # create user here
    ...
create_superuser(*username_field*, password, **other_fields)

The prototype of create_superuser() should accept the username field, plus all required fields as arguments. For example, if your user model uses email as the username field, and has date_of_birth as a required field, then create_superuser should be defined as:

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

Unlike create_user(), create_superuser() must require the caller to provide a password.

BaseUserManager provides the following utility methods:

class models.BaseUserManager
normalize_email(email)

一个通过将部分电子邮箱地址转为小写来使其规范化的类方法 classmethod 

get_by_natural_key(username)

Retrieves a user instance using the contents of the field nominated by USERNAME_FIELD.

make_random_password(length=10, allowed_chars='abcdefghjkmnpqrstuvwxyzABCDEFGHJKLMNPQRSTUVWXYZ23456789')

Returns a random password with the given length and given string of allowed characters. Note that the default value of allowed_chars doesn’t contain letters that can cause user confusion, including:

  • i, l, I, and 1 (lowercase letter i, lowercase letter L, uppercase letter i, and the number one)
  • o, O, and 0 (lowercase letter o, uppercase letter o, and zero)

Extending Django’s default User

如果你完全满意Django 的 用户 模型且你只想添加一些额外的配置文件信息,您可以简单的继承 django.contrib.auth.models.AbstractUser 并添加您的自定义字段,尽管我们建议像"Model design considerations"中描述的 Specifying a custom User model那样, 使用一个单独的模型。AbstractUser作为一种 抽象模型 提供默认 用户 的完整实现。

自定义用户与内置身份验证表单

As you may expect, built-in Django’s forms and views make certain assumptions about the user model that they are working with.

If your user model doesn’t follow the same assumptions, it may be necessary to define a replacement form, and pass that form in as part of the configuration of the auth views.

Custom users and django.contrib.admin

如果你想让你自定义的User模型也可以在站点管理上工作,那么你的模型应该再定义一些额外的属性和方法。 这些方法允许管理员去控制User到管理内容的访问:

class models.CustomUser
is_staff

Returns True if the user is allowed to have access to the admin site.

is_active

Returns True if the user account is currently active.

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.

has_module_perms(app_label):

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

You will also need to register your custom User model with the admin. If your custom User model extends django.contrib.auth.models.AbstractUser, you can use Django’s existing django.contrib.auth.admin.UserAdmin class. However, if your User model extends AbstractBaseUser, you’ll need to define a custom ModelAdmin class. 它可以是默认类型django.contrib.auth.admin.UserAdmin的子类;however, you’ll need to override any of the definitions that refer to fields on django.contrib.auth.models.AbstractUser that aren’t on your custom User class.

自定义用户和权限

To make it easy to include Django’s permission framework into your own User class, Django provides PermissionsMixin. This is an abstract model you can include in the class hierarchy for your User model, giving you all the methods and database fields necessary to support Django’s permission model.

PermissionsMixin provides the following methods and attributes:

class models.PermissionsMixin
is_superuser

Boolean. Designates that this user has all permissions without explicitly assigning them.

get_group_permissions(obj=None)

Returns a set of permission strings that the user has, through their groups.

If obj is passed in, only returns the group permissions for this specific object.

get_all_permissions(obj=None)

Returns a set of permission strings that the user has, both through group and user permissions.

If obj is passed in, only returns the permissions for this specific object.

has_perm(perm, obj=None)

Returns True if the user has the specified permission, where perm is in the format "<app label>.<permission codename>" (see permissions). If the user is inactive, this method will always return False.

If obj is passed in, this method won’t check for a permission for the model, but for this specific object.

has_perms(perm_list, obj=None)

Returns True if the user has each of the specified permissions, where each perm is in the format "<app label>.<permission codename>". If the user is inactive, this method will always return False.

If obj is passed in, this method won’t check for permissions for the model, but for the specific object.

has_module_perms(package_name)

Returns True if the user has any permissions in the given package (the Django app label). If the user is inactive, this method will always return False.

ModelBackend

If you don’t include the PermissionsMixin, you must ensure you don’t invoke the permissions methods on ModelBackend. ModelBackend assumes that certain fields are available on your user model. If your User model doesn’t provide those fields, you will receive database errors when you check permissions.

Custom users and Proxy models

One limitation of custom User models is that installing a custom User model will break any proxy model extending User. Proxy models must be based on a concrete base class; by defining a custom User model, you remove the ability of Django to reliably identify the base class.

If your project uses proxy models, you must either modify the proxy to extend the User model that is currently in use in your project, or merge your proxy’s behavior into your User subclass.

Custom users and testing/fixtures

If you are writing an application that interacts with the User model, you must take some precautions to ensure that your test suite will run regardless of the User model that is being used by a project. Any test that instantiates an instance of User will fail if the User model has been swapped out. This includes any attempt to create an instance of User with a fixture.

To ensure that your test suite will pass in any project configuration, django.contrib.auth.tests.utils defines a @skipIfCustomUser decorator. This decorator will cause a test case to be skipped if any User model other than the default Django user is in use. This decorator can be applied to a single test, or to an entire test class.

Depending on your application, tests may also be needed to be added to ensure that the application works with any user model, not just the default User model. To assist with this, Django provides two substitute user models that can be used in test suites:

class tests.custom_user.CustomUser

A custom user model that uses an email field as the username, and has a basic admin-compliant permissions setup

class tests.custom_user.ExtensionUser

A custom user model that extends django.contrib.auth.models.AbstractUser, adding a date_of_birth field.

You can then use the @override_settings decorator to make that test run with the custom User model. For example, here is a skeleton for a test that would test three possible User models – the default, plus the two User models provided by auth app:

from django.contrib.auth.tests.utils import skipIfCustomUser
from django.contrib.auth.tests.custom_user import CustomUser, ExtensionUser
from django.test import TestCase, override_settings


class ApplicationTestCase(TestCase):
    @skipIfCustomUser
    def test_normal_user(self):
        "Run tests for the normal user model"
        self.assertSomething()

    @override_settings(AUTH_USER_MODEL='auth.CustomUser')
    def test_custom_user(self):
        "Run tests for a custom user model with email-based authentication"
        self.assertSomething()

    @override_settings(AUTH_USER_MODEL='auth.ExtensionUser')
    def test_extension_user(self):
        "Run tests for a simple extension of the built-in User."
        self.assertSomething()

一个完整例子

这是一个管理器允许的自定义user这个用户模型使用邮箱地址作为用户名,并且要求填写出生年月。it provides no permission checking, beyond a simple admin flag on the user account. This model would be compatible with all the built-in auth forms and views, except for the User creation forms. This example illustrates how most of the components work together, but is not intended to be copied directly into projects for production use.

This code would all live in a models.py file for a custom authentication app:

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=self.normalize_email(email),
            date_of_birth=date_of_birth,
        )

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

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


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

    objects = MyUserManager()

    USERNAME_FIELD = 'email'
    REQUIRED_FIELDS = ['date_of_birth']

    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 __str__(self):              # __unicode__ on Python 2
        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

Then, to register this custom User model with Django’s admin, the following code would be required in the app’s admin.py file:

from django import forms
from django.contrib import admin
from django.contrib.auth.models import Group
from django.contrib.auth.admin import UserAdmin
from django.contrib.auth.forms import ReadOnlyPasswordHashField

from customauth.models import MyUser


class UserCreationForm(forms.ModelForm):
    """A form for creating new users. Includes all the required
    fields, plus a repeated password."""
    password1 = forms.CharField(label='Password', widget=forms.PasswordInput)
    password2 = forms.CharField(label='Password confirmation', widget=forms.PasswordInput)

    class Meta:
        model = MyUser
        fields = ('email', 'date_of_birth')

    def clean_password2(self):
        # Check that the two password entries match
        password1 = self.cleaned_data.get("password1")
        password2 = self.cleaned_data.get("password2")
        if password1 and password2 and password1 != password2:
            raise forms.ValidationError("Passwords don't match")
        return password2

    def save(self, commit=True):
        # Save the provided password in hashed format
        user = super(UserCreationForm, self).save(commit=False)
        user.set_password(self.cleaned_data["password1"])
        if commit:
            user.save()
        return user


class UserChangeForm(forms.ModelForm):
    """A form for updating users. Includes all the fields on
    the user, but replaces the password field with admin's
    password hash display field.
    """
    password = ReadOnlyPasswordHashField()

    class Meta:
        model = MyUser
        fields = ('email', 'password', 'date_of_birth', 'is_active', 'is_admin')

    def clean_password(self):
        # Regardless of what the user provides, return the initial value.
        # This is done here, rather than on the field, because the
        # field does not have access to the initial value
        return self.initial["password"]


class MyUserAdmin(UserAdmin):
    # The forms to add and change user instances
    form = UserChangeForm
    add_form = UserCreationForm

    # The fields to be used in displaying the User model.
    # These override the definitions on the base UserAdmin
    # that reference specific fields on auth.User.
    list_display = ('email', 'date_of_birth', 'is_admin')
    list_filter = ('is_admin',)
    fieldsets = (
        (None, {'fields': ('email', 'password')}),
        ('Personal info', {'fields': ('date_of_birth',)}),
        ('Permissions', {'fields': ('is_admin',)}),
    )
    # add_fieldsets is not a standard ModelAdmin attribute. UserAdmin
    # overrides get_fieldsets to use this attribute when creating a user.
    add_fieldsets = (
        (None, {
            'classes': ('wide',),
            'fields': ('email', 'date_of_birth', 'password1', 'password2')}
        ),
    )
    search_fields = ('email',)
    ordering = ('email',)
    filter_horizontal = ()

# Now register the new UserAdmin...
admin.site.register(MyUser, MyUserAdmin)
# ... and, since we're not using Django's built-in permissions,
# unregister the Group model from admin.
admin.site.unregister(Group)

Finally, specify the custom model as the default user model for your project using the AUTH_USER_MODEL setting in your settings.py:

AUTH_USER_MODEL = 'customauth.MyUser'