参考资料:https://docs.djangoproject.com/en/1.11/
1 自定义用户认证模型
上一节已经将django基础数据表更新到数据库了,其中包含了用户基础权限认证,登录数据库查看user表:
mysql> DESC auth_user
-> ;
+--------------+--------------+------+-----+---------+----------------+
| Field | Type | Null | Key | Default | Extra |
+--------------+--------------+------+-----+---------+----------------+
| id | int(11) | NO | PRI | NULL | auto_increment |
| password | varchar(128) | NO | | NULL | |
| last_login | datetime(6) | YES | | NULL | |
| is_superuser | tinyint(1) | NO | | NULL | |
| username | varchar(150) | NO | UNI | NULL | |
| first_name | varchar(30) | NO | | NULL | |
| last_name | varchar(30) | NO | | NULL | |
| email | varchar(254) | NO | | NULL | |
| is_staff | tinyint(1) | NO | | NULL | |
| is_active | tinyint(1) | NO | | NULL | |
| date_joined | datetime(6) | NO | | NULL | |
+--------------+--------------+------+-----+---------+----------------+
11 rows in set (0.01 sec)
Django认证系统是可以扩展的,我们可以扩展默认的User模型,或者使用自定义模型进行替换。
1.1 替换User模型:
本次项目中,替换了现有的User模型,接下了解下如何替换现有的User模型:
- 在项目开始时定义自己的User模型
** 这里需要强调的是在项目开始时自定义User模型,不然migrate的时候会报错,在项目实现中我会再做演示说明。**
from django.contrib.auth.models import AbstractUser
class UserProfile(AbstractUser):
name = models.CharField(max_length=20, default="", verbose_name="姓名")
......
- 通过在settings.py里面配置AUTH_USER_MODEL指向自定义的用户模型即可替换现有的User模
型:
AUTH_USER_MODEL = 'myapp.UserProfile'
1.2 引用自定义User模型:
需要注意的是使用自定义User模型后,如果直接引用User模型,系统是会出错的。正确的引用方法是使用 get_user_model(), 比如我们创建一个资产模型,资产通过外键引用User模型:
from django.db import models
class Asset(models.Model):
assetNum = models.CharField(max_length=128, default="", verbose_name="资产编号")
owner = models.ForeignKey(UserProfile, blank=True, null=True, verbose_name="责任人")
上面的做法是错误的,自定义User模型的正确使用方法如下:
from django.db import models
from django.contrib.auth import get_user_model
User = get_user_model()
class Asset(models.Model):
assetNum = models.CharField(max_length=128, default="", verbose_name="资产编号")
owner = models.ForeignKey(User, blank=True, null=True, verbose_name="责任人")
当然我们也可以使用 settings.AUTH_USER_MODEL来引用自定义User模型
from django.db import models
from django.conf import settings
User = settings.AUTH_USER_MODEL
class Asset(models.Model):
assetNum = models.CharField(max_length=128, default="", verbose_name="资产编号")
owner = models.ForeignKey(User, blank=True, null=True, verbose_name="责任人")
1.3 指定认证后端:
我们想要通过邮箱或者手机号来作为登陆认证的账户时,可以使用认证后台通过AUTHENTICATION_BACKENDS设置来指定认证后台。
- 编写自定义认证后端
from django.contrib.auth import get_user_model
from django.contrib.auth.backends import ModelBackend
from django.db.models import Q
User = get_user_model()
class UserBackend(ModelBackend):
def authenticate(self, request, username=None, password=None, **kwargs):
try:
user = User.objects.get(Q(username=username) | Q(mobile=username))
if user.check_password(password):
return user
except Exception as e:
return None
- 指定认证后端
在settings.py文件中加入如下内容:
AUTHENTICATION_BACKENDS = (
'users.views_user.UserBackend',
)
更多有关Django认证系统的知识请参看文章开头的官方文档
2 权限管理模型设计
2.1 创建app
接下来我们将创建system app, system包含用户管理、菜单管理和权限管理等系统基础模块。
- 使用pycharm打开我们的项目,右键项目根目录,选择 New → Python Package, 在弹出的窗口输入apps,这个包就用来存放项目的app.
- 选择pycharm上方Tools,点击Run manage.py Task..., 这时在pycharm下方会打开一个窗口,输入startapp system 回车创建app, 如下图:
将刚刚创建的system 移动到 apps下
为了能够顺利访问到我们新建的app,右键apps,选择Mark Directory as → Sources root
修改settings.py 加入如下内容:
import sys
sys.path.insert(0, os.path.join(BASE_DIR, 'apps'))
2.2 创建权限认证模型
权限认证模型说明:
Menu: 菜单管理,用来存储系统可用的URL
Role: 角色组,通过外键关联Menu,角色组中的用户将继承Role关联菜单的访问权限
Structure:组织架构,包含单位和部门信息
UserProfile: 自定义用户认证模型,替换系统原有的User模型
下面内容就是权限认证的模型详细内容,将如下内容复制到apps/system/models.py
from django.db import models
from django.contrib.auth.models import AbstractUser
class Menu(models.Model):
"""
菜单
"""
name = models.CharField(max_length=30, unique=True, verbose_name="菜单名") # unique=True, 这个字段在表中必须有唯一值.
parent = models.ForeignKey("self", null=True, blank=True, verbose_name="父菜单")
icon = models.CharField(max_length=50, null=True, blank=True, verbose_name="图标")
code = models.CharField(max_length=50, null=True, blank=True, verbose_name="编码")
url = models.CharField(max_length=128, unique=True, null=True, blank=True)
def __str__(self):
return self.name
class Meta:
verbose_name = '菜单'
verbose_name_plural = verbose_name
@classmethod
def get_menu_by_request_url(cls, url):
return dict(menu=Menu.objects.get(url=url))
class Role(models.Model):
"""
角色:用于权限绑定
"""
name = models.CharField(max_length=32, unique=True, verbose_name="角色")
permissions = models.ManyToManyField("menu", blank=True, verbose_name="URL授权")
desc = models.CharField(max_length=50, blank=True, null=True, verbose_name="描述")
class Structure(models.Model):
"""
组织架构
"""
type_choices = (("unit", "单位"), ("department", "部门"))
name = models.CharField(max_length=60, verbose_name="名称")
type = models.CharField(max_length=20, choices=type_choices, default="department", verbose_name="类型")
parent = models.ForeignKey("self", null=True, blank=True, verbose_name="父类架构")
class Meta:
verbose_name = "组织架构"
verbose_name_plural = verbose_name
def __str__(self):
return self.name
class UserProfile(AbstractUser):
name = models.CharField(max_length=20, default="", verbose_name="姓名")
birthday = models.DateField(null=True, blank=True, verbose_name="出生日期")
gender = models.CharField(max_length=10, choices=(("male", "男"), ("female", "女")),
default="male", verbose_name="性别")
mobile = models.CharField(max_length=11, default="", verbose_name="手机号码")
email = models.EmailField(max_length=50, verbose_name="邮箱")
image = models.ImageField(upload_to="image/%Y/%m", default="image/default.jpg",
max_length=100, null=True, blank=True)
department = models.ForeignKey("Structure", null=True, blank=True, verbose_name="部门")
post = models.CharField(max_length=50, null=True, blank=True, verbose_name="职位")
superior = models.ForeignKey("self", null=True, blank=True, verbose_name="上级主管")
roles = models.ManyToManyField("role", verbose_name="角色", blank=True)
class Meta:
verbose_name = "用户信息"
verbose_name_plural = verbose_name
ordering = ['id']
def __str__(self):
return self.name
2.3 使用模型
定义好模型后,还要告诉Django使用这些模型,我们需要修改settings.py文件,在INSTALLED_APPS中添加models.py所在应用的名称:
INSTALLED_APPS = [
......
'system',
# 'apps.system.apps.SystemConfig' # 这种写法也可以
......
]
注意:前面已经介绍了用户自定义认证模型的使用方法,想要使用自定义的认证模型UserProfile, 还需要在setting.py中添加下面内容:
AUTH_USER_MODEL = 'system.UserProfile'
如果这里不指定AUTH_USER_MODEL的话,makemigrations时会报错
ystem.UserProfile.user_permissions: (fields.E304) Reverse accessor for 'UserProfile.user_permissions' clashes with reverse accessor for 'User.user_permissions'.
HINT: Add or change a related_name argument to the definition for 'UserProfile.user_permissions' or 'User.user_permissions'.
最后执行makemigrations 和 migrate来生成数据表, 使用pycharm Tools,点击Run manage.py Task..., 在manage.py窗口输入下面命令:
makemigrations
migrate
你是否还记得在上面介绍使用自定义认证模型时候强调过:<font color="DarkRed"> 要在项目开始时自定义User模型 </font>
在第4节的时候埋下了一个伏笔,我执行了migrate创建生成了djang基础数据表,所以在这里使用自定义了用户认证模型,然后migrate时会报错:
django.db.migrations.exceptions.InconsistentMigrationHistory: Migration admin.0001_initial is applied before its dependency users.0001_initial on database 'default'.
这是因为在第4节中我们通过migrate在数据库中生成了django基础数据,其中包含了默认的User模型,这次自定义了User模型,再次migrate的时候,由于admin模型和默认User模型之间的依赖关系,系统会报错。处理方法:删除数据库中的所有基础数据表,重新migrate。
2.4 模型(Models)相关知识点
从这一章节开始我们会逐步介绍到当前章节创建模型使用到的知识点。
字段类型:
在权限认证模型中使用到的字段类型如下:
CharField: 用来存储字符串,必须制定一个参数 max_length用来限定字段最大长度
Foreignkey: 是一个关联字段,创建多表之间的多对一关系,如果创建同表之间的递归关联关系,可以使用models.ForeignKey('self')
ManyToManyField: 用来实现多对多的关联关系
DateField: 日期时间字段
EmailField: email字段,用来检查email地址是否合法
ImageField: 图片字段,用来定义图片上传和图片检查,需要安装pillow库
字段选项:
unique: 设置为True, 则表示这个字段必须有唯一值,这是从数据库级别来强制数据唯一,后面我们还会介绍通过form验证来确保数据输入的唯一
verbose_name:
blank: 默认值是False, 设置为True,则该字段润许为空
null: 默认值是False,如果为True,Django会在数据库中将空值转存为NULL
choices: 是一个可迭代结构(元祖),每个元组中的第一个元素,是存储在数据库中的值;第二个元素是使人容易理解的描述。
更多字段类型和字段选项请参考:
https://docs.djangoproject.com/en/1.11/ref/models/fields/#model-field-types
- 开源项目:
- 轻量级办公管理系统(乙方流程版)
- 轻量级办公管理系统(甲方定制版,本记录同步项目)
安装部署交流:83792608(QQ群)
更多欢迎关注:sandbox.im