昨日内容回顾

  1. 1. 简述权限管理的实现原理。
  2. 粒度控制到按钮级别的权限控制
  3. - 用户登陆成功之后,将权限和菜单信息放入session
  4. - 每次请求时,在中间件中做权限校验
  5. - inclusion_tag实现的动态菜单
  6. 2. 表结构
  7. 6张表,分别是:
  8. 菜单表,权限表,角色,用户表,用户角色关系表,角色权限关系表
  9. 3. 知识点
  10. - 中间件白名单:配置文件、中间件return None
  11. - 权限初始化:
  12. - left join
  13. - 特殊字典的构造
  14. 权限 = {
  15. 权限别名:{id:'',title:'',url,pid:''},
  16. 权限别名:{id:'',title:'',url,pid:''},
  17. 权限别名:{id:'',title:'',url,pid:''},
  18. }
  19. 菜单 = {
  20. 菜单ID:{
  21. title:'',
  22. icon:'',
  23. children:[
  24. {id:'1',.....}
  25. ]
  26. }
  27. }
  28. - key为数字的字典,在序列化时会变成字符串(*)
  29. - 配置文件
  30. - 中间件进行权限校验
  31. - 权限校验
  32. - 导航路径
  33. - pid,访问无法成为菜单的权限时,默认展开的父级权限ID
  34. - 动态生成菜单
  35. - 通过inclusion_tag和两层for循环 + 中间件传来的pid
  36. - 粒度控制到按钮
  37. - 基于filter并通过 别名 进行权限的判断;

一、django ModelForm

什么是ModelForm

Django中Model负责操作数据库,并且具有简单的数据库验证功能;Form用于用户请求的验证,具有强悍的数据库验证功能;ModelForm是将二者合二为一,即可用于数据库操作(部分),也可用于用户请求的验证(部分)!

其实Django Admin就是利用ModelForm的功能实现的。

Form组件和ModelForm的区别

