rbac 权限分配, 基于formset实现,批量增加
這里需要兩個(gè)知識(shí)點(diǎn):
- formset
- 自動(dòng)發(fā)現(xiàn)項(xiàng)目中的URL
1. 什么是formset:
Django中 form組件 或 ModelForm組件,用于做一個(gè)表單的驗(yàn)證。 接收前端form表單中的數(shù)據(jù),并進(jìn)行驗(yàn)證。 并且還可以用于表單的渲染工作。 (就是直接循環(huán)form對(duì)象,每一個(gè)字段都會(huì)被渲染成一個(gè)標(biāo)簽。并放在form標(biāo)簽中。)
注: 我提到了,是 “一個(gè)” 表單的驗(yàn)證。? 就是說前端的數(shù)據(jù)過來, 不管是增刪改查,都是對(duì)數(shù)據(jù)庫中的 “一行” 記錄,一條數(shù)據(jù)進(jìn)行的處理。??? 都是單一的。
ok, 既然如此,但我想要進(jìn)行批量操作的時(shí)候,對(duì)“多個(gè)” 表單進(jìn)行驗(yàn)證的時(shí)候。? 就使用 formset 啦! 可以理解為他是把 一堆的form標(biāo)簽, 套嵌進(jìn)了一個(gè) formset中, 然后一起進(jìn)行驗(yàn)證。
2. 那么怎么使用的呢?
先搞一個(gè),新的項(xiàng)目。 先用一下, 然后再 集成到,我的rbac項(xiàng)目里面去。
先遷移兩張表,用一下。 因?yàn)槲也幌雽懥?#xff01;所以直接復(fù)制,項(xiàng)目的。
Menu 表, 先手動(dòng)錄入兩條信息。
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 就表示數(shù)據(jù)源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)# 因?yàn)?menu_id 和 pid_id ,應(yīng)該是一個(gè)可以選擇的下拉框形式, 所以重寫了 __init__初始化函數(shù)。# 讓fields對(duì)象中的這兩個(gè)字段, 與數(shù)據(jù)庫中查詢出的結(jié)果,進(jìn)行拼接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的 可以做二級(jí)菜單的, exclude排除 menu==Null,不屬于任何一個(gè)一級(jí)菜單的。所有的權(quán)限記錄 注意繼承的是 forms.form 不再是 forms.ModelForm 這其中menu_id 和 pid_id是外鍵,一對(duì)多的關(guān)系。 所以我們需要讓頁面,展示的是一個(gè) select 下拉框的樣式。所以用的是 forms.ChoiceField? 參數(shù) choices=[(None, "---------")] 這是一個(gè)默認(rèn)值,用戶什么都沒選的時(shí)候, 頁面展示為"---------"數(shù)據(jù)庫存儲(chǔ)為?None. ?
不過最終,要是要,將外鍵關(guān)聯(lián)的表中的數(shù)據(jù), 給展示到這里的,所以, 重寫了 __init__ 初始化方法。從數(shù)據(jù)庫中取出數(shù)據(jù)并進(jìn)行拼接。
?因?yàn)橹貙懙氖? 父類的方法。 所以是 super。? 每個(gè)對(duì)象自己都有一個(gè)? fields 對(duì)象。 這個(gè)對(duì)象里面包含了我們類中寫的所有的字段。 所以在這里 對(duì)? menu_id 字段 和 pid_id 字段。進(jìn)行拼接。
因?yàn)槲覀兤唇拥氖? choices 這個(gè)參數(shù)。他 接收的是,列表格式,所以使用 values_list 取出數(shù)據(jù)庫中的值。 并不是所有的都取出來, 因?yàn)槲覀冃枰氖?#xff0c; 1.保存到數(shù)據(jù)庫中的外鍵關(guān)系,有個(gè)主鍵id就好。 2.然后是需要進(jìn)行頁面展示的 title 字段。
至于需要哪些,根據(jù)需求來: 我這里pid_id 需要的是。 Permission表中 pid字段為Null的, 這些字段是可以做二級(jí)菜單使用的。
OK 到此為止, 一個(gè)表單的 form 算是弄好了!
然后就是想要進(jìn)行批量的,添加。 需要借助一個(gè)Django內(nèi)置的模塊:
? from django.forms import formset_factory
formset_factory 接收兩個(gè)參數(shù), 一個(gè)是我們剛剛寫好的 MultiPermissionForm 然后是 extra 。
第一個(gè)參數(shù)是,我要渲染的form。 第二個(gè)參數(shù)是要渲染幾次。
看代碼:
然后是,前端頁面的渲染:
<!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>標(biāo)題</th><th>url</th><th>Code</th><th>Menu</th><th>權(quán)限</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%}? 還有一個(gè)也是必須要寫的{{ formset.management_form }}。如過不寫的話, 可能會(huì)遇到上傳數(shù)據(jù),丟失的錯(cuò)誤。
然后看一看,前端頁面的效果吧:
low的一筆, 不過湊合看吧!
so 我們看一看,運(yùn)行的效果怎么樣?
1. 第一次我什么都不寫,直接提交。
臥槽, 竟然成功了? 什么情況, 說好的驗(yàn)證呢?? 看一看 print(formset.cleaned_data) 的結(jié)果:
[{}, {}] ?
emmmm ok, 雖然經(jīng)過了, formset.is_valid()? 但是返回, 一個(gè)空列表是什么意思?
解釋一下: 如果我提交的時(shí)候,什么都不寫的話, form 組件就不幫我進(jìn)行驗(yàn)證了。 但是入過我寫了東西,才會(huì)進(jìn)行驗(yàn)證。
還是看一張圖解釋比較清晰:
這就很清楚了, 第一行數(shù)據(jù), 我寫了一個(gè)標(biāo)題。 他幫我進(jìn)行了驗(yàn)證, 然后沒通過的地方,也返回了相應(yīng)的錯(cuò)誤信息。
?但是 第二行,數(shù)據(jù)。 我什么都沒寫, form組件, 就沒有給我進(jìn)行驗(yàn)證。所以沒有驗(yàn)證,也就不存在錯(cuò)誤了, 對(duì)吧:
看一看 print(formset.cleaned_data) 的結(jié)果:
這次他沒有執(zhí)行, 因?yàn)? if formset.is_valid(): 沒有通過驗(yàn)證。
如果你只寫滿一行 和啥樣呢?? [{'title': '這是一個(gè)標(biāo)題', 'url': '/costomer/list/', 'name': 'costomer_list', 'menu_id': '2', 'pid_id': ''}, {}]
第二個(gè)還是空的。
OK 明白了,返回的是一個(gè)列表, 列表中是字典格式的每一行數(shù)據(jù) 看看怎么保存到數(shù)據(jù)庫吧:
肯定是 for 循環(huán)了。
?這里有兩種,保存的方式。
1. models.Permission.objects.create(**row)? 直接使用。 ORM 的方法。 將row 這個(gè)字典。 傳給 create() 可以進(jìn)行保存。
2. 也可以 先生成一個(gè) model 對(duì)象。 然后這個(gè)對(duì)象執(zhí)行 save()? 方法。 都可以進(jìn)行保存。
?OK 看效果:
然后 最后還有一個(gè)問題就是, 關(guān)于唯一性的問題的? 就是數(shù)據(jù)庫中, 添加了 unipue=Ture 的這個(gè)字段:
所以我們就得 捕獲到這個(gè)錯(cuò)誤,然后進(jìn)行處理:
然后 這里有個(gè)大大大大大的問題,怎樣才能, 讓這個(gè)字段出的這個(gè)唯一的錯(cuò)誤, 相應(yīng)的顯示到這個(gè)字段上呢?
1. 我們知道 每一個(gè)form對(duì)象, 都有兩個(gè)屬性 他的字段 和他的錯(cuò)誤信息: formset是啥樣的呢?
[ form(字段,錯(cuò)誤), form(字段,錯(cuò)誤), form(字段,錯(cuò)誤), form(字段,錯(cuò)誤) ]? 大概就是這個(gè)樣子。
2. 然后從前端返回的數(shù)據(jù)呢? 是什么樣的呢?
[ {},? {},? {}, {} ]?? 就是這個(gè)樣子。
他們唯一有練習(xí)的地方就是,? 第一個(gè)form對(duì)象, 對(duì)應(yīng)的就是? 第一個(gè) {}? 的 索引。
所以我接下來 進(jìn)行循環(huán)的 時(shí)候, 就不能按照上面的方法,再去循環(huán)了, 而是應(yīng)該這樣。
formset.cleaned_data[i] # cleaned_data就是前端返回來的 [ {},? {},? {}, {} ] formset.errors[i] # errors 就是所有的錯(cuò)誤信息,
取出的時(shí)候, 就按照索引進(jìn)行,取出數(shù)據(jù)。 中間進(jìn)行判斷錯(cuò)誤。 如果有錯(cuò)誤,就加到當(dāng)前索引下的 errors 列表中。 然后返回前端進(jìn)行渲染。
?這里有一個(gè)小細(xì)節(jié),? formset.cleaned_data[i]? 和 formset.errors[i]? 這兩句是 互斥的, 也就是如果發(fā)生錯(cuò)誤 cleaned_data 你就拿不到數(shù)據(jù)了。
formset.cleaned_data 是這樣工作的: 檢查如果formset中沒有錯(cuò)誤信息,則將用戶的提交的數(shù)據(jù)獲取到。他不是一次性把每一個(gè)form對(duì)象中的數(shù)據(jù)都獲取到。而是執(zhí)行一次就獲取一次 form 對(duì)象中的數(shù)據(jù)。
什么意思呢? 就是說, 如果循環(huán)的第一次就發(fā)生了錯(cuò)誤, 那后面的數(shù)據(jù), 你也得不到了。 but way?
因?yàn)閒ormset.errors[i]? 這個(gè)就相當(dāng)于,人為的給form中添加上了一個(gè)錯(cuò)誤;formset.cleaned_data去檢查的時(shí)候,就會(huì)檢測到這個(gè)錯(cuò)誤,然后后面的數(shù)據(jù)你就別想了
SO 看代碼吧
if not row:
continue # 如果是空的數(shù)據(jù), 下面就不用走了。try:obj = models.Permission(**row)obj.validate_unique()# 檢測當(dāng)前對(duì)象在數(shù)據(jù)庫是否存在 唯一的異常。如果有他就會(huì)拋出一個(gè)異常obj.save()except Exception as e:# 捕獲到這個(gè)異常, e就是錯(cuò)誤信息的提示
# 為什么是 update(e) 因?yàn)?errors是這樣子的 [{"title":[]}] 在這里就是獲取到其中一個(gè)字典, 然后給字典 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 進(jìn)行實(shí)例化return render(request, "multi_add.html", {"formset": formset})
?ok? 搞定了。 批量增加的時(shí)候, 就沒啥別的問題了。
再就是批量修改了:? 煩煩煩。。。。。。。。。。。。
?
轉(zhuǎn)載于:https://www.cnblogs.com/chengege/p/10718076.html
總結(jié)
以上是生活随笔為你收集整理的rbac 权限分配, 基于formset实现,批量增加的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 流畅的Python(Fluent Pyt
- 下一篇: 每周一题 扫雷问题