Django当中实现批量操作
Django当中要实现批量操作,一般我们用forms组件或者modelform中的formset来实现。
一、什么是formset?
Form组件或ModelForm用于做一个表单验证而formset是用于做多个表单的验证组件,用于做批量操作。
二、如何实现批量操作?
1.设置models
1 from django.db import models 2 3 # Create your models here. 4 5 class Menu(models.Model): 6 """ 7 菜单表 8 """ 9 title = models.CharField(verbose_name='菜单名称', max_length=32) 10 icon = models.CharField(verbose_name='图标', max_length=32) 11 12 class Permission(models.Model): 13 """ 14 权限表 15 """ 16 title = models.CharField(verbose_name='标题', max_length=32) 17 url = models.CharField(verbose_name='含正则的URL', max_length=128) 18 pid = models.ForeignKey(to='Permission', verbose_name='关联权限', help_text='对于非菜单权限需要选择一个可以成为菜单的权限,用户做默认展开和选中展开', 19 on_delete=models.CASCADE, null=True, blank=True, related_name='parents') 20 menu = models.ForeignKey(verbose_name='所属菜单', to='Menu', null=True, blank=True, help_text='null表示不是菜单;非null表示是二级菜单',on_delete=models.CASCADE) 21 name = models.CharField(verbose_name='url别名',max_length= 32,unique=True)
2.设置form信息
1 # 用了forms.Form 所以必须自己编写那些字段,如果用了forms.ModelForm 则不需要自己去写 2 class Multi_addModelForm(forms.Form): 3 4 title = forms.CharField(widget=forms.TextInput(attrs={"class":"form-control"})) 5 url = forms.CharField(widget=forms.TextInput(attrs={"class": "form-control"})) 6 name = forms.CharField(widget=forms.TextInput(attrs={"class": "form-control"})) 7 menu_id = forms.ChoiceField(choices=[(None,'------')], 8 widget=forms.Select(attrs={"class": "form-control"}), 9 required=False,) 10 pid_id = forms.ChoiceField(choices=[(None,'------')], 11 widget=forms.Select(attrs={"class": "form-control"}), 12 required=False,) # required = False 表示并不需要验证 13 # 重写构造方法 14 def __init__(self,*args,**kwargs): 15 super(Multi_addModelForm,self).__init__(*args,**kwargs) 16 self.fields['menu_id'].choices += models.Menu.objects.values_list('id','title') 17 self.fields['pid_id'].choices += models.Permission.objects.filter(pid__isnull=True).exclude(menu__isnull=True).values_list("id",'title')
3.批量添加view.py
1 def multi_add(request): 2 formset_class = formset_factory(Multi_addModelForm,extra=2) # extra=2表示可以创建2个表单 3 if request.method =="GET": 4 5 formset = formset_class() # 实例化 此处formset里面有5个form 6 return render(request,'multi_add.html',{"formset":formset}) 7 formset = formset_class(data=request.POST) # 这么拿是将html页面中的2行数据都可以拿到 request.POST 必须传入其中 8 # 此刻formset存放的数据 是 [form1对象,form2对象,form3对象...........] 每个form对象中都有(字段,错误信息) 9 10 # 接下来就是进行校验 11 if formset.is_valid(): 12 # 校验成功,就要去保存数据到数据库中 13 print(formset.cleaned_data) # formset.cleaned_data数据里存放如下:[{},{},{}] 14 # [{'title': 'a1', 'url': 'a1', 'name': 'a1', 'menu_id': '', 'pid_id': ''}, {'title': 'a2', 'url': 'a2', 'name': 'a2', 'menu_id': '', 'pid_id': ''}] 这是两行数据 15 # 此时,要注意有个事情,当前端没有写任何数据而提交,也可以走通。则后端拿到的数据为 [{},{}] 1个空的列表 16 17 """ 18 for row in formset.cleaned_data: 19 # 方法一 这种方法一和下面的方法二不推荐,因为如果数据已存在。再去提交,则会报错。唯一索引就会引起错误UNIQUE constraint failed 20 models.Permission.objects.create(**row) # 将前端获取到的数据保存到数据库中 21 方法二: 22 obj = models.Permission(**row) 23 obj.save() 24 # 这两个方法都会出错误。 25 """ 26 # 该运用以下方法 (从源头中得到了灵感) 27 # 注意必须在for循环前面 先将formset.cleaned_data 取出来 28 flag = True 29 post_row_list = formset.cleaned_data # formset.cleaned_data会先去检查formset中有无错误信息,然后将用户数据提取到 30 # 执行到这个步骤,其实说明通过了formset.is_valid() 说明没有错误才执行到此 31 for i in range(0,formset.total_form_count()) # formset.total_form_count()表示formset中有多少个formset对象 row = post_row_list[i] row表示的是前端中每一行拿到的数据 32 # for 循环每次都要去formset.cleaned_data 中首先去检查下有没有错误,有错误就不会执行下去。 因为下面要人为去构建错误,所以必须在for循环上面,先取到formset.cleaned_data。 33 if not row: # 表示该用户没有提交数据,点击后数据库中不增加 34 continue 35 # 通过捕获异常 36 try: 37 obj = models.Permission(**row) 38 obj.validate_unique() # 检查当前对象在数据库是否存在唯一的异常,去数据库看有没有,有则抛异常。 39 obj.save() 40 except Exception as e:# 有错误的话,捕获到e 41 # formset.errors[i] # 表示formset对象中的错误,数据类型:["title":{}.] 42 formset.errors.update(e) # 取到对应的错误信息,用e进行替换 43 flag = False 44 if flag: 45 return HttpResponse("替换成功!") 46 else: 47 return render(request,'multi_add.html',{'formset':formset}) 48 49 return render(request, 'multi_add.html',{'formset':formset})
4.批量添加前端代码
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> <form method="post"> {% csrf_token %} {#注意这个地方需要加下面这个内容#} {{ formset.management_form }} # 要特别注意 <table style="border: 1px solid"> <thead> <tr> <th>标题</th> <th>URL</th> <th>NAME</th> <th>菜单</th> <th>父权限</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> </body> </html>
注意事项:
1.必须加入 {{ formset.management_form }} 否则就会报错,这在使用到formset_factory必须在前端传。
2.这里使用到了两次循环。