1. 程式人生 > >Django表單集合----Formset

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>