日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪(fǎng)問(wèn) 生活随笔!

生活随笔

當(dāng)前位置: 首頁(yè) > 编程资源 > 编程问答 >内容正文

编程问答

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

發(fā)布時(shí)間:2023/12/20 编程问答 35 豆豆
生活随笔 收集整理的這篇文章主要介紹了 rbac 权限分配, 基于formset实现,批量增加 小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

這里需要兩個(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)證。? 就是說(shuō)前端的數(shù)據(jù)過(guò)來(lái), 不管是增刪改查,都是對(duì)數(shù)據(jù)庫(kù)中的 “一行” 記錄,一條數(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)槲也幌雽?xiě)了!所以直接復(fù)制,項(xiàng)目的。
Menu 表, 先手動(dòng)錄入兩條信息。

Permission 表, 還是空的, 所以試試 formset 給這張表,批量的添加。

編寫(xiě)form類(lèi):

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è)可以選擇的下拉框形式, 所以重寫(xiě)了 __init__初始化函數(shù)。# 讓fields對(duì)象中的這兩個(gè)字段, 與數(shù)據(jù)庫(kù)中查詢(xún)出的結(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")# 過(guò)濾出 pid==Null的 可以做二級(jí)菜單的, exclude排除 menu==Null,不屬于任何一個(gè)一級(jí)菜單的。所有的權(quán)限記錄 注意繼承的是 forms.form 不再是 forms.ModelForm 這其中menu_id 和 pid_id是外鍵,一對(duì)多的關(guān)系。 所以我們需要讓頁(yè)面,展示的是一個(gè) select 下拉框的樣式。

所以用的是 forms.ChoiceField? 參數(shù) choices=[(None, "---------")] 這是一個(gè)默認(rèn)值,用戶(hù)什么都沒(méi)選的時(shí)候, 頁(yè)面展示為"---------"數(shù)據(jù)庫(kù)存儲(chǔ)為?None. ?
不過(guò)最終,要是要,將外鍵關(guān)聯(lián)的表中的數(shù)據(jù), 給展示到這里的,所以, 重寫(xiě)了 __init__ 初始化方法。從數(shù)據(jù)庫(kù)中取出數(shù)據(jù)并進(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")

?因?yàn)橹貙?xiě)的是? 父類(lèi)的方法。 所以是 super。? 每個(gè)對(duì)象自己都有一個(gè)? fields 對(duì)象。 這個(gè)對(duì)象里面包含了我們類(lèi)中寫(xiě)的所有的字段。 所以在這里 對(duì)? menu_id 字段 和 pid_id 字段。進(jìn)行拼接。
  因?yàn)槲覀兤唇拥氖? choices 這個(gè)參數(shù)。他 接收的是,列表格式,所以使用 values_list 取出數(shù)據(jù)庫(kù)中的值。 并不是所有的都取出來(lái), 因?yàn)槲覀冃枰氖?#xff0c; 1.保存到數(shù)據(jù)庫(kù)中的外鍵關(guān)系,有個(gè)主鍵id就好。 2.然后是需要進(jìn)行頁(yè)面展示的 title 字段。

至于需要哪些,根據(jù)需求來(lái): 我這里pid_id 需要的是。 Permission表中 pid字段為Null的, 這些字段是可以做二級(jí)菜單使用的。

exclude排除; 排除的是 Permission表中 menu字段為空的, 表示他不屬于任何一個(gè)一級(jí)菜單的這些字段。

OK 到此為止, 一個(gè)表單的 form 算是弄好了!


然后就是想要進(jìn)行批量的,添加。 需要借助一個(gè)Django內(nèi)置的模塊:
?   from django.forms import formset_factory
formset_factory 接收兩個(gè)參數(shù), 一個(gè)是我們剛剛寫(xiě)好的 MultiPermissionForm 然后是 extra 。
第一個(gè)參數(shù)是,我要渲染的form。 第二個(gè)參數(shù)是要渲染幾次。
看代碼:

def multi_add(request):'''批量添加:param request::return:'''# 使用formset_factory 讓它在內(nèi)部,幫我生成 extra 指定數(shù)量的 form對(duì)象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 進(jìn)行實(shí)例化formset = formset_class()return render(request, "multi_add.html", locals()) views

然后是,前端頁(yè)面的渲染:

<!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è)也是必須要寫(xiě)的{{ formset.management_form }}。如過(guò)不寫(xiě)的話(huà), 可能會(huì)遇到上傳數(shù)據(jù),丟失的錯(cuò)誤。

然后看一看,前端頁(yè)面的效果吧:

low的一筆, 不過(guò)湊合看吧!

so 我們看一看,運(yùn)行的效果怎么樣?
  1. 第一次我什么都不寫(xiě),直接提交。
臥槽, 竟然成功了? 什么情況, 說(shuō)好的驗(yàn)證呢?? 看一看 print(formset.cleaned_data) 的結(jié)果:
    [{}, {}] ?
