formset:适用于对多个表单进行操作,字段需要也可以用model中的表的字段来作为验证规则。
modelformset:适用于对多个表单进行操作,字段需要也可以用model中的表的字段来作为验证规则,速度可能快一些(有待考证)
formset用法:
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
from django import forms from django.forms import widgets from django.core.exceptions import ValidationError class MultiPermissionForm(forms.Form): id = forms.IntegerField( widget=widgets.HiddenInput(), required=False ) title = forms.CharField( widget=widgets.TextInput(attrs={'class': "form-control"}) ) url = forms.CharField( widget=widgets.TextInput(attrs={'class': "form-control"}) ) name = forms.CharField( widget=widgets.TextInput(attrs={'class': "form-control"}) ) menu_id = forms.ChoiceField( choices=[(None, '-----')], widget=widgets.Select(attrs={'class': "form-control"}), required=False, ) pid_id = forms.ChoiceField( choices=[(None, '-----')], widget=widgets.Select(attrs={'class': "form-control"}), 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') def clean_pid_id(self): menu = self.cleaned_data.get('menu_id') pid = self.cleaned_data.get('pid_id') if menu and pid: raise forms.ValidationError('菜单和根权限同时只能选择一个') return pid from django.forms.models import formset_factory from django.shortcuts import render, redirect def add(request): """ 增加 :param request: :return: """ MultiPermissionFormSet = formset_factory(MultiPermissionForm, extra=0) if request.method == 'GET': form = MultiPermissionFormSet() return render(request,'list.html',{'form':form} form = MultiPermissionFormSet(request.post) if form.is_valid(): return redirect('url') else: ......... def edit(request): """ 修改 :param request: :return: """ MultiPermissionFormSet = formset_factory(MultiPermissionForm, extra=0) if request.method == 'GET': form = MultiPermissionFormSet('数据') # formset是支持批量修改的所以 这里传入的数据一定要是可迭代对象,并且里面的 数据类型是字典或者对象 return render(request,'list.html',{'form':form} form = MultiPermissionFormSet(request.post) if form.is_valid(): return redirect('url') else: .........
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
<div class="luffy-container"> <form method="post" action="?type=generate"> {% csrf_token %} {{ form.management_form }} # 这里一定要记得写这一步 <div class="panel panel-default"> <!-- Default panel contents --> <div class="panel-heading"> <i class="fa fa-binoculars" aria-hidden="true"></i> 待新建权限列表 <button class="right btn btn-primary btn-xs" style="padding: 2px 8px;margin: -3px;"> <i class="fa fa-save" aria-hidden="true"></i> 新建 </button> </div> <div class="panel-body" style="color: #9d9d9d;"> 注意:路由系统中自动发现且数据库中不存在的路由。 </div> <table class="table table-bordered"> <thead> <tr> <th>序号</th> <th>名称</th> <th>URL</th> <th>别名</th> <th>所属菜单</th> <th>根权限</th> </tr> </thead> <tbody> {% for form in generate_formset %} <tr> <td style="vertical-align: middle;">{{ forloop.counter }}</td> {% for field in form %} # 这里在渲染标签的时候一定要渲染 id这个标签 这个很重要不然会报错 这里是写了两个fou循环 所以自动渲染了id标签 {% if forloop.first %} <td class="hide"> {% else %} <td> {% endif %} {{ field }}<span style="color: firebrick;">{{ field.errors.0 }}</span> </td> {% endfor %} {% endfor %} </tbody> </table> </div> </form>
modelformset用法:
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
from django import forms from app01.models import StudyRecord class StudyRecordModelForm(forms.ModelForm): class Meta: model = StudyRecord fields = ['student','record','score','homework_note'] from django.shortcuts import render, redirect from django.forms.models import modelformset_factory def changelist_view(self,request): ccid = request.GET.get('ccid') model_formset_cls = modelformset_factory(models.StudyRecord,StudyRecordModelForm,extra=0) queryset = models.StudyRecord.objects.filter(course_record_id=ccid) if request.method == "GET": formset = model_formset_cls(queryset=queryset) # 这里UI定是个可迭代对象,因为modelformset是操作多表的,里面的数据类型可以为字典或者对象 return render(request,'study_record.html',{'formset':formset}) formset = model_formset_cls(data=request.POST) # print(request.POST) if formset.is_valid(): formset.save() return redirect('/stark/crm/studyrecord/list/?ccid=%s' %ccid ) return render(request, 'study_record.html', {'formset': formset})
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
<div class="panel panel-default"> <div class="panel-heading">学习记录</div> <div class="panel-body"> <div style="width: 680px;margin: 0 auto;"> <form method="post"> {% csrf_token %} {{ formset.management_form }} # 这里一定要加这句代码 <table class="table table-bordered"> <thead> <tr> <th>姓名</th> <th>考勤</th> <th>作业成绩</th> <th>作业评语</th> </tr> </thead> <tbody> {% for form in formset %} <tr> {{ form.id }} # 这里只写了一层for循环,所以手动写字段,必须把id字段写上 <td>{{ form.instance.student }}</td> <td>{{ form.record }} {{ form.record.errors.0 }}</td> <td>{{ form.score }} {{ form.score.errors.0 }}</td> <td>{{ form.homework_note }} {{ form.homework_note.errors.0 }}</td> </tr> {% endfor %} </tbody> </table> <input type="submit" value="保存"> </form> </div> </div> </div>
其实和modelform方法差不多,只不过是显示的时候可以直接修改,显示的select的那种模式
from django.forms import modelformset_factory # 展示学习记录 def study_record_list(request, course_record_id): # 这是一个类 里面必填的两个参数,一个是model 一个是ModelForm FormSet = modelformset_factory(models.StudyRecord, StudyRecordForm, extra=0) # extra=0 是把多的那一行去掉 # formset 就是一组组的表单 formset = FormSet(queryset=models.StudyRecord.objects.filter(course_record_id=course_record_id)) if request.method == 'POST': formset = FormSet(request.POST) if formset.is_valid(): formset.save() return redirect(reverse('study_record_list', args=(course_record_id,))) return render(request, 'study_record_list.html', {'formset': formset})
要想在前端(study_record_list.html)展示效果,在前端页面中必须加上下面这些东西:
下面的formset是后端传过来的
对学习记录初始化的过程要进行批量操作, 但是有一个问题,比如我们已经初始化话过一次,但是后来了几个同学,这时候我们在进行初始化的时候就会报错,
已经添加过的不能再添加, 这时候我们就能用到get_or_create(没有才创建)
# 学习记录初始化 def multi_init(self): course_record_ids = self.request.POST.getlist('ids') # 给一个课程记录下的所有学生添加学习记录 for course_record_id in course_record_ids: # 拿到所有的学生 course_record_obj = models.CourseRecord.objects.filter(pk=course_record_id).first() all_student = course_record_obj.re_class.customer_set.all().filter(status='studying') for stuedent in all_student: # models.StudyRecord.objects.create(student=stuedent,course_record_id=course_record_id) # get 获取,没有才创建 后来的学生也能听初始化 已经初始化过的不在创建 (用这种就行) models.StudyRecord.objects.get_or_create(student=stuedent, course_record_id=course_record_id) # 先更新 在创建 # models.StudyRecord.objects.update_or_create(student=stuedent, course_record_id=course_record_id) # # 批量插入 # study_record_list = [] # for student in all_student: # if models.StudyRecord.objects.filter(student=student,course_record_id=course_record_id).exists(): # continue # obj = models.StudyRecord(student=student,course_record_id=course_record_id) # study_record_list.append(obj) # models.StudyRecord.objects.bulk_create(study_record_list,batch_size=10) # 一次插10个
显示的结果
modelforrmset示例:
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
from django import forms from app01.models import StudentStudyRecord class StudentStudyRecordModelForm(forms.ModelForm): class Meta: model = StudentStudyRecord fields = ["score", "homework_note"]
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
from django.shortcuts import render, redirect from django.forms.models import modelformset_factory from app01.app01_forms import StudentStudyRecordModelForm from app01.models import StudentStudyRecord class RecordScoreView(View): def get(self, request, class_study_record_id): model_formset_cls = modelformset_factory(model=StudentStudyRecord, form=StudentStudyRecordModelForm, extra=0) queryset = StudentStudyRecord.objects.filter(classstudyrecord=class_study_record_id) formset = model_formset_cls(queryset=queryset) return render(request, "student/record_score.html", locals()) def post(self, request, class_study_record_id): model_formset_cls = modelformset_factory(model=StudentStudyRecord, form=StudentStudyRecordModelForm, extra=0) queryset = StudentStudyRecord.objects.filter(classstudyrecord=class_study_record_id) # print("request.POST",request.POST) formset = model_formset_cls(request.POST) if formset.is_valid(): formset.save() # print(formset.errors) return redirect(request.path)
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> <link rel="stylesheet" href="/static/bootstrap/css/bootstrap.min.css"> </head> <body> <div class="panel panel-default"> <div class="panel-heading">学习记录</div> <div class="panel-body"> <div style="width: 680px;margin: 0 auto;"> <form method="post" action=""> {% csrf_token %} {{ formset.management_form }} # 这里一定要加这句代码 <table class="table table-bordered"> <thead> <tr> <th>姓名</th> <th>考勤</th> <th>作业成绩</th> <th>作业评语</th> </tr> </thead> <tbody> {% for form in formset %} <tr> {{ form.id }} # 这里只写了一层for循环,所以手动写字段,必须把id字段写上 <td>{{ form.instance.student }}</td> <td>{{ form.instance.get_record_display }} {{ form.record.errors.0 }}</td> <td>{{ form.score }} {{ form.score.errors.0 }}</td> <td>{{ form.homework_note }} {{ form.homework_note.errors.0 }}</td> </tr> {% endfor %} </tbody> </table> <input type="submit" value="保存"> </form> </div> </div> </div> </body> </html>