1 权限管理模型设计
1.1 建立app
新建一个app, 名称叫system包含用户管理、菜单管理和权限管理等系统基础模块。html
- 使用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
- 修改sandboxMP/sandboxMP/settings.py 加入以下内容:
import sys
sys.path.insert(0, os.path.join(BASE_DIR, 'apps'))
1.2 建立权限认证模型
sandboxMP项目使用的是自定义权限认证模型,模型说明:python
Menu: 菜单管理,用来存储系统可用的URL Role: 角色组,经过外键关联Menu,角色组中的用户将继承Role关联菜单的访问权限 Structure:组织架构,包含单位和部门信息 UserProfile: 自定义用户认证模型,替换系统原有的User模型
下面内容就是权限认证的模型详细内容,将以下内容复制到apps/system/models.pyweb
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, on_delete=models.SET_NULL, 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, on_delete=models.SET_NULL, 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, on_delete=models.SET_NULL, verbose_name="部门") post = models.CharField(max_length=50, null=True, blank=True, verbose_name="职位") superior = models.ForeignKey("self", null=True, blank=True, on_delete=models.SET_NULL, 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
1.3 使用模型
定义好模型后,还要告诉Django使用这些模型,咱们须要修改settings.py文件,在INSTALLED_APPS中添加models.py所在应用的名称:数据库
INSTALLED_APPS = [
...原内容省略... 'system', ]
想要使用自定义的认证模型UserProfile, 还须要在setting.py中添加下面内容:django
AUTH_USER_MODEL = 'system.UserProfile'
注意:
在定义用户模型的时候使用到了ImageField字段类型,在执行makemigrations前须要安装依赖包:pillow,打开CMD窗口,进入本项目的python虚拟环境,而后安装pillow:后端
C:\Users\RobbieHan>workon sandboxMP (sandboxMP) C:\Users\RobbieHan>pip install pillow
也能够在pycharm 的Terminal终端窗口执行安装命令: pip install pillow浏览器
最后执行makemigrations 和 migrate来生成数据表, 使用pycharm Tools,点击Run manage.py Task..., 在manage.py窗口输入下面命令:session
makemigrations migrate
1.4 模型(Models)相关知识点
字段类型:架构
在权限认证模型中使用到的字段类型以下:app
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: 是一个可迭代结构(元祖),每一个元组中的第一个元素,是存储在数据库中的值;第二个元素是令人容易理解的描述。
on_delete :在django2.0版本之前,定关联字段时,on_delete选项并非必须的,而在django2.0版本后,在定义关联字段时on_delete是必需要定义的,经常使用参数以下:
on_delete=models.CASCADE, # 删除关联数据,与之关联也删除 on_delete=models.DO_NOTHING, # 删除关联数据,什么也不作 on_delete=models.PROTECT, # 删除关联数据,引起错误ProtectedError on_delete=models.SET_NULL, # 删除关联数据,与之关联的值设置为null on_delete=models.SET_DEFAULT, # 删除关联数据,与之关联的值设置为默认值
须要注意的是在使用SET_NULL的时候,该字段在模型定义的时候须要设置可为空,例如:
user = models.ForeignKey(User, on_delete=models.SET_NULL, blank=True, null=True)
一样在使用SET_DEFAULT的时候,须要预先定义default:
user = models.ForeignKey(User, on_delete=models.SET_DEFAULT, default='默认值')
更多字段类型和字段选项请参考:
https://docs.djangoproject.com/en/1.11/ref/models/fields/#model-field-types
2 用户认证和访问限制
用户登陆认证的需求以下:
- 用户登录系统才能够访问某些页面,
- 若是用户没有登录而直接访问就会跳转到登录界面,
- 用户在跳转的登录界面中完成登录后,自动访问跳转到以前访问的地址,
- 用户可使用用户名、手机号码或者其余字段做为登录用户名。
在pycharm中,选中sandboxMP/apps/system,右键,选择 New → Python File, 在弹出的窗口输入名称:views_user,在刚建立的页面中导入须要的模块:
from django.shortcuts import render from django.views.generic.base import View from django.http import HttpResponseRedirect from django.contrib.auth import authenticate, login, logout from django.urls import reverse
说明: 如下建立的视图,都是写在sandboxMP/apps/system/views_user.py文件中
2.1 建立index页面视图
index页面视图,是本项目建立的第一个视图:
class IndexView(View): def get(self, request): return render(request, 'index.html')
知识点介绍:
一、视图: Django官方文档对“视图”的介绍是用来封装处理用户请求和返回响应的逻辑。
咱们能够定义视图函数,用来接受Web请求并返回Web响应,也可使用基于类的视图对象,本项目的视图实现都是基于类建立的视图,和基于函数的视图相比据有必定的区别和优点:
- 能够经过单独的方法编写与HTTP方法相关的代码(GET, POST等),无需经过条件分支来判断HTTP方法
- 可将代码分解成可重用的组件,例如Mixin(多继承),发挥面向对象技术优点,使用更加灵活,易于扩展
二、render函数: Django的快捷函数,结合给定的模板和一个给定的上下文字典,并返回一个渲染后的HttpRespose对象,语法:render(request, template_name, context=None, content_type=None, status=None, using=None),其中 request 和template_name必须参数,其它为可选参数。
2.2 建立用户登录视图
在建立用户登录视图前,先建立一个sandboxMP/apps/system/forms.py文件,用来作登录用户的输入验证,内容以下:
from django import forms
class LoginForm(forms.Form): username = forms.CharField(required=True, error_messages={"requeired": "请填写用户名"}) password = forms.CharField(required=True, error_messages={"requeired": "请填写密码"})
建立用户登录视图:
from .forms import LoginForm class LoginView(View): def get(self, request): if not request.user.is_authenticated: return render(request, 'system/users/login.html') else: return HttpResponseRedirect('/') def post(self, request): redirect_to = request.GET.get('next', '/') login_form = LoginForm(request.POST) ret = dict(login_form=login_form) if login_form.is_valid(): user_name = request.POST['username'] pass_word = request.POST['password'] user = authenticate(username=user_name, password=pass_word) if user is not None: if user.is_active: login(request, user) return HttpResponseRedirect(redirect_to) else: ret['msg'] = '用户未激活!' else: ret['msg'] = '用户名或密码错误!' else: ret['msg'] = '用户和密码不能为空!' return render(request, 'system/users/login.html', ret)
知识点介绍:
Django使用会话和中间件来拦截认证系统中的请求对象。它们在每个请求上提供一个request.user属性,表示当前的用户。若是当前的用户没有登入,该属性将设置成AnonymousUser的一个实例,不然将会是User实例。
一、request.user.is_authenticated: 用来判断用户是否登入,如LoginView中:
# 当用户访问登录页面时,判断用户若是未登入则访问登录页面,若是登入则跳转到首页 if not request.user.is_authenticated: return render(request, 'system/users/login.html') else: return HttpResponseRedirect('/')
二、is_valid(): Forms实例的一个方法,用来作字段验证,当输入字段值合法时,它将返回True,同时将表单的数据存放到cleaned_data属性中。
三、authenticate(request=None, **credentials): 用来认证用户,credentials为关键字参数,默认为username和password,若是经过认证后端检查,则返回一个User对象。
四、login(request, user, backend=None): 用来从视图中登录一个用户,同时将用户的ID保存在session表中。注意:在调用login()以前必须使用authenticate()成功认证这个用户。
五、HttpResponseRedirect[source]: 用来重定向访问,参数是重定向的地址,能够是完整的URL,也能够相想读与项目的绝对路径。
2.3 建立用户登出视图
class LogoutView(View): def get(self, request): logout(request) return HttpResponseRedirect(reverse('login'))
知识点介绍:
一、logout(request): 登出用户。
二、reverse(viewname): 根据url name来进行url的反向解析。
2.4 配置用户URL路由
想要经过URL来访问视图应用,还须要配置URL路由,修改sandboxMP/sandboxMP/urls.py:
from django.contrib import admin from django.urls import path from system.views_user import IndexView, LoginView, LogoutView urlpatterns = [ path('admin/', admin.site.urls), path('', IndexView.as_view(), name='index'), path('login/', LoginView.as_view(), name='login'), path('logout/', LogoutView.as_view(), name='logout'), ]
2.5 建立认证用户
在pycharm选择 Tools,点击Run manage.py Task..., 在打开的窗口中输入createsuperuser,根据提示输入用户名,邮箱和密码,操做过程以下:
manage.py@sandboxMP > createsuperuser
"C:\Program Files\JetBrains\PyCharm2017.3.2\bin\runnerw.exe" C:\Users\RobbieHan\Envs\sandboxMP\Scripts\python.exe "C:\Program Files\JetBrains\PyCharm2017.3.2\helpers\pycharm\django_manage.py" createsuperuser D:/ProjectFile/sandboxMP 用户名: admin 邮箱: robbie_han@outlook.com Warning: Password input may be echoed. Password: !qaz@wsx Warning: Password input may be echoed. Password (again): !qaz@wsx Superuser created successfully.
运行项目,访问系统:http://127.0.0.1:8000,咱们并无登入用户,直接能够访问首页,这和咱们的要求不符。接下来实现页面访问限制,要求必须登入用户才能访问。
2.6 页面访问限制
页面访问限制的实现需求:
- 用户登陆系统才能够访问某些页面
- 若是用户没有登录而直接访问就会跳转到登录界面
- 用户在跳转的登录页面完成登录后,自动访问跳转前的访问地址
新建sandboxMP/apps/system/mixin.py,写入以下内容:
from django.contrib.auth.decorators import login_required
class LoginRequiredMixin(object): @classmethod def as_view(cls, **init_kwargs): view = super(LoginRequiredMixin, cls).as_view(**init_kwargs) return login_required(view)
修改sandboxMP/sandboxMP/settings.py, 加入LOGIN_URL
LOGIN_URL = '/login/'
须要登入用户才能访问的视图,只须要继承LoginRequiredMixin便可,修改后的IndexView视图以下:
from .mixin import LoginRequiredMixin class IndexView(LoginRequiredMixin, View): def get(self, request): return render(request, 'index.html')
注意:LoginRequiredMixin位于继承列表最左侧位置
重启项目,咱们再次访问首页,打开浏览器,输入http://127.0.0.1:8000,这时咱们会发现,浏览器中的URL会变成:http://127.0.0.1:8000/login/?next=/, 须要咱们先登录后才会跳转到首页。
使用咱们在2.5小节中建立的用户:admin,密码: !qaz@wsx登录系统
2.7 媒体文件的访问
尽管在建立用户时设置了默认头像,而且已经放置了默认头像使用的图片,可是用户登陆后仍是没法显示头像,因此还须要配置媒体文件的访问。
媒体文件是由用户上传的文件,路径是变化的,好比用户上传的头像文件。
设置文件上传目录
修改sandboxMP/sandboxMP/settings.py文件,添加以下配置:
MEDIA_URL = '/media/' MEDIA_ROOT = os.path.join(BASE_DIR, 'media')
打开sandboxMP/sandboxMP/urls.py,新增以下配置:
from django.conf import settings from django.urls import re_path from django.views.static import serve if settings.DEBUG: urlpatterns += [ re_path(r'^media/(?P<path>.*)$', serve, {"document_root": settings.MEDIA_ROOT}), ]
刷新页面就能够看到用户头像了,须要注意的是,这里之因此使用if settings.DEBUG,是由于这种配置模式应该仅限用于开发模式,在生产环境应该经过web前