zoukankan      html  css  js  c++  java
  • Django表单集合----Formset

    概述:Formset(表单集)是多个表单的集合。Formset在Web开发中应用很普遍,它可以让用户在同一个页面上提交多张表单,一键添加多个数据,比如一个页面上添加多个用户信息,下面将会详细讲述如何使用Formset。

    一、Formset的分类

    Django针对不同的formset提供了三种方法:formset_factory,modelformset_factory和inlineformset_factory。

    二、如何使用formset_factory

    对于继承forms.Form的自定义表单,我们可以使用formset_factory。

    #models.py
    
    from django import forms
    
    class BookForm(forms.Form):
        name = forms.CharField(max_length=100)
        title = forms.CharField()
        pub_date = forms.DateField(required=False)
     
     
    # forms.py - build a formset of books
     
    from django.forms import formset_factory
    from myapp.models import BookForm
     
    # extra: 想要显示空表单的数量
    # max_num: 表单显示最大数量,可选,默认1000
     
    BookFormSet = formset_factory(BookForm, extra=3, max_num=2)

    在视图文件views.py里,我们可以像使用form一样使用formset

    # views.py - formsets example.
    
    from .forms import BookFormSet
    from django.shortcuts import render
     
    def manage_books(request):
        if request.method == 'POST':
            formset = BookFormSet(request.POST)
            if formset.is_valid():
                # do something with the formset.cleaned_data
                pass
        else:
            formset = BookFormSet()
            #如果想传入初始数据可设置initial = [{'name':'python','pub_date':'北京出版社'}]
        return render(request, 'manage_books.html', {'formset': formset})

    注意:如果使用了 initial 来显示formset,那么您需要在处理formset提交时传入相同的 initial ,以便formset检测用户更改了哪些表单。例如,您可能有这样的: BookFormSet(request.POST, initial=[...])

    模板里可以这样使用formset:

    <form action=”.” method=”POST”>
    {{ formset }}
    </form>

    也可以这样使用:

    <form method="post">
        {{ formset.management_form }}   #一定要加这行代码
        <table>
            {% for form in formset %}
            {{ form }}
            {% endfor %}
        </table>
    </form>

    formset_factory()参数解释:

    1、如果 max_num 的值大于初始数据现有数量,那空白表单可显示的数量取决于 extra 的数量,只要总表单数不超过 max_num 。例如, extra=2 , max_num=2 并且formset有一个 initial 初始化项,则会显示一张初始化表单和一张空白表单。

    2、如果初始数据项的数量超过 max_num ,那么 max_num 的值会被无视,所有初始数据表单都会显示,并且也不会有额外的表单显示。例如,假设 extra=3 , max_num=1 并且formset有两个初始化项,那么只会显示两张有初始化数据的表单。

    3、max_num 的值 None (默认值),它限制最多显示(1000)张表单,其实这相当于没有限制。

    三、如何使用modelformset_factory

    Formset也可以直接由模型model创建,这时你需要使用modelformset_factory。你可以指定需要显示的字段和表单数量。

    class StudentStudyRecordModelForm(forms.ModelForm):
        class Meta:
            model=StudentStudyRecord
            fields=["score","homework_note"]

    由ModelForm创建formset:

    model_formset_cls=modelformset_factory(model=StudentStudyRecord,form=StudentStudyRecordModelForm,extra=0)

    views.py

    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)
            formset=model_formset_cls(request.POST)
            if formset.is_valid():
                formset.save()
            return redirect(request.path)

    模板:

            <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 }}
                                    <td>{{ form.instance.student }}</td>
                                    <td>{{ form.instance.get_record_display }} </td>
                                    <td>{{ form.score }} </td>
                                    <td>{{ form.homework_note }}</td>
                                </tr>
                            {% endfor %}
                            </tbody>
                        </table>
                        <input type="submit" value="保存">
                    </form>

    四、如何使用inlineformset_factory

    试想我们有如下recipe(菜谱)模型,Recipe(菜谱)与Ingredient(原料)是一对多的关系。一般的formset只允许我们一次性提交多个Recipe或多个Ingredient。但如果我们希望同一个页面上添加一个菜谱(Recipe)和多个原料(Ingredient),这时我们就需要用使用inlineformset了。

    from django.db import models
     
     
    class Recipe(models.Model):
        title = models.CharField(max_length=255)
        description = models.TextField()
     
     
    class Ingredient(models.Model):
        recipe = models.ForeignKey(Recipe, on_delete=models.CASCADE, related_name='ingredient')
        name = models.CharField(max_length=255)

    利用inlineformset_factory创建formset的方法如下所示。该方法的第一个参数和第二个参数都是模型,其中第一个参数必需是ForeignKey。

    # forms.py
    
    from django.forms import ModelForm
    from django.forms import inlineformset_factory
     
    from .models import Recipe, Ingredient, Instruction
     
     
    class RecipeForm(ModelForm):
        class Meta:
            model = Recipe
            fields = ("title", "description",)
     
     
    IngredientFormSet = inlineformset_factory(Recipe, Ingredient, fields=('name',),
                                              extra=3, can_delete=False, max_num=5)

    views.py中使用formset创建和更新recipe(菜谱)的代码如下。在对IngredientFormSet进行实例化的时候,必需指定recipe的实例。

    def recipe_update(request, pk):       #更新
        recipe = get_object_or_404(Recipe, pk=pk)
        if request.method == "POST":
            form = RecipeForm(request.POST, instance=recipe)
     
            if form.is_valid():
                recipe = form.save()
                ingredient_formset = IngredientFormSet(request.POST, instance=recipe)
     
                if ingredient_formset.is_valid():
                    ingredient_formset.save()
     
            return redirect('/recipe/')
        else:
            form = RecipeForm(instance=recipe)
            ingredient_formset = IngredientFormSet(instance=recipe)
     
        return render(request, 'recipe/recipe_update.html', {'form': form,
                                                             'ingredient_formset': ingredient_formset,
                                                          })
     
    def recipe_add(request):      #创建
        if request.method == "POST":
            form = RecipeForm(request.POST)
     
            if form.is_valid():
                recipe = form.save()
                ingredient_formset = IngredientFormSet(request.POST, instance=recipe)
     
                if ingredient_formset.is_valid():
                    ingredient_formset.save()
     
            return redirect('/recipe/')
        else:
            form = RecipeForm()
            ingredient_formset = IngredientFormSet()
     
        return render(request, 'recipe/recipe_add.html', {'form': form,
                                                          'ingredient_formset': ingredient_formset,
                                                          })
     

    模板recipe/recipe_add.html代码如下:

    <h1>Add Recipe</h1>
    <form action="." method="post">
        {% csrf_token %}
        
        {{ form.as_p }}
        
        <fieldset>
            <legend>Recipe Ingredient</legend>
            {{ ingredient_formset.management_form }}
            {{ ingredient_formset.non_form_errors }}
            {% for form in ingredient_formset %}
                    {{ form.name.errors }}
                    {{ form.name.label_tag }}
                    {{ form.name }}
                </div>
          {% endfor %}
        </fieldset>
     
        <input type="submit" value="Add recipe" class="submit" />
    </form>
  • 相关阅读:
    以太坊测试网络搭建以及RPC服务开启-配置注意事项
    AD预测论文研读系列1
    DenseNet 论文阅读笔记
    Deep learning with Python 学习笔记(7)
    ADNI数据
    利用卷积神经网络进行阿尔茨海默病分类的神经影像模式融合 论文研读笔记
    阿尔茨海默病早期诊断的脑结构分级图 论文研读笔记
    GoogLeNetv4 论文研读笔记
    ResNet 论文研读笔记
    GoogLeNetv3 论文研读笔记
  • 原文地址:https://www.cnblogs.com/fengchong/p/10002628.html
Copyright © 2011-2022 走看看