ModelForm是Django Model.py和Form组件的结合体,可以简单/快速使用 Form验证和数据库操作功能,但不如Form组件灵活,如果在使用Django做web开发过程中验证的数据和数据库字段相关(可以对表进行增、删、改操,注意 Many to many字段,也可以级联操作第3张关系表;),建议优先使用ModelForm,用起来更方便些,但是在使用ModelForm的时候慎用fields=’all,获取数据库所有字段势必造成性能损耗; ### ModelForm应用场景 我们来假设这样一个场景,假如,我们有一个类需要保存org的一些信息如下面这些字段。这个类我们叫做ChangOrgModel python course_num = models.IntegerField(default=0, verbose_name=u'课程数') students_num = models.IntegerField(default=0, verbose_name=u'学习人数') address = models.CharField(max_length=200, verbose_name=u'地址') 如果我们使用form我们需要怎么做呢。我们大概需要这样做。 python class OrgModelForm(forms.Form): course_num = forms.IntegerField() students_num = forms.IntegerField() address = forms.CharField(max_length=200) 我们不难发现,我们的Form定义跟Model的定义基本没什么区别,应该加maxlength的时候还是需要加,我们相当于是重写了一遍,其次,这里只有三个,并没有感觉,当有7、8甚至更多的时候,这样重复操作就很让人受不了了。所以这里我们可以使用ModelForm。 ## 定义ModelForm类 python from django import forms from app01 import models class UserModelForm(forms.ModelForm): class Meta: model = models.User #关联的model类 fields = "__all__" #或('name','email','user_type') #验证哪些字段,"__all__"表示所有字段 exclude = None #排除的字段 labels = None #提示信息 help_texts = None #帮助提示信息 widgets = None #自定义插件 error_messages = None #自定义错误信息(整体错误信息from django.core.exceptions import NON_FIELD_ERRORS) field_classes = None #自定义字段类(也阔以自定义字段) localized_fields = () #本地化,根据settings中TIME_ZONE设置的不同时区显示时间 ## ModelForm验证执行的过程 Form所有的钩子ModelForm都有。 ```python is_valid()—>self.errors—>full_clean()—>self._clean_fields() —> clean字段名(自定义方法)  self.clean_form() —> clean(self)  self._post_clean() (整体错误) clean字段名(自定义方法) ## 举例 新建一个项目untitled1,注意: django版本为1.11 ![](https://cdn.nlark.com/yuque/0/2020/png/1484428/1602213370907-fd2b791e-de0d-4578-b152-833ea86c6cfb.png) 修改models.pypython from django.db import models class Depart(models.Model): # 部门 caption = models.CharField(verbosename=’部门’,maxlength=32) def str(self): return self.caption class Role(models.Model): # 角色 title = models.CharField(verbosename=’角色名’,maxlength=32) def str(self): return self.title class User(models.Model): # 用户 name = models.CharField(verbosename=’姓名’,maxlength=32) depart = models.ForeignKey(verbose_name=’部门’,to=’Depart’,on_delete=models.CASCADE) gender_choices = ( (1,’男’), (2,’女’), ) gender = models.IntegerField(verbose_name=’性别’,choices=gender_choices,default=1) roles = models.ManyToManyField(verbose_name=’角色’,to=’Role’) 执行2个命令,生成表python python manage.py makemigrations python manage.py migrate 修改admin.py,注册表python from django.contrib import admin from app01 import models # Register your models here. admin.site.register(models.Depart) admin.site.register(models.Role) admin.site.register(models.User) 创建超级用户python python manage.py createsuperuser 登录admin后台,录入数据。这样做的目的是为了渲染页面时,不会出现空页面! ### 录入基本数据 新建部门 ![](https://cdn.nlark.com/yuque/0/2020/png/1484428/1602213370793-bd481684-cd37-450c-bae7-02d1aa07bbde.png) ![](https://cdn.nlark.com/yuque/0/2020/png/1484428/1602213370725-eeb50e4a-87c5-4031-bef6-f2494dc67dea.png) 新建角色 ![](https://cdn.nlark.com/yuque/0/2020/png/1484428/1602213370786-3a2bf0a1-8746-4d1b-902b-22dcb557714b.png) ![](https://cdn.nlark.com/yuque/0/2020/png/1484428/1602213370772-43044cba-998e-42f5-aa0b-6460a303e9b9.png) 修改urls.py,增加路由python from app01 import views urlpatterns = [ url(r’^admin/‘, admin.site.urls), url(r’^user/list/$’, views.user_list), ] 修改views.py,增加视图python from django.shortcuts import render from app01 import models # Create your views here. def user_list(request): user_queryset = models.User.objects.all() return render(request,’user_list.html’,{‘user_queryset’:user_queryset}) 在templates目录下,创建文件user_list.htmlhtml <!DOCTYPE html>
添加 {% for row in user_queryset %} {% endfor %}
名称 性别 部门 角色 操作
{{ row.name }} {{ row.get_gender_display }} {{ row.depart.caption }} {% for node in row.roles.all %} {{ node.title }} {% endfor %} 编辑
访问页面:[http://127.0.0.1:8000/user/list/](http://127.0.0.1:8000/user/list/) ![](https://cdn.nlark.com/yuque/0/2020/png/1484428/1602213370864-832fea08-f424-4399-9345-ebc64239f5f3.png) 默认是空的。需要写添加页面 添加页面,怎么写呢?写4个input框?假如有10个表呢?累成狗! 使用ModelForm,根据models.py定义的字段,自动生成input框! ### 添加功能 修改urls.py,增加路由html urlpatterns = [ url(r’^admin/‘, admin.site.urls), url(r’^user/list/$’, views.user_list), url(r’^user/add/$’, views.user_add), ] 修改views.py,增加添加页面,使用ModelForm 需要导入forms组件python from django import forms 完整代码如下:python from django.shortcuts import render,redirect from app01 import models from django import forms # Create your views here. def user_list(request): user_queryset = models.User.objects.all() return render(request,’user_list.html’,{‘user_queryset’:user_queryset}) class UserForm(forms.ModelForm): # 类名最好是表名+Form class Meta: model = models.User # user表 fields = ‘__all
‘ # 所有字段 # fields = [‘name’,’depart’] widgets = { # 定义name字段的输入框为text ‘name’:forms.TextInput(attrs={‘class’:’form-control’}), # 定义depart字段的输入框为Select ‘depart’:forms.Select(attrs={‘class’:’form-control’}), ‘gender’:forms.Select(attrs={‘class’:’form-control’}), # 定义roles字段的输入框为多选框 ‘roles’:forms.SelectMultiple(attrs={‘class’:’form-control’}), } # 错误信息 error_messages = { # name字段的错误信息 ‘name’:{ # 英文的required转为中文提示 ‘required’:’用户名不能为空’ } } def user_add(request): if request.method == “GET”: form = UserForm() # 实例化一个空的ModelForm对象 else: form = UserForm(request.POST) # 接收POST请求数据 if form.is_valid(): # 进行验证 print(‘通过验证’) form.save() # 将验证通过的数据插入到数据库中 return redirect(‘/user/list/‘) # 重定向页面 return render(request,’user_add.html’,{‘form’:form}) 在templates目录下,创建文件user_add.htmlhtml <!DOCTYPE html>

添加用户

{% csrf_token %} {#for循环form对象#} {% for field in form %}
{#显示字段名#}
{#显示字段的输入框以及错误信息,0表示取第一个错误#} {{ field }} {{ field.errors.0 }}
{% endfor %}
刷新页面,点击添加,效果如下: ![](https://cdn.nlark.com/yuque/0/2020/png/1484428/1602213370807-0022ab6a-b5a2-4dda-a1d0-483a889514e2.png) 直接提交空表单,会有错误提示 ![](https://cdn.nlark.com/yuque/0/2020/png/1484428/1602213370967-d5e55749-8dde-430a-9191-81ab493c2245.png) 添加一个正常数据 ![](https://cdn.nlark.com/yuque/0/2020/png/1484428/1602213370810-e533ba74-139c-4c10-926e-9306bf2fda65.png) 添加成功后,页面会自动跳转 ![](https://cdn.nlark.com/yuque/0/2020/png/1484428/1602213370991-7ddfb5f6-c21f-4529-88e7-4cd2b93ee405.png) ### 修改功能 修改urls.py,增加路由python urlpatterns = [ url(r’^admin/‘, admin.site.urls), url(r’^user/list/$’, views.user_list), url(r’^user/add/$’, views.user_add), url(r’^user/edit/(?P\d+)/$’, views.user_edit), ] 修改views.py,增加视图python from django.shortcuts import render,redirect from app01 import models from django import forms # Create your views here. def user_list(request): user_queryset = models.User.objects.all() return render(request,’user_list.html’,{‘user_queryset’:user_queryset}) class UserForm(forms.ModelForm): # 类名最好是表名+Form class Meta: model = models.User # user表 fields = ‘__all
‘ # 所有字段 # fields = [‘name’,’depart’] widgets = { # 定义name字段的输入框为text ‘name’:forms.TextInput(attrs={‘class’:’form-control’}), # 定义depart字段的输入框为Select ‘depart’:forms.Select(attrs={‘class’:’form-control’}), ‘gender’:forms.Select(attrs={‘class’:’form-control’}), # 定义roles字段的输入框为多选框 ‘roles’:forms.SelectMultiple(attrs={‘class’:’form-control’}), } # 错误信息 error_messages = { # name字段的错误信息 ‘name’:{ # 英文的required转为中文提示 ‘required’:’用户名不能为空’ } } def user_add(request): if request.method == “GET”: form = UserForm() # 实例化一个空的ModelForm对象 else: form = UserForm(request.POST) # 接收POST请求数据 if form.is_valid(): # 进行验证 print(‘通过验证’) form.save() # 将验证通过的数据插入到数据库中 return redirect(‘/user/list/‘) # 重定向页面 return render(request,’user_add.html’,{‘form’:form}) def user_edit(request,uid): # 查询指定id的数据 obj = models.User.objects.filter(id=uid).first() if request.method ==’GET’: # 赋值instance可以使form表单是可以接受对象的数据 form = UserForm(instance=obj) return render(request,’user_edit.html’,{‘form’:form}) else: # instance的参数,如果存在那么save()方法会更新这个实例,否则会创建一个新的实例 # 由于这里是更新,如果不指定instance。那么它会新增一条数据 # 我们这里不需要新增,必须要指定instance参数 form = UserForm(data=request.POST,instance=obj) if form.is_valid(): form.save() # 更新一条数据 return redirect(‘/user/list/‘) # 重定向 else: return render(request, ‘user_edit.html’, {‘form’: form}) 在templates目录下,创建文件user_edit.htmlhtml <!DOCTYPE html>

编辑用户

{% csrf_token %} {% for field in form %}
{{ field.label }}{{ field }} {{ field.errors.0 }}
{% endfor %}
刷新页面,点击第一条数据后面的编辑,效果如下: ![](https://cdn.nlark.com/yuque/0/2020/png/1484428/1602213370735-d33dd1cb-1081-4cce-8bd2-2d8e0b8c97a1.png) 上面会自动渲染数据,那么问题来了,它是如何渲染页面的? 看views中的一段代码python if request.method ==’GET’: # 赋值instance可以使form表单是可以接受对象的数据 form = UserForm(instance=obj) return render(request,’user_edit.html’,{‘form’:form}) 这里的obj就是,表里面的一条数据,也就是一个对象。那么执行render时,会渲染这个变量。 看前端页面代码html
{{ field.label }}{{ field }} {{ field.errors.0 }}
这里的field,就是字段对象。注意:这个对象是有值的,forms组件会渲染它! 修改一下数据 ![](https://cdn.nlark.com/yuque/0/2020/png/1484428/1602213370909-4e370514-1ecd-4880-98ba-b990d9d347d6.png) 点击提交,它会自动跳转 数据就更改了! ![](https://cdn.nlark.com/yuque/0/2020/png/1484428/1602213370895-8b99568d-5680-468d-b83c-6e6fc300a501.png) ### 删除功能 修改views.pypython from django.shortcuts import render,redirect,HttpResponse from app01 import models from django import forms # Create your views here. def user_list(request): user_queryset = models.User.objects.all() return render(request,’user_list.html’,{‘user_queryset’:user_queryset}) class UserForm(forms.ModelForm): # 类名最好是表名+Form class Meta: model = models.User # user表 fields = ‘__all
‘ # 所有字段 # fields = [‘name’,’depart’] widgets = { # 定义name字段的输入框为text ‘name’:forms.TextInput(attrs={‘class’:’form-control’}), # 定义depart字段的输入框为Select ‘depart’:forms.Select(attrs={‘class’:’form-control’}), ‘gender’:forms.Select(attrs={‘class’:’form-control’}), # 定义roles字段的输入框为多选框 ‘roles’:forms.SelectMultiple(attrs={‘class’:’form-control’}), } # 错误信息 error_messages = { # name字段的错误信息 ‘name’:{ # 英文的required转为中文提示 ‘required’:’用户名不能为空’ } } def user_add(request): if request.method == “GET”: form = UserForm() # 实例化一个空的ModelForm对象 else: form = UserForm(request.POST) # 接收POST请求数据 if form.is_valid(): # 进行验证 print(‘通过验证’) form.save() # 将验证通过的数据插入到数据库中 return redirect(‘/user/list/‘) # 重定向页面 return render(request,’user_add.html’,{‘form’:form}) def user_edit(request,uid): # 查询指定id的数据 obj = models.User.objects.filter(id=uid).first() if request.method ==’GET’: # 赋值instance可以使form表单是可以接受对象的数据 form = UserForm(instance=obj) return render(request,’user_edit.html’,{‘form’:form}) else: # instance的参数,如果存在那么save()方法会更新这个实例,否则会创建一个新的实例 # 由于这里是更新,如果不指定instance。那么它会新增一条数据 # 我们这里不需要新增,必须要指定instance参数 form = UserForm(data=request.POST,instance=obj) if form.is_valid(): form.save() # 更新一条数据 return redirect(‘/user/list/‘) # 重定向 else: return render(request, ‘user_edit.html’, {‘form’: form}) def user_del(request,uid): # 返回一个元组 obj = models.User.objects.filter(id=uid).delete() if obj[0]: return redirect(‘/user/list/‘) else: return HttpResponse(‘删除失败’) 修改user_list.html,增加删除按钮html <!DOCTYPE html>
添加 {% for row in user_queryset %} {% endfor %}
名称 性别 部门 角色 操作
{{ row.name }} {{ row.get_gender_display }} {{ row.depart.caption }} {% for node in row.roles.all %} {{ node.title }} {% endfor %} 编辑 删除
``` 刷新页面,点击删除 Day110 django ModelForm,客户管理之 编辑权限(一) - 图1 删除成功后,页面刷新 Day110 django ModelForm,客户管理之 编辑权限(一) - 图2 # 二、客户管理之 编辑权限(一) 由于时间关系.略… ## 效果图 http://127.0.0.1:8000/rbac/menu/list/ Day110 django ModelForm,客户管理之 编辑权限(一) - 图3 如果是二级菜单,有淡蓝色的背景 权限编辑 Day110 django ModelForm,客户管理之 编辑权限(一) - 图4 菜单编辑 Day110 django ModelForm,客户管理之 编辑权限(一) - 图5 完整代码如下: 链接:https://pan.baidu.com/s/1xYkyWFwmOZIFK4cqWWUizg 密码:zzs9 未完待续…