前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >Django表单集合----Formset

Django表单集合----Formset

作者头像
菲宇
发布2019-07-02 10:17:50
1.1K0
发布2019-07-02 10:17:50
举报
文章被收录于专栏:菲宇菲宇

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

一、Formset的分类

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

二、如何使用formset_factory

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

代码语言:javascript
复制
#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

代码语言:javascript
复制
# 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:

代码语言:javascript
复制
<form action=”.” method=”POST”>
{{ formset }}
</form>

也可以这样使用:

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

formset_factory()参数解释:

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

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

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

三、如何使用modelformset_factory

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

代码语言:javascript
复制
class StudentStudyRecordModelForm(forms.ModelForm):
    class Meta:
        model=StudentStudyRecord
        fields=["score","homework_note"]

由ModelForm创建formset:

代码语言:javascript
复制
model_formset_cls=modelformset_factory(model=StudentStudyRecord,form=StudentStudyRecordModelForm,extra=0)

views.py

代码语言:javascript
复制
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)

模板:

代码语言:javascript
复制
        <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了。

代码语言:javascript
复制
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。

代码语言:javascript
复制
# 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的实例。

代码语言:javascript
复制
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代码如下:

代码语言:javascript
复制
<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>
本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
原始发表:2019年06月26日,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 一、Formset的分类
  • 二、如何使用formset_factory
  • 三、如何使用modelformset_factory
  • 四、如何使用inlineformset_factory
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档