django formset實(shí)現(xiàn)數(shù)據(jù)表的批量操作的示例代碼
什么是formset
我們知道forms組件是用來做表單驗(yàn)證,更準(zhǔn)確一點(diǎn)說,forms組件是用來做數(shù)據(jù)庫(kù)表中一行記錄的驗(yàn)證。有forms組件不同,formset是同科同時(shí)驗(yàn)證表中的多行記錄,即formset是做表單批量驗(yàn)證的組件。
批量添加
首先要實(shí)例化formset對(duì)象,對(duì)象初始化時(shí)需要提供操作表的forms表單類,參數(shù) extra 用來顯示驗(yàn)證幾行數(shù)據(jù)。將實(shí)例化的formset對(duì)象傳遞給前端頁(yè)面,前端模板通過兩層循環(huán):第一層循環(huán)form,第二層循環(huán)form中的字段。當(dāng)GET請(qǐng)求時(shí),直接將實(shí)例化的formset對(duì)象傳遞給前端。當(dāng)POST請(qǐng)求時(shí),批量驗(yàn)證表單,當(dāng)所有數(shù)據(jù)都沒有問題時(shí),后臺(tái)數(shù)據(jù)庫(kù)保存數(shù)據(jù)。
后臺(tái)保存數(shù)據(jù)時(shí),有兩種方式:第一種方式簡(jiǎn)潔,但是無法捕獲字段唯一約束的錯(cuò)誤;因此使用formset批量添加數(shù)據(jù)時(shí)最好使用第二中方式,手動(dòng)捕獲唯一約束錯(cuò)誤信息并交給formset送到前端頁(yè)面顯示。
models.Permission.objects.create(**row) obj = models.Permission(**row) | obj.save()
唯一約束錯(cuò)誤信息捕獲的過程,需要使用 obj.validate_unique() 判斷該對(duì)象是否滿足唯一約束,如果不滿足則通過異常捕獲操作,捕獲異常信息。通過 formset.errors[i].update(e) 把錯(cuò)誤信息放入formset中送到前端頁(yè)面顯示。之所以這樣做,是因?yàn)橥ㄟ^forms組件的驗(yàn)證時(shí)無法捕獲唯一約束的錯(cuò)誤。因此這里通過手動(dòng)收集錯(cuò)誤信息并放入forset中。
此外,如果前端頁(yè)面渲染的表單沒有填寫數(shù)據(jù),直接提交是不會(huì)報(bào)錯(cuò)的。 formset默認(rèn)只要不改動(dòng)字段就不會(huì)對(duì)該行數(shù)據(jù)做驗(yàn)證。只要填寫一個(gè)字段,該行數(shù)據(jù)則會(huì)做表單驗(yàn)證 。
# views.py def multi_add(request): """ 批量添加 :param request: :return: """ formset_class = formset_factory(MultiPermissionForm, extra=2) if request.method == 'GET': formset = formset_class() return render(request, 'multi_add.html', {'formset': formset}) formset = formset_class(data=request.POST) if formset.is_valid(): flag = True # 檢查formset中沒有錯(cuò)誤信息,則講用戶提交的數(shù)據(jù)獲取到。 post_row_list = formset.cleaned_data for i in range(0, formset.total_form_count()): row = post_row_list[i] if not row: continue try: obj = models.Permission(**row) obj.validate_unique() # 檢查當(dāng)前對(duì)象在數(shù)據(jù)庫(kù)是否存在唯一的異常。 obj.save() except Exception as e: formset.errors[i].update(e) flag = False if flag: return HttpResponse('提交成功') else: return render(request, 'multi_add.html', {'formset': formset}) return render(request, 'multi_add.html', {'formset': formset})
前端模板通過兩層循環(huán):第一層循環(huán)formset得到每一個(gè)form,第二層循環(huán)form得到每一個(gè)字段。與forms組件使用一樣,需要手動(dòng)添加form表單和input提交數(shù)按鈕及csrf_token跨域偽造請(qǐng)求。此外,使用formset,還需要增加 {{ formset.management_form }} , 使用哪個(gè)formset就增加哪個(gè)formset.management_form.
# multi_add.html <form method="post"> {% csrf_token %} {{ formset.management_form }} <table border="1"> <thead> <tr> <th>標(biāo)題</th> <th>URL</th> <th>NAME</th> <th>菜單</th> <th>父權(quán)限</th> </tr> </thead> <tbody> {% for form in formset %} <tr> {% for field in form %} <td>{{ field }} <span style="color: red;">{{ field.errors.0 }}</span></td> {% endfor %} </tr> {% endfor %} </tbody> </table> <input type="submit" value="提交"> </form>
批量編輯
批量編輯和批量增加大體是一致的,但是存在不同的使用區(qū)別。實(shí)例化formset對(duì)象時(shí)默認(rèn)extra=1,需要手動(dòng)修改為extra=0;GET請(qǐng)求,頁(yè)面需要顯示默認(rèn)值,通過參數(shù)initial賦值列表內(nèi)部嵌套字典的數(shù)據(jù)結(jié)構(gòu)的數(shù)據(jù)。 且需要傳遞每行數(shù)據(jù)的id,告訴formset需要修改的數(shù)據(jù)id 。此時(shí)使用的forms類相比批量添加使用的類多一個(gè)id字段, id = forms.IntegerField( widget=forms.HiddenInput()) ,默認(rèn)隱藏的字段,前端頁(yè)面不顯示。
同理也會(huì)遇到唯一約束錯(cuò)誤,使用循環(huán)和反射為每個(gè)字段做數(shù)據(jù)更新賦值,然后再提交數(shù)據(jù)庫(kù)保存。
def multi_edit(request): formset_class = formset_factory(MultiUpdatePermissionForm, extra=0) if request.method == 'GET': formset = formset_class( initial=models.Permission.objects.all().values('id', 'title', 'name', 'url', 'menu_id', 'pid_id')) return render(request, 'multi_edit.html', {'formset': formset}) formset = formset_class(data=request.POST) if formset.is_valid(): # 檢查formset中沒有錯(cuò)誤信息,則講用戶提交的數(shù)據(jù)獲取到。 post_row_list = formset.cleaned_data flag = True for i in range(0, formset.total_form_count()): row = post_row_list[i] if not row: continue permission_id = row.pop('id') try: permission_object = models.Permission.objects.filter(id=permission_id).first() for key, value in row.items(): setattr(permission_object, key, value) permission_object.validate_unique() permission_object.save() except Exception as e: formset.errors[i].update(e) flag = False if flag: return HttpResponse('提交成功') else: return render(request, 'multi_edit.html', {'formset': formset}) return render(request, 'multi_edit.html', {'formset': formset})
前端模板循環(huán)顯示每個(gè)字段時(shí),要判斷是否是第一個(gè)id字段,如果是第一個(gè)就直接 {{field}} ,頁(yè)面將不會(huì)顯示。
<form method="post"> {% csrf_token %} {{ formset.management_form }} <table border="1"> <thead> <tr> <th>標(biāo)題</th> <th>URL</th> <th>NAME</th> <th>菜單</th> <th>父權(quán)限</th> </tr> </thead> <tbody> {% for form in formset %} <tr> {% for field in form %} {% if forloop.first %} {{ field }} {% else %} <td>{{ field }} <span style="color: red;">{{ field.errors.0 }}</span></td> {% endif %} {% endfor %} </tr> {% endfor %} </tbody> </table> <input type="submit" value="提交"> </form>
以上就是本文的全部?jī)?nèi)容,希望對(duì)大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。
相關(guān)文章
python實(shí)現(xiàn)文件分組復(fù)制到不同目錄的例子
這篇文章主要介紹了python實(shí)現(xiàn)文件按組復(fù)制到不同目錄的例子,需要的朋友可以參考下2014-06-06淺談python 四種數(shù)值類型(int,long,float,complex)
下面小編就為大家?guī)硪黄獪\談python 四種數(shù)值類型(int,long,float,complex)。小編覺得挺不錯(cuò)的,現(xiàn)在就分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧2016-06-06Python的加密模塊md5、sha、crypt使用實(shí)例
這篇文章主要介紹了Python的加密模塊md5、sha、crypt使用實(shí)例,本文給出了MD5和crypt模塊的代碼實(shí)例,需要的朋友可以參考下2014-09-09詳解OpenCV中直方圖,掩膜和直方圖均衡化的實(shí)現(xiàn)
這篇文章主要為大家詳細(xì)介紹了OpenCV中直方圖、掩膜、直方圖均衡化詳細(xì)介紹及代碼的實(shí)現(xiàn),文中的示例代碼講解詳細(xì),需要的可以參考一下2022-11-11淺談Django 頁(yè)面緩存的cache_key是如何生成的
這篇文章主要介紹了Django 頁(yè)面緩存的cache_key是如何生成的,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2021-03-03PyQt6中自定義浮點(diǎn)型滑塊類的實(shí)現(xiàn)
在PyQt6中,滑塊是常用的用戶界面元素之一,用于選擇數(shù)值范圍,本文主要介紹了PyQt6中自定義浮點(diǎn)型滑塊類的實(shí)現(xiàn),具有一定的參考價(jià)值,感興趣的可以了解一下2024-03-03