ModelForm
Posted crazy-zjl
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了ModelForm相关的知识,希望对你有一定的参考价值。
ModelForm
模型的属性与表单属性对应关系
如果模型字段设置了 blank=True
,那么表单字段的 required
属性被设置为 False
,否则 required=True
。
表单字段的 label
设置为模型字段的 verbose_name
,并且首字母大写。
表单字段的 help_text
设置为模型字段的 help_text
。
如果模型字段设置了 choices
,那么表单字段的 widget
会被设置为 Select
,其选项来自模型字段的 choices
。这些选项通常包含一个默认选中的空选项。如果字段设置了必填,则会强制用户进行选择。如果模型字段设置了 blank=False
以及一个明确的 default
值,则表单字段中不会包含空选项(默认会选中 default
值)
error_messages注意事项
在 form field 级别或者 form Meta 级别定义的错误信息优先级总是高于在 model field 级别定义的。
在 model fields 上定义的错误信息只有在 model validation 步骤引发 ValidationError
时才会使用,并且没有在表单级定义相应的错误信息。
您可以通过添加 NON_FIELD_ERRORS
到 ModelForm
内部的 Meta
类的 error_messages
中来覆盖模型验证引发的 NON_FIELD_ERRORS
错误信息
from django.core.exceptions import NON_FIELD_ERRORS from django.forms import ModelForm class ArticleForm(ModelForm): class Meta: error_messages = { NON_FIELD_ERRORS: { ‘unique_together‘: "%(model_name)s‘s %(field_labels)s are not unique.", } }
save()方法
每个 ModelForm
还有一个 save()
方法。此方法根据绑定到表单的数据创建并保存数据库对象。 ModelForm
的子类可接受一个现有的模型实例作为关键字参数 instance
;如果提供了,则 save()
会更新这个实例。如果没有,则 save()
会创建一个对应模型的新实例。
>>> from myapp.models import Article >>> from myapp.forms import ArticleForm # Create a form instance from POST data. >>> f = ArticleForm(request.POST) # Save a new Article object from the form‘s data. >>> new_article = f.save() # Create a form to edit an existing Article, but use # POST data to populate the form. >>> a = Article.objects.get(pk=1) # >>> f = ArticleForm(request.POST, instance=a) >>> f.save()
请注意,如果表单尚未验证,调用 save()
将通过检查 form.errors
来实现验证。如果表单验证不过,则会引发 ValueError
—— 比如,如果 form.errors
返回 True
。
save()
方法接受一个可选参数 commit
,它的值是 True
或者 False
。如果调用 save()
的时候使用 commit=False
,那么它会返回一个尚未保存到数据库的对象。在这种情况下,需要您自己在生成的模型实例上调用 save()
。如果要在保存对象之前对对象执行自定义操作,或者要使用其中一个专用的 model save options,这很有用。 commit
的值默认为 True
。
另一个使用 commit=False
的作用,您可以在模型与另一个模型有多对多关系的时候看到。如果您的模型具有多对多关系,并且在保存表单时指定了 commit=False
,Django无法立即保存多对多关系的表单数据。这是因为实例的多对多数据只有实例在数据库中存在时才能保存。
要解决这个问题,Django会在您每次使用 commit=False
保存表单时,向 ModelForm
子类添加一个 save_m2m()
方法。在您手动保存表单生成的实例后,可以调用 save_m2m()
来保存多对多的表单数据。例如:
# 用POST数据创建一个表单实例 >>> f = AuthorForm(request.POST) # 返回一个尚未保存的实例对象 >>> new_author = f.save(commit=False) # 修改一些数据 >>> new_author.some_field = ‘some_value‘ # 保存这个新的实例 >>> new_author.save() # 调用save_m2m(),保存具有 多对多关系 的数据 >>> f.save_m2m()
只有在您使用 save(commit=False)
的时候才需要调用 save_m2m()
。当您在表单上使用普通的 save()
时,无需调用其他方法,所有数据(包括多对多数据)都会被保存。例如:
# 用POST数据创建表单实例 >>> a = Author() >>> f = AuthorForm(request.POST, instance=a) # 创建并保存这个新实例,不需要在做其他事 >>> new_author = f.save()
除了 save()
和 save_m2m()
方法之外,ModelForm
与普通的表单工作方式一样。例如,用 is_valid()
方法来检查合法性,用 is_multipart()
方法来确定表单是否需要multipart文件上传(之后是否必须将 request.FILES
传递给表单),等等。更多相关信息,请参阅 Binding uploaded files to a form 。
选择要使用的字段
强烈建议您使用 fields
属性来显式设置所有应在表单中编辑的字段。如果不这样做,当一张表单不慎允许用户设置某些字段,尤其是在将新字段添加到模型中时,很容易导致安全问题。根据表单渲染方式的不同,甚至可能不会在网页上显示问题。
另一种方法是自动包含所有字段,其他放入黑名单。
但是,有两种简单的方法保证你不会出现这些安全问题:
-
将
fields
属性设置为特殊值‘__all__‘
以表明需要使用模型中的所有字段。例如:
from django.forms import ModelForm class AuthorForm(ModelForm): class Meta: model = Author fields = ‘__all__‘
2. 将 ModelForm
中Meta类的 exclude
属性设置为表单中需要排除的字段列表。
例如:
class PartialAuthorForm(ModelForm): class Meta: model = Author #除了"title"字段,其他字段都需要显示 exclude = [‘title‘]
不管使用哪一种,字段会按模型中定义的顺序在表单中出现, ManyToManyField
会排在最后。
另外,Django有个规则:如果您在模型字段中定义了 editable=False
, *任何*使用 ModelForm
给该模型创建的表单都不会包含这个字段
注意: 任何没在上面逻辑中包含的表单字段都会不被表单的 save() 方法处理。 另外,如果手动将排除的字段添加回表单,它们也不会被模型实例初始化。 Django会阻止任何尝试保存不完整模型的行为,所以如果模型不允许未被表单实例化的字段(如:被排除的字段)为空, 并且没有为该字段提供默认值,那么任何尝试用这种字段的 ModelForm 的 save() 方法都会失败。为了避免这种情况, 您必须使用初始值实例化您模型中未被表单实例化,但又必填的字段: author = Author(title=‘Mr‘) form = PartialAuthorForm(request.POST, instance=author) form.save() 或者,您可以使用 save(commit=False) 然后手动设置其他必填字段: form = PartialAuthorForm(request.POST) author = form.save(commit=False) author.title = ‘Mr‘ author.save()
覆盖默认字段
form
和model
模型的默认字段类型都是相对应的。如果您的模型中有一个 DateField
,您可能希望在表单中将它展示为 DateField
。 ModelForm
可以让您灵活地改变给定模型的表单字段
要为字段指定自定义组件,请使用内部 Meta
类的 widgets
属性。它可以是一个映射字段名到组件类或组件实例的字典。
例如,如果您希望 Author
的 name
属性的 CharField
由 <textarea>
代替默认的 <input type="text">
来表示,您可以重写字段的部件:
from django.forms import ModelForm, Textarea from myapp.models import Author class AuthorForm(ModelForm): class Meta: model = Author fields = (‘name‘, ‘title‘, ‘birth_date‘) widgets = { ‘name‘: Textarea(attrs={‘cols‘: 80, ‘rows‘: 20}), }
widgets
字典接受组件实例(例如, Textarea(...)
)或者类(例如, Textarea
)。
同样的,如果您想进一步自定义一个字段,还可以指定内部Meta类的 labels
、 help_texts
和 error_messages
属性。
例如您想自定义 name
字段中所有面向用户的字符文本:
gettext和gettext_lazy区别: 标准翻译:使用函数 gettext() 来指定一个翻译字符串 惰性翻译:使得其中的值只有在访问时才会被翻译,而不是在gettext_lazy() 被调用时翻译 参考:https://cloud.tencent.com/developer/article/1368969 from django.utils.translation import gettext_lazy as _ class AuthorForm(ModelForm): class Meta: model = Author fields = (‘name‘, ‘title‘, ‘birth_date‘) labels = { ‘name‘: _(‘Writer‘), } help_texts = { ‘name‘: _(‘Some useful help text.‘), } error_messages = { ‘name‘: { ‘max_length‘: _("This writer‘s name is too long."), }, }
您还可以指定 field_classes
来自定义表单实例化的字段类型:
例如,如果您想对 slug
字段使用 MySlugFormField
,您可以这样做:
from django.forms import ModelForm from myapp.models import Article class ArticleForm(ModelForm): class Meta: model = Article fields = [‘pub_date‘, ‘headline‘, ‘content‘, ‘reporter‘, ‘slug‘] field_classes = { ‘slug‘: MySlugFormField, }
最后,如果您想完全控制一个字段(包括它的type, validators, required, 等等),您可以通过声明指定字段来做到这一点,就像在一个普通的 Form
中那样声明。
如果您想指定一个字段的验证器,可以通过声明定义该字段并设置其 validators
参数来实现:
from django.forms import CharField, ModelForm from myapp.models import Article class ArticleForm(ModelForm): slug = CharField(validators=[validate_slug]) class Meta: model = Article fields = [‘pub_date‘, ‘headline‘, ‘content‘, ‘reporter‘, ‘slug‘]
启用对字段的本地化
默认情况下, ModelForm
中的字段不会本地化他们的数据。要为字段启用本地化,您可以在 Meta
类中使用 localized_fields
属性
>>> from django.forms import ModelForm >>> from myapp.models import Author >>> class AuthorForm(ModelForm): ... class Meta: ... model = Author ... localized_fields = (‘birth_date‘,)
如果 localized_fields
设置为特殊值 ‘__all__‘
,则所有字段都将被本地化。
表单继承
与普通表单一样,您可以通过继承它们来扩展和重用 ModelForms
。如果您需要在父类中声明额外字段或额外方法以用于从模型派生的多个表单中,则此方法非常有用。例如,使用之前的 ArticleForm
类。
>>> class EnhancedArticleForm(ArticleForm): ... def clean_pub_date(self): ... ...
这会创建一个与 ArticleForm
行为相同的表单,除了 pub_date
字段会有一些额外的验证和cleaning。
如果要更改 Meta.fields
或 Meta.exclude
列表,您也可以继承父类的内部 Meta
类:
>>> class RestrictedArticleForm(EnhancedArticleForm): ... class Meta(ArticleForm.Meta): ... exclude = (‘body‘,)
这相比 EnhancedArticleForm
增加了额外方法,并修改了原始的 ArticleForm.Meta
以删除一个字段。
然而,有几项需要注意。
-
适用于普通的Python名称解析规则。如果您有多个声明
Meta
内部类的基类,就是说如果声明了子类的Meta
就会使用它,否则就用第一个父类的Meta
。(和类的继承类似) - 可以同时继承
Form
和ModelForm
,但是,您必须确保ModelForm
在MRO中出现在首位。这是因为这些类依赖于不同的元类,而一个类只能有一个元类。 - 通过在子类上将名称设置为
None
,可以声明性地移除从父类继承的Field
。
您只能使用这种技术排除父类中声明定义的字段;它不会阻止 ModelForm
元类生成默认字段
提供初始值
与普通表单一样,可以在实例化表单时通过指定 initial
参数来指定表单的初始值。以这种方式提供的初始值会覆盖表单字段的初始值以及对应模型实例的初始值。例如:
>>> article = Article.objects.get(pk=1) >>> article.headline ‘My headline‘ >>> form = ArticleForm(initial={‘headline‘: ‘Initial headline‘}, instance=article) >>> form[‘headline‘].value() ‘Initial headline‘
ModelForm的工厂函数
您可以不使用类定义,而是使用独立函数 modelform_factory()
来创建给定模型的表单。如果您没有很多自定义设置,这可能会更方便:
>>> from django.forms import modelform_factory >>> from myapp.models import Book >>> BookForm = modelform_factory(Book, fields=("author", "title"))
这也可以用来对已有表单进行简单的修改,例如给某个字段指定使用组件:
>>> from django.forms import Textarea >>> Form = modelform_factory(Book, form=BookForm, ... widgets={"title": Textarea()})
要包含的字段可以使用 fields
和 exclude
关键字参数或 ModelForm
内部的 Meta
类中相应的属性来指定。
... 或者为个别字段启用本地化功能:
>>> Form = modelform_factory(Author, form=AuthorForm, localized_fields=("birth_date",))
模型表单集
和 普通表单集 一样,Django提供了几个增强的formset类,可以很方便地配合Django模型使用。让我们重用下上面的 Author
模型:
>>> from django.forms import modelformset_factory >>> from myapp.models import Author >>> AuthorFormSet = modelformset_factory(Author, fields=(‘name‘, ‘title‘))
使用 fields
参数限制formset仅使用给定的字段。或者,您可以使用排除法,指定排除哪些字段:
>>> AuthorFormSet = modelformset_factory(Author, exclude=(‘birth_date‘,))
这将创建一个能够处理与 Author
模型相关数据的formset。它运行起来就像一个普通的formset:
>>> formset = AuthorFormSet() >>> print(formset) <input type="hidden" name="form-TOTAL_FORMS" value="1" id="id_form-TOTAL_FORMS"><input type="hidden" name="form-INITIAL_FORMS" value="0" id="id_form-INITIAL_FORMS"><input type="hidden" name="form-MAX_NUM_FORMS" id="id_form-MAX_NUM_FORMS"> <tr><th><label for="id_form-0-name">Name:</label></th><td><input id="id_form-0-name" type="text" name="form-0-name" maxlength="100"></td></tr> <tr><th><label for="id_form-0-title">Title:</label></th><td><select name="form-0-title" id="id_form-0-title"> <option value="" selected>---------</option> <option value="MR">Mr.</option> <option value="MRS">Mrs.</option> <option value="MS">Ms.</option> </select><input type="hidden" name="form-0-id" id="id_form-0-id"></td></tr>
注意:
modelformset_factory() 使用 formset_factory() 来生成表单集。
这意味着模型formset只是一个知道如何与指定模型交互的普通formset的扩展。
更改查询集
默认情况下,当您创建一个模型formset时,formset将使用一个包含模型中所有对象(例如 Author.objects.all()
)的查询集。你可以通过使用 queryset
参数来覆盖这一行为:
>>> formset = AuthorFormSet(queryset=Author.objects.filter(name__startswith=‘O‘))
或者,您可以创建一个子类,然后在 __init__
中设置 self.queryset
:
from django.forms import BaseModelFormSet from myapp.models import Author class BaseAuthorFormSet(BaseModelFormSet): def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) self.queryset = Author.objects.filter(name__startswith=‘O‘)
然后,将你的 BaseAuthorFormSet
类传递给工厂函数:
>>> AuthorFormSet = modelformset_factory( ... Author, fields=(‘name‘, ‘title‘), formset=BaseAuthorFormSet)
如果您想返回一个不包含 任何 已存在模型实例的formset,您可以指定一个空的QuerySet:
>>> AuthorFormSet(queryset=Author.objects.none())
更改表单
默认情况下,当您使用 modelformset_factory
时,程序会用 modelform_factory()
创建一个模型表单。这通常在指定自定义模型表单时很有用。例如,您可以创建一个具有自定义验证的自定义模型表单:
class AuthorForm(forms.ModelForm): class Meta: model = Author fields = (‘name‘, ‘title‘) def clean_name(self): # custom validation for the name field ...
然后,将您的模型表单传递给工厂函数
AuthorFormSet = modelformset_factory(Author, form=AuthorForm)
并不是总需要自定义模型表单。 modelformset_factory
函数有几个参数传递给 modelform_factory
,如下所述。
在表单中使用 widgets
指定部件
使用 widgets
参数,您可以设置一个字典值来为 ModelForm
指定字段自定义部件。这与 ModelForm
内部 Meta
类中 widgets
字典的工作方式一样:
>>> AuthorFormSet = modelformset_factory( ... Author, fields=(‘name‘, ‘title‘), ... widgets={‘name‘: Textarea(attrs={‘cols‘: 80, ‘rows‘: 20})})
使用 localized_fields
来启用字段本地化
您可以使用 localized_fields
参数为表单中的字段启用本地化。
>>> AuthorFormSet = modelformset_factory( ... Author, fields=(‘name‘, ‘title‘, ‘birth_date‘), ... localized_fields=(‘birth_date‘,))
如果 localized_fields
设置为特殊值 ‘__all__‘
,则所有字段都将被本地化。
提供初始值
与常规的formset一样,在实例化modelformset_factory()返回的modelformset类时,可以通过指定初始参数在formset中为表单指定初始数据。然而,对于model formset,初始值只适用于额外的表单,即不附加到现有模型实例的表单。如果initial的长度超过了额外表单的数量,则忽略多余的初始数据。如果用户没有更改带有初始数据的额外表单,则不会对其进行验证或保存。
在formset中保存对象
与使用 ModelForm 一样,您可以将数据保存为 model 对象。这是通过formset的save()方法完成的:
# Create a formset instance with POST data. >>> formset = AuthorFormSet(request.POST) # Assuming all is valid, save the data. >>> instances = formset.save()
save()
方法返回已保存到数据库的实例。如果绑定数据中给定实例的数据没有发生变化,那么该实例将不会保存到数据库中,也不会包含在返回值中(实例,在上面的示例中)。
当表单中缺少字段(例如,因为它们被排除在外)时, save()
方法不会设置这些字段。在选择要使用的字段时,您可以找到关于这个限制的更多信息,它也适用于常规的 ModelForms
。
通过 commit=False
返回未保存的模型实例:
# don‘t save to the database >>> instances = formset.save(commit=False) >>> for instance in instances: ... # do something with instance ... instance.save()
这使您能够在将数据保存到数据库之前将数据附加到实例。如果您的表单集包含 ManyToManyField
,您还需要调用formset.save_m2m()
来确保正确保存多对多关系。
在调用 save()
之后,您的模型表单集合将具有三个新属性,包含表单集的更改
models.BaseModelFormSet.
changed_objects
¶
models.BaseModelFormSet.
deleted_objects
¶
models.BaseModelFormSet.
new_objects
¶
限制可编辑对象的数量
与常规的表单集一样,您可以使用max_num和额外的参数来modelformset_factory()
,以限制显示的额外表单的数量。
max_num
不阻止显示现有对象:
另外, extra=0
并不妨碍创建新的模型实例,因为您可以使用javascript添加额外的表单,或者仅仅发送额外的POST数据。formset还没有为“仅编辑”视图提供阻止创建新实例的功能。
如果 max_num
的值大于现有相关对象的数量,则在forms总数不超过max_num的情况下,将向formset添加额外空白表单:
>>> AuthorFormSet = modelformset_factory(Author, fields=(‘name‘,), max_num=4, extra=2) >>> formset = AuthorFormSet(queryset=Author.objects.order_by(‘name‘)) >>> for form in formset: ... print(form.as_table()) <tr><th><label for="id_form-0-name">Name:</label></th><td><input id="id_form-0-name" type="text" name="form-0-name" value="Charles Baudelaire" maxlength="100"><input type="hidden" name="form-0-id" value="1" id="id_form-0-id"></td></tr> <tr><th><label for="id_form-1-name">Name:</label></th><td><input id="id_form-1-name" type="text" name="form-1-name" value="Paul Verlaine" maxlength="100"><input type="hidden" name="form-1-id" value="3" id="id_form-1-id"></td></tr> <tr><th><label for="id_form-2-name">Name:</label></th><td><input id="id_form-2-name" type="text" name="form-2-name" value="Walt Whitman" maxlength="100"><input type="hidden" name="form-2-id" value="2" id="id_form-2-id"></td></tr> <tr><th><label for="id_form-3-name">Name:</label></th><td><input id="id_form-3-name" type="text" name="form-3-name" maxlength="100"><input type="hidden" name="form-3-id" id="id_form-3-id"></td></tr>
max_num
的值 None
(默认值),它限制最多显示(1000)张表单,其实这相当于没有限制。
在view中使用 model formset
模型表单集非常类似于表单集。假设我们想要呈现一个编辑Author模型实例的formset:
from django.forms import modelformset_factory from django.shortcuts import render from myapp.models import Author def manage_authors(request): AuthorFormSet = modelformset_factory(Author, fields=(‘name‘, ‘title‘)) if request.method == ‘POST‘: formset = AuthorFormSet(request.POST, request.FILES) if formset.is_valid(): formset.save() # do something. else: formset = AuthorFormSet() return render(request, ‘manage_authors.html‘, {‘formset‘: formset})
正如您所看到的,模型表单集的视图逻辑与“普通”表单集的视图逻辑并没有太大的不同。唯一的区别是,我们调用 formset.save()
将数据保存到数据库中。(上面在将对象保存到formset中已经描述了这一点。)
在 ModelFormSet
重新定义 clean()
就像使用 ModelForms
一样,默认情况下, ModelFormSet
的 clean()
方法将验证formset中的任何项都不会违反模型上的唯一约束( unique
, unique_together
or unique_for_date|month|year
)。如果您想覆盖 ModelFormSet
上的 clean()
方法并维持该验证,您必须调用父类的 clean
方法:
from django.forms import BaseModelFormSet class MyModelFormSet(BaseModelFormSet): def clean(self): super().clean() # example custom validation across forms in the formset for form in self.forms: # your custom formset validation ...
还请注意,在完成此步骤之前,已经为每个表单创建了单独的模型实例。修改表单中的值。cleaned_data
不足以影响保存的值。如果您希望修改 ModelFormSet.clean()
中的值,您必须修改 form.instance
:
from django.forms import BaseModelFormSet class MyModelFormSet(BaseModelFormSet): def clean(self): super().clean() for form in self.forms: name = form.cleaned_data[‘name‘].upper() form.cleaned_data[‘name‘] = name # update the instance value. form.instance.name = name
使用自定义queryset
如前所述,您可以覆盖model formset使用的默认queryset:
from django.forms import modelformset_factory from django.shortcuts import render from myapp.models import Author def manage_authors(request): AuthorFormSet = modelformset_factory(Author, fields=(‘name‘, ‘title‘)) if request.method == "POST": formset = AuthorFormSet( request.POST, request.FILES, queryset=Author.objects.filter(name__startswith=‘O‘), ) if formset.is_valid(): formset.save() # Do something. else: formset = AuthorFormSet(queryset=Author.objects.filter(name__startswith=‘O‘)) return render(request, ‘manage_authors.html‘, {‘formset‘: formset})
注意,在本例中,我们在 POST
和 GET
案例中都传递了 queryset
参数。
在模板中使用表单集
在Django模板中呈现表单集有三种方法。
首先,您可以让formset完成大部分工作:
<form method="post"> {{ formset }} </form>
其次,您可以手动渲染表单集,让表单自行处理:
<form method="post"> {{ formset.management_form }} {% for form in formset %} {{ form }} {% endfor %} </form>
当您自己手动渲染表单时,请确保渲染如上所示的管理表单。参见 management form documentation。
第三,您可以手动渲染每个字段:
<form method="post"> {{ formset.management_form }} {% for form in formset %} {% for field in form %} {{ field.label_tag }} {{ field }} {% endfor %} {% endfor %} </form>
如果选择使用第三种方法,并且不使用 {% for %}
循环遍历字段,则需要呈现主键字段。例如,如果您正在渲染模型的 name
和 age
字段:
<form method="post"> {{ formset.management_form }} {% for form in formset %} {{ form.id }} <ul> <li>{{ form.name }}</li> <li>{{ form.age }}</li> </ul> {% endfor %} </form>
注意,我们需要显式地渲染 {{ form.id }}
。这可以确保在POST情况下,model formset将正确工作。(本例假设一个名为id的主键。如果您已经显式地定义了自己的主键,而这个主键不是id,那么请确保它被渲染。)
内联表单集
内联表单集是模型表单集之上的一个小抽象层。这简化了通过外键处理相关对象的情况。假设你有这两个模型:
from django.db import models class Author(models.Model): name = models.CharField(max_length=100) class Book(models.Model): author = models.ForeignKey(Author, on_delete=models.CASCADE) title = models.CharField(max_length=100)
如果您想创建一个允许您编辑属于某个特定作者的图书的formset,您可以这样做:
>>> from django.forms import inlineformset_factory >>> BookFormSet = inlineformset_factory(Author, Book, fields=(‘title‘,)) >>> author = Author.objects.get(name=‘Mike Royko‘) >>> formset = BookFormSet(instance=author)
BookFormSet的前缀是‘book_set‘ (<model name>_set)。如果Book的foreign - key 到 Author有一个related_name,那么就用它。
在InlineFormSet上重写方法
当重写 InlineFormSet
上的方法时,您应该子类化BaseInlineFormSet
,而不是 BaseModelFormSet
。
例如,如果您想覆盖 clean()
:
from django.forms import BaseInlineFormSet class CustomInlineFormSet(BaseInlineFormSet): def clean(self): super().clean() # example custom validation across forms in the formset for form in self.forms: # your custom formset validation ...
参见在ModelFormSet上重新定义clean()。
然后,当您创建您的内联表单集时,传入可选参数 formset
:
>>> from django.forms import inlineformset_factory >>> BookFormSet = inlineformset_factory(Author, Book, fields=(‘title‘,), ... formset=CustomInlineFormSet) >>> author = Author.objects.get(name=‘Mike Royko‘) >>> formset = BookFormSet(instance=author)
同一个模型多个外键
如果您的模型包含同一个模型的多个外键,则需要使用 fk_name
手动解决歧义。例如,考虑以下模型:
class Friendship(models.Model): from_friend = models.ForeignKey( Friend, on_delete=models.CASCADE, related_name=‘from_friends‘, ) to_friend = models.ForeignKey( Friend, on_delete=models.CASCADE, related_name=‘friends‘, ) length_in_months = models.IntegerField()
要解决这个问题,可以使用fk_name
来 inlineformset_factory()
:
>>> FriendshipFormSet = inlineformset_factory(Friend, Friendship, fk_name=‘from_friend‘, ... fields=(‘to_friend‘, ‘length_in_months‘))
在view中使用内联表单集
您可能希望提供一个视图,该视图允许用户编辑模型的相关对象。你可以这样做:
def manage_books(request, author_id): author = Author.objects.get(pk=author_id) BookInlineFormSet = inlineformset_factory(Author, Book, fields=(‘title‘,)) if request.method == "POST": formset = BookInlineFormSet(request.POST, request.FILES, instance=author) if formset.is_valid(): formset.save() # Do something. Should generally end with a redirect. For example: return HttpResponseRedirect(author.get_absolute_url()) else: formset = BookInlineFormSet(instance=author) return render(request, ‘manage_books.html‘, {‘formset‘: formset})
注意,在POST和GET案例中是如何传递实例的。
学习自用,欢迎大神评论、指正
更多详情见Django文档之ModelForm:
https://docs.djangoproject.com/zh-hans/2.1/topics/forms/modelforms/
以上是关于ModelForm的主要内容,如果未能解决你的问题,请参考以下文章