阅读 2118

Django 用户认证系统:拓展 User 模型

本教程为系列教程,为方便阅读,每篇教程开头列出全部教程的目录索引:


Django 用户认证系统提供了一个内置的 User 对象,用于记录用户的用户名,密码等个人信息。对于 Django 内置的 User 模型, 仅包含以下一些主要的属性:

  • username,即用户名
  • password,密码
  • email,邮箱
  • first_name,名
  • last_name,姓

对于一些网站来说,用户可能还包含有昵称、头像、个性签名等等其它属性,因此仅仅使用 Django 内置的 User 模型是不够。好在 Django 用户系统遵循可拓展的设计原则,我们可以方便地拓展 User 模型。

继承 AbstractUser 拓展用户模型

这是推荐做法。事实上,查看 User 模型的源码就知道,User 也是继承自 AbstractUser 抽象基类,而且仅仅就是继承了 AbstractUser,没有对 AbstractUser 做任何的拓展。以下就是 User 的源码:

class User(AbstractUser):
    """
    Users within the Django authentication system are represented by this
    model.

    Username, password and email are required. Other fields are optional.
    """
    class Meta(AbstractUser.Meta):
        swappable = 'AUTH_USER_MODEL'复制代码

所以,如果我们继承 AbstractUser,将获得 User 的全部特性,而且还可以根据自己的需求进行拓展。

我们之前新建了一个 users 应用,通常我们把和数据库模型相关的代码写在 models.py 文件里。打开 users/models.py 文件,写上我们自定义的用户模型代码:

users/models.py

from django.db import models
from django.contrib.auth.models import AbstractUser


class User(AbstractUser):
    nickname = models.CharField(max_length=50, blank=True)

    class Meta(AbstractUser.Meta):
        pass复制代码

我们给自定义的用户模型新增了一个 nickname(昵称)属性,用来记录用户的昵称信息,设置 blank=True 的目的是让用户在注册时无需填写昵称。根据你的需求可以自己进一步拓展,例如增加用户头像、个性签名等等,添加多少属性字段没有任何限制。

同时,我们继承了 AbstractUser 的内部类属性 Meta ,不过目前什么也没做。在这里继承 Meta 的原因是在你的项目中可能需要设置一些 Meta 类的属性值,不要忘记继承 AbstractUser.Meta 中已有的属性。

注意:一定要继承 AbstractUser,而不是继承 auth.User。尽管 auth.User 继承自 AbstractUser 且并没有对其进行任何额外拓展,但 AbstractUser 是一个抽象类,而 auth.User 不是。如果你继承了 auth.User 类,这会变成多表继承,在目前的情况下这种继承方式是不被推荐的。关于 Django 的抽象模型类和多表继承,请查阅 Django 的官方文档 模型继承

此外,AbstractUser 类又继承自 AbstractBaseUser,前者在后者的基础上拓展了一套用户权限(Permission)系统。因此如非特殊需要,尽量不要从 AbstractBaseUser 拓展,否则你需要做更多的额外工作。

为了让 Django 用户认证系统使用我们自定义的用户模型,必须在 settings.py 里通过 AUTH_USER_MODEL 指定自定义用户模型所在的位置,即需要如下设置:

django_auth_example/settings.py

# 其它设置...
AUTH_USER_MODEL = 'users.User'复制代码

即告诉 Django,使用 users 应用下的 User 用户模型。

顺便再修改一下语言设置和时区设置:

django_auth_example/settings.py

# 其它设置...

LANGUAGE_CODE = 'zh-hans'
TIME_ZONE = 'Asia/Shanghai'复制代码

设置好自定义用户模型后,生成数据库迁移文件,并且迁移数据库以生成各个应用必要的数据库表。即运行如下两条命令:

$ python manage.py makemigrations
$ python manage.py migrate复制代码

OK,现在 Django 用户系统使用的用户模型就是自定义的 User 模型了。

注意:一定要在设置好 AUTH_USER_MODEL = 'users.User' 后在第一次迁移数据库,即指定好自定义的用户模型后再执行数据库迁移命令。

使用 Profile 模式拓展用户模型

如果想为一个已使用了 Django 内置 User 模型的项目拓展用户模型,上述继承 AbstractUser 的拓展方式会变得有点麻烦。Django 没有提供一套自动化的方式将内置的 User 迁移到自定义的用户模型,因为 Django 已经为内置的 User 模型生成了相关数据库迁移文件和数据库表。如果非要这么做的话,需要手工修改迁移文件和数据库表,并且移动数据库中相关的用户数据。

所以我们采用另一种不改动数据库表的方式来拓展用户模型,具体来说,我们在创建一个模型(通常命名为 Profile)来记录用户相关的数据,然后使用一对一的方式将这个 Profile 模型和 User 关联起来,就好像每个用户都关联着一张记录个人资料的表一样。代码如下:

models.py

from django.contrib.auth.models import User

class Profile(models.Model):
    nickname = models.CharField(max_length=50, blank=True)
    user = models.OneToOneField(User)复制代码

这种方式和 AbstractUser 的区别是,继承 AbstractUser 的用户模型只有一张数据库表。而 Profile 这种模式有两张表,一张是 User 模型对应的表,一张是 Profile 模型对应的表,两张表通过一对一的关系关联。可见,当要查询某个用户的 Profile 时,需要执行额外的跨表查询操作,所以这种方式比起直接继承 AbstractUser 效率更低一点。因此对于新项目来说,优先推荐使用继承 AbstractUser 的方式来拓展用户模型。

PS:如果你使用了Profile 模式,你可能希望在创建 User 对象的时候同时也创建与之关联的 Profile 对象。你可以使用 Django 的 Signal 实现这个需求。由于 Profile 模式不是我们要介绍的重点内容,因此具体的实现细节请参照相关的文档,这里不再赘述。

OK,自定义的 User 模型已经建立好了,接下来就是如何创建用户,即用户注册流程了。

总结

本教程的示例项目代码位于 GitHub:Django Auth Example

如果遇到问题,请通过下面的方式寻求帮助。

更多 Django 相关教程,请访问我的个人博客:追梦人物的博客

关注下面的标签,发现更多相似文章
评论