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.这里使用到了两次循环。

 

posted on 2022-08-02 08:31  一先生94  阅读(46)  评论(0编辑  收藏  举报

导航