django---表单集合Formset
Formset(表单集)是多个表单的集合。Formset在Web开发中应⽤很普遍,它可以让⽤户在同⼀个页⾯上提交多张表单,⼀键添加多个数据,⽐如⼀个页⾯上添加多个⽤户信息。今天⼩编我就介绍下Django Formset的基础知识,Formset的分类以及如何使⽤Formset。
为什么要使⽤Django Formset
我们先来下看下Django中不使⽤Formset情况下是如何在同⼀页⾯上⼀键提交2张或多张表单的。我们在模板中给每个表单取不同的名字,如form1和form2(如下⾯代码所⽰)。注: form1和form2分别对应forms.py⾥的Form1()和Form2()。
<form >
{{ form1.as_p }}
{{ form2.as_p }}
</form>
⽤户点击提交后,我们就可以在视图⾥了对⽤户提交的数据分别处理。
hod == 'POST':
form1 = Form1( request.POST,prefix="form1")
form2 = Form2( request.POST,prefix="form2")
if form1.is_valid() or form2.is_valid():
pass
else:
form1 = Form1(prefix="form1")
form2 = Form2(prefix="form2")
这段代码看似并不复杂,然⽽当表单数量很多或不确定时,这个代码会⾮常冗长。我们希望能控制表单的数量,这是我们就可以⽤Formset 了。
Formset的分类
Django针对不同的formset提供了3种⽅法: formset_factory, modelformset_factory和inlineformset_factory。我们接下来分别看下如何使⽤它们。
如何使⽤formset_factory
对于继承forms.Form的⾃定义表单,我们可以使⽤formset_factory。我们可以通过设置extra和max_num属性来确定我们想要展⽰的表单数量。注意: max_num优先级⾼于extra。⽐如下例中,我们想要显⽰3个空表单(extra=3),但最后只会显⽰2个空表单,因为max_num=2。
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 .forms 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):
hod == 'POST':
formset = BookFormSet(request.POST, request.FILES)
if formset.is_valid():
# do something with the formset.cleaned_data
pass
else:
formset = BookFormSet()
django项目实例return render(request, 'manage_books.html', {'formset': formset})
模板⾥可以这样使⽤formset。
<form action=”.” method=”POST”>
{{ formset }}
</form>
也可以这样使⽤。
<form method="post">
{{ formset.management_form }}
<table>
{% for form in formset %}
{{ form }}
{% endfor %}
</table>
</form>
如何使⽤modelformset_factory
Formset也可以直接由模型model创建,这时你需要使⽤modelformset_factory。你可以指定需要显⽰的字段和表单数量。
from django.forms import modelformset_factory
dels import Author
AuthorFormSet = modelformset_factory(
Author, fields=('name', 'title'), extra = 3)
当然上⾯⽅法我并不推荐,因为对单个表单添加验证⽅法⾮常不⽅便。我更喜欢的⽅式先创建⾃定义的ModelForm,添加单个表单验证,然后再利⽤modelformset_factory创建formset。
class AuthorForm(forms.ModelForm):
class Meta:
model = Author
fields = ('name', 'title')
def clean_name(self):
# custom validation for the name field
...
由ModelForm创建formset:
AuthorFormSet = modelformset_factory(Author, form=AuthorForm)
在模板和视图⾥使⽤formset的⽅法与前⾯的例⼦是⼀样的。
如何使⽤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)
hod == "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):
hod == "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 }}
{{ _form_errors }}
{% for form in ingredient_formset %}
{{ s }}
{{ form.name.label_tag }}
{{ form.name }}
</div>
{% endfor %}
</fieldset>
<input type="submit" value="Add recipe" class="submit" />
</form>
最后的效果如下图所⽰:
整个formset的验证
formset由多个表单组成,单个表单的验证可以通过⾃定义的clean⽅法来完成,然⽽有时我们需要对整个formset的数据进⾏验证。⼀个常见例⼦就是去重。
⽐如下⾯例⼦中⽤户⼀次性提交多篇⽂章标题后,我们需要检查title是否已重复。我们先定义⼀个BaseFormSet,然后使⽤
formset=BaseArticleFormSet添加formset的验证。
from django.forms import BaseFormSet
from django.forms import formset_factory
from myapp.forms import ArticleForm
class BaseArticleFormSet(BaseFormSet):
def clean(self):
"""Checks that no two articles have the same title."""
if s):
return
titles = []
for form in self.forms:
title = form.cleaned_data['title']
if title in titles:
raise forms.ValidationError("Articles in a set must have distinct titles.")
titles.append(title)
ArticleFormSet = formset_factory(ArticleForm, formset=BaseArticleFormSet)
给Formset添加额外字段
在BaseFormSet⾥我们不仅可以添加formset的验证,⽽且可以添加额外的字段,如下所⽰:
from django.forms import BaseFormSet
from django.forms import formset_factory
from myapp.forms import ArticleForm
class BaseArticleFormSet(BaseFormSet):
def add_fields(self, form, index):
super().add_fields(form, index)
form.fields["my_field"] = forms.CharField()
ArticleFormSet = formset_factory(ArticleForm, formset=BaseArticleFormSet) ---------------------
作者:⼤江狗
来源:CSDN
原⽂:blog.csdn/weixin_42134789/article/details/81505983
版权声明:本⽂为博主原创⽂章,转载请附上博⽂链接!
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。
发表评论