rbac 权限分配, 基于formset实现,批量增加

这里需要两个知识点:
  - formset
  - 自动发现项目中的URL
1. 什么是formset:
  Django中 form组件 或 ModelForm组件,用于做一个表单的验证。 接收前端form表单中的数据,并进行验证。 并且还可以用于表单的渲染工作。 (就是直接循环form对象,每一个字段都会被渲染成一个标签。并放在form标签中。)

  注: 我提到了,是 “一个” 表单的验证。  就是说前端的数据过来, 不管是增删改查,都是对数据库中的 “一行” 记录,一条数据进行的处理。    都是单一的。

  ok, 既然如此,但我想要进行批量操作的时候,对“多个” 表单进行验证的时候。  就使用 formset 啦! 可以理解为他是把 一堆的form标签, 套嵌进了一个 formset中, 然后一起进行验证。

2. 那么怎么使用的呢?
  先搞一个,新的项目。 先用一下, 然后再 集成到,我的rbac项目里面去。
先迁移两张表,用一下。 因为我不想写了!所以直接复制,项目的。
Menu 表, 先手动录入两条信息。

Permission 表, 还是空的, 所以试试 formset 给这张表,批量的添加。

编写form类:

class MultiPermissionForm(forms.Form):title = forms.CharField(widget=forms.TextInput(attrs={"class": "form-control"}))url = forms.CharField(widget=forms.TextInput(attrs={"class": "form-control"}))name = forms.CharField(widget=forms.TextInput(attrs={"class": "form-control"}))menu_id = forms.ChoiceField(  # ChoiceField 和 choices 就表示数据源choices=[(None, "---------")],widget=forms.Select(attrs={"class": "form-control"}),required=False)pid_id = forms.ChoiceField(choices=[(None, "---------")],widget=forms.Select(attrs={"class": "form-control"}),required=False)# 因为 menu_id 和 pid_id ,应该是一个可以选择的下拉框形式, 所以重写了 __init__初始化函数。# 让fields对象中的这两个字段, 与数据库中查询出的结果,进行拼接def __init__(self, *args, **kwargs):super().__init__(*args, **kwargs)self.fields["menu_id"].choices += models.Menu.objects.values_list("mid", "title")self.fields["pid_id"].choices += models.Permission.objects.filter(pid__isnull=True).exclude(menu__isnull=True).values_list("id", "title")# 过滤出 pid==Null的 可以做二级菜单的, exclude排除 menu==Null,不属于任何一个一级菜单的。所有的权限记录
注意继承的是 forms.form 不再是 forms.ModelForm
这其中menu_id  和  pid_id是外键,一对多的关系。 所以我们需要让页面,展示的是一个 select 下拉框的样式。

所以用的是 forms.ChoiceField  参数 choices=[(None, "---------")] 这是一个默认值,用户什么都没选的时候, 页面展示为"---------"数据库存储为 None.  
不过最终,要是要,将外键关联的表中的数据, 给展示到这里的,所以, 重写了 __init__ 初始化方法。从数据库中取出数据并进行拼接。

    def __init__(self, *args, **kwargs):super().__init__(*args, **kwargs)self.fields["menu_id"].choices += models.Menu.objects.values_list("mid", "title")self.fields["pid_id"].choices += models.Permission.objects.filter(pid__isnull=True).exclude(menu__isnull=True).values_list("id", "title")

 因为重写的是  父类的方法。 所以是 super。  每个对象自己都有一个  fields 对象。 这个对象里面包含了我们类中写的所有的字段。 所以在这里 对  menu_id 字段 和 pid_id 字段。进行拼接。
  因为我们拼接的是  choices 这个参数。他 接收的是,列表格式,所以使用 values_list 取出数据库中的值。 并不是所有的都取出来, 因为我们需要的是, 1.保存到数据库中的外键关系,有个主键id就好。 2.然后是需要进行页面展示的 title 字段。

至于需要哪些,根据需求来: 我这里pid_id 需要的是。 Permission表中 pid字段为Null的, 这些字段是可以做二级菜单使用的。

exclude排除; 排除的是 Permission表中 menu字段为空的, 表示他不属于任何一个一级菜单的这些字段。

OK 到此为止, 一个表单的 form 算是弄好了!


然后就是想要进行批量的,添加。 需要借助一个Django内置的模块:
    from django.forms import formset_factory
formset_factory 接收两个参数, 一个是我们刚刚写好的 MultiPermissionForm 然后是 extra 。
第一个参数是,我要渲染的form。 第二个参数是要渲染几次。
看代码:

def multi_add(request):'''批量添加:param request::return:'''# 使用formset_factory 让它在内部,帮我生成 extra 指定数量的 form对象formset_class = formset_factory(MultiPermissionForm, extra=2)if request.method == "POST":formset = formset_class(data=request.POST)if formset.is_valid():print(formset.cleaned_data)return HttpResponse("提交成功")return render(request, "multi_add.html", locals())# 然后将formset_class 进行实例化formset = formset_class()return render(request, "multi_add.html", locals())
views

然后是,前端页面的渲染:

<!DOCTYPE html>
<html lang="en">
<head><meta charset="UTF-8"><meta http-equiv="x-ua-compatible" content="IE=edge"><meta name="viewport" content="width=device-width, initial-scale=1"><title>Title</title>
</head>
<body>
<form method="post">{% csrf_token %}{{ formset.management_form }}<table border="1"><thead><tr><th>标题</th><th>url</th><th>Code</th><th>Menu</th><th>权限</th></tr></thead><tbody>{% for form in formset %}<tr>{% for field in form %}<td>{{ field }} <span>{{ field.errors.0 }}</span></td>{% endfor %}</tr>{% endfor %}</tbody></table><input type="submit" value="提交">
</form></body>
</html>
templates

除了Django必须要的  {%csre_token%}  还有一个也是必须要写的{{ formset.management_form }}。如过不写的话, 可能会遇到上传数据,丢失的错误。

然后看一看,前端页面的效果吧:

low的一笔, 不过凑合看吧!

so 我们看一看,运行的效果怎么样?
  1. 第一次我什么都不写,直接提交。
卧槽, 竟然成功了? 什么情况, 说好的验证呢?  看一看 print(formset.cleaned_data) 的结果:
    [{}, {}]  
emmmm ok, 虽然经过了, formset.is_valid()  但是返回, 一个空列表是什么意思?
  解释一下: 如果我提交的时候,什么都不写的话, form 组件就不帮我进行验证了。 但是入过我写了东西,才会进行验证。
还是看一张图解释比较清晰:

这就很清楚了, 第一行数据, 我写了一个标题。 他帮我进行了验证, 然后没通过的地方,也返回了相应的错误信息。

 但是 第二行,数据。 我什么都没写, form组件, 就没有给我进行验证。所以没有验证,也就不存在错误了, 对吧:
看一看 print(formset.cleaned_data) 的结果:
  这次他没有执行, 因为  if formset.is_valid(): 没有通过验证。
如果你只写满一行 和啥样呢?  [{'title': '这是一个标题', 'url': '/costomer/list/', 'name': 'costomer_list', 'menu_id': '2', 'pid_id': ''}, {}]
第二个还是空的。
OK 明白了,返回的是一个列表, 列表中是字典格式的每一行数据 看看怎么保存到数据库吧:
肯定是 for 循环了。

def multi_add(request):formset_class = formset_factory(MultiPermissionForm, extra=2)if request.method == "POST":formset = formset_class(data=request.POST)if formset.is_valid():print(formset.cleaned_data)for row in formset.cleaned_data:# models.Permission.objects.create(**row)obj = models.Permission(**row)obj.save()return HttpResponse("提交成功")return render(request, "multi_add.html", locals())formset = formset_class()# 然后将formset_class 进行实例化return render(request, "multi_add.html", locals())

 这里有两种,保存的方式。
  1. models.Permission.objects.create(**row)  直接使用。 ORM 的方法。 将row 这个字典。 传给 create() 可以进行保存。
  2. 也可以 先生成一个 model 对象。 然后这个对象执行 save()  方法。 都可以进行保存。

 OK 看效果:

然后 最后还有一个问题就是, 关于唯一性的问题的  就是数据库中, 添加了 unipue=Ture 的这个字段:
所以我们就得 捕获到这个错误,然后进行处理:
然后 这里有个大大大大大的问题,怎样才能, 让这个字段出的这个唯一的错误, 相应的显示到这个字段上呢?
  1. 我们知道 每一个form对象, 都有两个属性 他的字段 和他的错误信息: formset是啥样的呢?
    [ form(字段,错误), form(字段,错误), form(字段,错误), form(字段,错误) ]  大概就是这个样子。
  2. 然后从前端返回的数据呢? 是什么样的呢?
    [ {},  {},  {}, {} ]   就是这个样子。
他们唯一有练习的地方就是,  第一个form对象, 对应的就是  第一个 {}  的 索引。
所以我接下来 进行循环的 时候, 就不能按照上面的方法,再去循环了, 而是应该这样。

for i in range(0, formset.total_form_count()):  # formset.total_form_count() 这个是检测formset中有多少 form对象。如果有5个, 就循环0,1,2,3,4,
  formset.cleaned_data[i] # cleaned_data就是前端返回来的 [ {},  {},  {}, {} ] formset.errors[i] # errors 就是所有的错误信息,

取出的时候, 就按照索引进行,取出数据。 中间进行判断错误。 如果有错误,就加到当前索引下的 errors 列表中。 然后返回前端进行渲染。

 这里有一个小细节,  formset.cleaned_data[i]  和 formset.errors[i]  这两句是 互斥的, 也就是如果发生错误 cleaned_data 你就拿不到数据了。

formset.cleaned_data 是这样工作的: 检查如果formset中没有错误信息,则将用户的提交的数据获取到。他不是一次性把每一个form对象中的数据都获取到。而是执行一次
就获取一次 form 对象中的数据。

什么意思呢? 就是说, 如果循环的第一次就发生了错误, 那后面的数据, 你也得不到了。 but way?
因为formset.errors[i]  这个就相当于,人为的给form中添加上了一个错误;formset.cleaned_data去检查的时候,就会检测到这个错误,然后后面的数据你就别想了
SO 看代码吧

def multi_add(request):formset_class = formset_factory(MultiPermissionForm, extra=2)if request.method == "POST":formset = formset_class(data=request.POST)if formset.is_valid():flag = Truepost_row_date = formset.cleaned_data # 先一步将用户提交的数据,全部获取到,并赋值给一个变量,防止中途发生错误,剩余数据取不到。for i in range(0, formset.total_form_count()):row = post_row_date[i]
         if not row:
           continue # 如果是空的数据, 下面就不用走了。try:obj = models.Permission(**row)obj.validate_unique()# 检测当前对象在数据库是否存在 唯一的异常。如果有他就会抛出一个异常obj.save()except Exception as e:# 捕获到这个异常, e就是错误信息的提示
            # 为什么是 update(e) 因为 errors是这样子的 [{"title":[]}] 在这里就是获取到其中一个字典, 然后给字典 update()formset.errors[i].update(e)flag = Falseif flag:return HttpResponse("提交成功")else:return render(request, "multi_add.html", {"formset": formset})return render(request, "multi_add.html", {"formset": formset})formset = formset_class() # 然后将formset_class 进行实例化return render(request, "multi_add.html", {"formset": formset})

 ok  搞定了。 批量增加的时候, 就没啥别的问题了。
再就是批量修改了:  烦烦烦。。。。。。。。。。。。






 

转载于:https://www.cnblogs.com/chengege/p/10718076.html

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/462850.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

HDU 1247 Hat’s Words 字典树(Trie树)

HDU 1247 Hat’s Words 字典树(Trie树) 字典树的建立是应该都是一样的 下面是我的做法: 建立完后, 对每一个单词都进行find_string()判断是否符合, 分别对其分成两半, 用j分隔开(左闭右开); 分别find()其子串[0, j1), [j1, string_len), 当两子串都找到后,则输出此主串, 然后,b…

setBackgroundResource的一个问题

2019独角兽企业重金招聘Python工程师标准>>> 一&#xff0c;<RelativeLayout xmlns:android"http://schemas.android.com/apk/res/android" android:id"id/layout" android:layout_width"fill_parent" android:layout_height…

Memcache应用场景

Memcache应用场景介绍面临的问题对于高并发高访问的Web应用程序来说&#xff0c;数据库存取瓶颈一直是个令人头疼的问题。特别当你的程序架构还是建立在单数据库模式&#xff0c;而一个数据池连接数峰 值已经达到500的时候&#xff0c;那你的程序运行离崩溃的边缘也不远了。很多…

mingw + msys 上编译 ffmpeg

下载以下文件 mingw msys msysdtk ffmpeg-0.6.1 1.安装 mingw &#xff0c;一路next d:\mingw 2.安装 msys &#xff0c;msysdtk 到同样的路径下,比如:d:\msys\1.0&#xff0c;ffmpeg需要用到perl&#xff0c;刚好msysdtk中有 3.整合 msys 和 mingw 创建文件 d:\msys\1.0\…

双向长短期神经网络(Bi-LSTM)-多输入时序预测

目录 一、程序及算法内容介绍&#xff1a; 基本内容&#xff1a; 亮点与优势&#xff1a; 二、实际运行效果&#xff1a; 三、部分代码展示&#xff1a; 四、完整代码下载&#xff1a; 一、程序及算法内容介绍&#xff1a; 基本内容&#xff1a; 本代码基于Matlab平台编…

微软的云笔记:OneNote+SkyDrive

OneNote是微软的一款笔记软件&#xff08;如果单说功能要绝对要比EverNote强大多了&#xff09;&#xff0c;它可以让你随时记录各种文字、图片&#xff0c;同时在格式排版方面它也继承了Word的强大编辑功能。但是随着各种移动办公需求的产生OneNote这种本地化的保存方式确实在…

【转】温州的南拳

南拳和北腿少林武当功太极八卦连环掌中华有神功在我国传统武术中素有南拳和北腿之分&#xff0c;实际上这种分法点出了我国南北地域拳风的特点&#xff0c;南方的拳术沉马寸劲、迅疾紧凑、重拳厉掌&#xff0c;着眼在拳&#xff1b;北方的拳路却是阔幅舒展、大开大合&#xff0…

day05-数据类型与操作

转载于:https://www.cnblogs.com/klw1/p/10727073.html

开发高级 Web 部件

通过用户控件实现 Web 部件相当容易&#xff0c;但是也有一些弊端&#xff1a; 受限的重用&#xff1a;如果不手动复制 .ascx 文件到其他 Web 应用程序的目录下&#xff0c;就不能动态添加这些控件到其他 Web 应用程序的页面中。 受限的个性化&#xff1a;用户控件的个性化仅限…

强一致性和弱一致性的区别

一致性包括强一致性和弱一致性&#xff1a; 弱一致性&#xff1a;可以理解为CAP定律中的不一致现象。但是经过一定的时间会达到最终一致性 强一致性: 包含线性一致性和顺序一致性。 因此强一致性不一定是线性一致性&#xff0c;但线性一致性一定是强一致性

Juqery Html(),append()等方法的Bug

标题中说是jquery中的Bug&#xff0c;只是个人这么认为&#xff0c;先申明一下&#xff01; 这几天在做动态加载图片热区&#xff0c;我用Ajax获取到了area标签&#xff0c;这里有多个area,在IE8和FF里测试正常&#xff0c;可一到IE7,和IE6里面就显示不正常了。后来发现jquery中…

maven 聚合工程 用spring boot 搭建 spring cloud 微服务 模块式开发项目

项目的简单介绍: 项目采用maven聚合工程 用spring boot 搭建 spring cloud的微服务 模块式开发 项目的截图: 搭建开始: 能上图 我少打字 1.首先搭建maven的聚合工程 1.1创建聚合工程的父模块 1.2设置父模块的POM文件 主要是配置 spring boot版本&#xff0c;spring cloud 版本&…

使用loadrunner编写webservice接口请求

1、使用工具&#xff1a; loadrunner12&#xff0c;本实例截图中都是loadrunner12工具 2、操作步骤&#xff1a; 1)、新建脚本&#xff0c;选择Web Services协议&#xff1a; 2)、选择工具栏&#xff1a; 3)、点击Import&#xff0c;输入wsdl地址&#xff1a;测试代码用的地址&…

实验二《Java面向对象程序设计》实验报告

一、实验内容 初步掌握单元测试和TDD理解并掌握面向对象三要素&#xff1a;封装、继承、多态初步掌握UML建模熟悉S.O.L.I.D原则了解设计模式 二、实验步骤 &#xff08;一&#xff09;单元测试 1.三种代码&#xff1a;伪代码、测试代码、产品代码需求&#xff1a;在一个MyUtil类…

iOS开发那些事--创建基于故事板的iOS 6的HelloWorld

基于故事板的HelloWorld工程 Storyboard&#xff08;故事板&#xff09;是用来替代xib的技术&#xff0c;也是iOS 5最重要的新特性之一。我们用Storyboard&#xff08;故事板&#xff09;重构HelloWorld。 使用故事板重构HelloWorld 勾选“Use Storyboards”项。 工程创建完成之…

基于美国人口数据分析

https://github.com/jakevdp/PythonDataScienceHandbook 英文看不懂的话请自行选择中文翻译版转载于:https://www.cnblogs.com/Lucifer77/p/10741538.html

MySQL innodb每行数据长度的限制

今天在使用MySQL innodb时&#xff0c;create table时&#xff0c;报出这样的一个错误: Row size too large. The maximum row size for the used table type, not counting BLOBs, is 8126. You have to change some columns to TEXT or BLOBs 查阅MySQL的官方资料才发现&…

Hibernate实体JSONObject化时遇到的问题

2019独角兽企业重金招聘Python工程师标准>>> ###在对Hibernate持久化的对象进行JSON化的时候&#xff0c;出现了几个问题。 第一个是因为在对象中有相互引用&#xff0c;如A->B,B.set->A这样的一对多关系&#xff0c;使得在遍历构造JSON时&#xff0c;产生了死…

截取中英文字符串

代码 functionsubstr(str, len){if(!str ||!len) { return; }//预期计数&#xff1a;中文2字节&#xff0c;英文1字节vara 0;//循环计数vari 0;//临时字串vartemp ;for(i0;i<str.length;i){if(str.charCodeAt(i)>255){//按照预期计数增加2a2; }else{ a; }//如果增加计数…

mysql概述

MySql大致分为三层结构&#xff1a; 第一层&#xff1a;客户端并非MySql所独有&#xff0c;例如&#xff1a;连接处理、授权认证、安全等功能均在这一层处理 第二层&#xff1a;核心服务包括查询解析、分析、优化、缓存、内置函数(比如 : 时间、数学、加密等函数)&#xff0c;所…