emmmm ok, 雖然經(jīng)過(guò)了, formset.is_valid()? 但是返回, 一個(gè)空列表是什么意思?
  解釋一下: 如果我提交的時(shí)候,什么都不寫(xiě)的話(huà), form 組件就不幫我進(jìn)行驗(yàn)證了。 但是入過(guò)我寫(xiě)了東西,才會(huì)進(jìn)行驗(yàn)證。
還是看一張圖解釋比較清晰:

這就很清楚了, 第一行數(shù)據(jù), 我寫(xiě)了一個(gè)標(biāo)題。 他幫我進(jìn)行了驗(yàn)證, 然后沒(méi)通過(guò)的地方,也返回了相應(yīng)的錯(cuò)誤信息。

?但是 第二行,數(shù)據(jù)。 我什么都沒(méi)寫(xiě), form組件, 就沒(méi)有給我進(jìn)行驗(yàn)證。所以沒(méi)有驗(yàn)證,也就不存在錯(cuò)誤了, 對(duì)吧:
看一看 print(formset.cleaned_data) 的結(jié)果:
  這次他沒(méi)有執(zhí)行, 因?yàn)? if formset.is_valid(): 沒(méi)有通過(guò)驗(yàn)證。
如果你只寫(xiě)滿(mǎn)一行 和啥樣呢?? [{'title': '這是一個(gè)標(biāo)題', 'url': '/costomer/list/', 'name': 'costomer_list', 'menu_id': '2', 'pid_id': ''}, {}]
第二個(gè)還是空的。
OK 明白了,返回的是一個(gè)列表, 列表中是字典格式的每一行數(shù)據(jù) 看看怎么保存到數(shù)據(jù)庫(kù)吧:
肯定是 for 循環(huán)了。

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 進(jìn)行實(shí)例化return render(request, "multi_add.html", locals())

?這里有兩種,保存的方式。
  1. models.Permission.objects.create(**row)? 直接使用。 ORM 的方法。 將row 這個(gè)字典。 傳給 create() 可以進(jìn)行保存。
  2. 也可以 先生成一個(gè) model 對(duì)象。 然后這個(gè)對(duì)象執(zhí)行 save()? 方法。 都可以進(jìn)行保存。

?OK 看效果:

然后 最后還有一個(gè)問(wèn)題就是, 關(guān)于唯一性的問(wèn)題的? 就是數(shù)據(jù)庫(kù)中, 添加了 unipue=Ture 的這個(gè)字段:
所以我們就得 捕獲到這個(gè)錯(cuò)誤,然后進(jìn)行處理:
然后 這里有個(gè)大大大大大的問(wèn)題,怎樣才能, 讓這個(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è) {}? 的 索引。
所以我接下來(lái) 進(jìn)行循環(huán)的 時(shí)候, 就不能按照上面的方法,再去循環(huán)了, 而是應(yīng)該這樣。

for i in range(0, formset.total_form_count()): # formset.total_form_count() 這個(gè)是檢測(cè)formset中有多少 form對(duì)象。如果有5個(gè), 就循環(huán)0,1,2,3,4,
  formset.cleaned_data[i] # cleaned_data就是前端返回來(lái)的 [ {},? {},? {}, {} ] 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中沒(méi)有錯(cuò)誤信息,則將用戶(hù)的提交的數(shù)據(jù)獲取到。他不是一次性把每一個(gè)form對(duì)象中的數(shù)據(jù)都獲取到。而是執(zhí)行一次
就獲取一次 form 對(duì)象中的數(shù)據(jù)。

什么意思呢? 就是說(shuō), 如果循環(huán)的第一次就發(fā)生了錯(cuò)誤, 那后面的數(shù)據(jù), 你也得不到了。 but way?
因?yàn)閒ormset.errors[i]? 這個(gè)就相當(dāng)于,人為的給form中添加上了一個(gè)錯(cuò)誤;formset.cleaned_data去檢查的時(shí)候,就會(huì)檢測(cè)到這個(gè)錯(cuò)誤,然后后面的數(shù)據(jù)你就別想了
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 # 先一步將用戶(hù)提交的數(shù)據(jù),全部獲取到,并賦值給一個(gè)變量,防止中途發(fā)生錯(cuò)誤,剩余數(shù)據(jù)取不到。for i in range(0, formset.total_form_count()):row = post_row_date[i]
         if not row:
           continue # 如果是空的數(shù)據(jù), 下面就不用走了。try:obj = models.Permission(**row)obj.validate_unique()# 檢測(cè)當(dāng)前對(duì)象在數(shù)據(jù)庫(kù)是否存在 唯一的異常。如果有他就會(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í)候, 就沒(méi)啥別的問(wèn)題了。
再就是批量修改了:? 煩煩煩。。。。。。。。。。。。






?

轉(zhuǎn)載于:https://www.cnblogs.com/chengege/p/10718076.html

總結(jié)

以上是生活随笔為你收集整理的rbac 权限分配, 基于formset实现,批量增加的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。

如果覺(jué)得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。