新建一个app, 名称叫system包含用户管理、菜单管理和权限管理等系统基础模块。html
import sys
sys.path.insert(0, os.path.join(BASE_DIR, 'apps'))
复制代码
sandboxMP项目使用的是自定义权限认证模型,模型说明:前端
Menu: 菜单管理,用来存储系统可用的URL
Role: 角色组,经过外键关联Menu,角色组中的用户将继承Role关联菜单的访问权限
Structure:组织架构,包含单位和部门信息
UserProfile: 自定义用户认证模型,替换系统原有的User模型
复制代码
下面内容就是权限认证的模型详细内容,将以下内容复制到apps/system/models.pypython
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
复制代码
定义好模型后,还要告诉Django使用这些模型,咱们须要修改settings.py文件,在INSTALLED_APPS中添加models.py所在应用的名称:git
INSTALLED_APPS = [
...原内容省略...
'system',
]
复制代码
想要使用自定义的认证模型UserProfile, 还须要在setting.py中添加下面内容:github
AUTH_USER_MODEL = 'system.UserProfile'
复制代码
注意:
在定义用户模型的时候使用到了ImageField字段类型,在执行makemigrations前须要安装依赖包:pillow,打开CMD窗口,进入本项目的python虚拟环境,而后安装pillow:web
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窗口输入下面命令:django
makemigrations
migrate
复制代码
字段类型:
后端
在权限认证模型中使用到的字段类型以下:浏览器
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='默认值')
复制代码
更多字段类型和字段选项请参考:
docs.djangoproject.com/en/1.11/ref…
用户登陆认证的需求以下:
在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文件中
index页面视图,是本项目建立的第一个视图:
class IndexView(View):
def get(self, request):
return render(request, 'index.html')
复制代码
知识点介绍:
一、视图: Django官方文档对“视图”的介绍是用来封装处理用户请求和返回响应的逻辑。
咱们能够定义视图函数,用来接受Web请求并返回Web响应,也可使用基于类的视图对象,本项目的视图实现都是基于类建立的视图,和基于函数的视图相比据有必定的区别和优点:
二、render函数: Django的快捷函数,结合给定的模板和一个给定的上下文字典,并返回一个选而后的HttpRespose对象,语法:render(request, template_name, context=None, content_type=None, status=None, using=None),其中 request 和template_name必须参数,其它为可选参数。
在建立用户登录视图前,先建立一个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(): Form实力的一个方法,用来作字段验证,当输入字段值合法时,它将返回True,同时将表单的数据存放到cleaned_data属性中。
三、authenticate(request=None, **credentials): 用来认证用户,credentials为关键字参数,默认为username和password,若是经过认证后端检查,则返回一个User对象。
四、login(request, user, backend=None): 用来从视图中登录一个用户,同时将用户的ID保存在session表中。注意:在调用login()以前必须使用authenticate()成功认证这个用户。
五、HttpResponseRedirect[source]: 用来重定向访问,参数是重定向的地址,能够是完整的URL,也能够相想读与项目的绝对路径。
class LogoutView(View):
def get(self, request):
logout(request)
return HttpResponseRedirect(reverse('login'))
复制代码
知识点介绍:
一、logout(request): 登出用户。
二、reverse(viewname): 根据url name来进行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'),
]
复制代码
在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,咱们并无登入用户,直接能够访问首页,这和咱们的要求不符。接下来实现页面访问限制,要求必须登入用户才能访问。
页面访问限制的实现需求:
新建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登录系统
尽管在建立用户时设置了默认头像,而且已经放置了默认头像使用的图片,可是用户登陆后仍是没法显示头像,因此还须要配置媒体文件的访问。
媒体文件是由用户上传的文件,路径是变化的,好比用户上传的头像文件。
设置文件上传目录
修改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前端来处理这些媒体文件的访问。
最新最全文档发布在知识星球,能够经过微信搜索公众号“知识星球”,直接回复"52824366"得到访问入口
本节文档对应源码版本: github.com/RobbieHan/s…
很是欢迎感兴趣的朋友,到个人Github或掘金上作客,闲暇之余给个赞或Star,赠人玫瑰手留余香
文档配套项目地址:github.com/RobbieHan/s…
轻量级办公管理系统项目开源地址:github.com/RobbieHan/g…