权限组件(11):基于formset实现批量增加
效果图:
增加页面:
编辑页面:
因为后面要对权限进行批量操作,所以先用这个示例演示下如何实现批量操作
数据库
from django.db import models class Menu(models.Model): """ 菜单表 """ title = models.CharField(verbose_name='菜单名称', max_length=32) icon = models.CharField(verbose_name='图标', max_length=32) def __str__(self): return self.title class Permission(models.Model): """ 权限表 """ title = models.CharField(verbose_name='标题', max_length=32) url = models.CharField(verbose_name='含正则的URL', max_length=128) name = models.CharField(verbose_name='URL的别名', max_length=32, unique=True) menu = models.ForeignKey(verbose_name='所属菜单', to='Menu', null=True, blank=True, help_text='null表示不是菜单;非null表示是二级菜单', on_delete=models.CASCADE) pid = models.ForeignKey(verbose_name='关联的权限', to='Permission', null=True, blank=True, related_name='parents', help_text='对于非菜单权限需要选择一个可以成为菜单的权限,用于做默认展开和选中菜单', on_delete=models.CASCADE) def __str__(self): return self.title
一、配置路由
from django.urls import path from formset import views urlpatterns = [ path('multi/add', views.multi_add), path('multi/edit', views.multi_edit), ]
二、forms表单验证
from django import forms from formset import models class MultiPermissionForm(forms.Form): title = forms.CharField() url = forms.CharField() name = forms.CharField() menu_id = forms.ChoiceField( choices=[(None, '----------')], required=False ) pid_id = forms.ChoiceField( choices=[(None, '----------')], required=False ) def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) self.fields['menu_id'].choices += models.Menu.objects.values_list('id', 'title') self.fields['pid_id'].choices += models.Permission.objects.filter(pid__isnull=True).exclude( menu__isnull=True).values_list('id', 'title') class MultiUpdatePermissionForm(forms.Form): id = forms.IntegerField( widget=forms.HiddenInput() ) title = forms.CharField() url = forms.CharField() name = forms.CharField() menu_id = forms.ChoiceField( choices=[(None, '----------')], required=False ) pid_id = forms.ChoiceField( choices=[(None, '----------')], required=False ) def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) self.fields['menu_id'].choices += models.Menu.objects.values_list('id', 'title') self.fields['pid_id'].choices += models.Permission.objects.filter(pid__isnull=True).exclude( menu__isnull=True).values_list('id', 'title')
三、视图函数
from django.forms import formset_factory from django.shortcuts import render, HttpResponse from formset import models from formset.forms.formset import MultiPermissionForm, MultiUpdatePermissionForm def multi_add(request): """ 批量增加 :param request: :return: """ formset_class = formset_factory(MultiPermissionForm, extra=5) # 在内部生成五个form表单 if request.method == 'GET': formset = formset_class() return render(request, 'multi_add.html', {'formset': formset}) formset = formset_class(data=request.POST) # 储存的所有信息,包括html标签 if formset.is_valid(): no_repeat_field = True # 要把cleaned_data放到for循环上面,因为在下面一旦cleaned_data检测到错误信息就会报错。里面储存了一个个form[{},{},{}......] form_list = formset.cleaned_data # 检查formset中有没有错误信息,没有则将用户提交的数据取到。有错误信息就报错 for num in range(0, formset.total_form_count()): form = form_list[num] # 一个具体的form if not form: continue try: # 下面的方式和model.Permission.object.create(**row)效果一样,这里用这种方式是为了捕获唯一性错误 permission_obj = models.Permission(**form) permission_obj.validate_unique() # 检查当前对象在数据库是否存在唯一的 permission_obj.save() except Exception as e: formset.errors[num].update(e) # 把错误信息放到对应的form里面 no_repeat_field = False if no_repeat_field: return HttpResponse('提交成功') else: return render(request, 'multi_add.html', {'formset': formset}) return render(request, 'multi_add.html', {'formset': formset}) def multi_edit(request): formset_class = formset_factory(MultiUpdatePermissionForm, extra=0) # 默认等于1,如果不想让它多增加一个,就把默认改成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(): no_repeat_field = True form_list = formset.cleaned_data for num in range(0, formset.total_form_count()): form = form_list[num] if not form: continue permission_id = form.pop('id') try: permission_obj = models.Permission.objects.filter(id=permission_id).first() for key, value in form.items(): setattr(permission_obj, key, value) # 更新数据库的字段 permission_obj.validate_unique() permission_obj.save() except Exception as e: formset.errors[num].update(e) no_repeat_field = False if no_repeat_field: return HttpResponse("提交成功") else: return render(request, 'multi_edit.html', {'formset': formset}) return render(request, 'multi_edit.html', {'formset': formset})
四、模板渲染
multi_add.html
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> <link rel="stylesheet" href="https://cdn.bootcss.com/bootstrap/4.0.0/css/bootstrap.css"> </head> <body> <div class="container" style="margin-top: 100px"> <div class="row"> <form action="" method="post" novalidate> {% csrf_token %} {{ formset.management_form }} <table class="table table-hover table-stripped" border="1"> <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> <hr/> <input type="submit" value="提交" class="btn btn-primary"> </form> </div> </div> </body> </html>
multi_edit.html
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> <link rel="stylesheet" href="https://cdn.bootcss.com/bootstrap/4.0.0/css/bootstrap.css"> </head> <body> <div class="container" style="margin-top: 100px"> <div class="row"> <form action="" method="post" novalidate> {% csrf_token %} {{ formset.management_form }} <table class="table table-hover table-stripped" border="1"> <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 %} {% if forloop.first %} {{ field }} {% else %} <td> {{ field }} <span style="color:red">{{ field.errors.0 }}</span> </td> {% endif %} {% endfor %} </tr> {% endfor %} </tbody> </table> <hr/> <input type="submit" value="提交" class="btn btn-primary"> </form> </div> </div> </body> </html>
edit和add的区别是edit多了个id并把id隐藏起来了