从模型创建表单 — Django 6.0.4 documentation(2026)
创始人
2026-06-02 00:20:52
0
Django教程

从模型创建表单

ModelForm

class ModelForm[source]

如果您正在构建一个数据库驱动的应用程序,那么您很有可能会用到与Django模型密切相关的表单。例如,您可能有一个 BlogComment 模型,并且您想创建一个让用户提交评论的表单。在这种情况下,在表单中定义字段类型是多余的,因为您已经在模型中定义了字段。

因此,Django 提供了一个辅助类让你可以从一个 Django 模型创建一个 Form 类。

例如:

>>> from django.forms import ModelForm
>>> from myapp.models import Article

# Create the form class.
>>> class ArticleForm(ModelForm):
...     class Meta:
...         model = Article
...         fields = ["pub_date", "headline", "content", "reporter"]
...

# Creating a form to add an article.
>>> form = ArticleForm()

# Creating a form to change an existing article.
>>> article = Article.objects.get(pk=1)
>>> form = ArticleForm(instance=article)

字段类型

生成的 Form 类将按照 fields 属性中指定的顺序为每个指定的模型字段设置一个表单字段。

每个模型字段都有一个对应的默认表单字段。例如,模型中的 CharField 在表单中被表现为 CharFieldManyToManyField 则表现为 MultipleChoiceField 。以下是完整的转化清单:

模型字段

表单字段

AutoField

不呈现在表单中

BigAutoField

不呈现在表单中

BigIntegerField

IntegerFieldmin_value 设置为-9223372036854775808,将 max_value 设置为9223372036854775807。

BinaryField

CharField ,如果在模型字段上的 editable 被设置为 True ,则不在表单中显示。

BooleanField

BooleanField, 或 NullBooleanField (如果 null=True )。

CharField

CharFieldmax_length 设置为模型字段的 max_length ,如果模型中设置了 null=True ,会将 empty_value 设置为 None

DateField

DateField

DateTimeField

DateTimeField

DecimalField

DecimalField

DurationField

DurationField

EmailField

EmailField

FileField

FileField

FilePathField

FilePathField

FloatField

FloatField

ForeignKey

ModelChoiceField (见下文)

ImageField

ImageField

IntegerField

IntegerField

IPAddressField

IPAddressField

GenericIPAddressField

GenericIPAddressField

JSONField

JSONField

ManyToManyField

ModelMultipleChoiceField (见下文)

PositiveBigIntegerField

IntegerField

PositiveIntegerField

IntegerField

PositiveSmallIntegerField

IntegerField

SlugField

SlugField

SmallAutoField

不呈现在表单中

SmallIntegerField

IntegerField

TextField

CharField 设置中 widget=forms.Textarea

TimeField

TimeField

URLField

URLField

UUIDField

UUIDField

如您所料, ForeignKeyManyToManyField 模型字段类型是特殊情况:

  • ForeignKeydjango.forms.ModelChoiceField 表示, 它是一个 ChoiceField ,其选项是一个模型的 QuerySet

  • ManyToManyFielddjango.forms.ModelMultipleChoiceField 表示,它是一个 MultipleChoiceField ,其选项为一个模型 QuerySet

另外,每个生成的表单字段的属性设置如下:

  • 如果模型字段设置了 blank=True ,那么表单字段的 required 属性被设置为 False ,否则 required=True

  • 表单字段的 label 设置为模型字段的 verbose_name ,并且首字母大写。

  • 表单字段的 help_text 设置为模型字段的 help_text

  • 如果模型字段设置了 choices ,那么表单字段的 widget 会被设置为 Select ,其选项来自模型字段的 choices 。这些选项通常包含一个默认选中的空选项。如果字段设置了必填,则会强制用户进行选择。如果模型字段设置了 blank=False 以及一个明确的 default 值,则表单字段中不会包含空选项(默认会选中 default 值)。

最后,请注意,您可以覆盖给定模型字段对应的表单字段。参见下文 覆盖默认字段

一个完整的例子

思考下下面这组模型:

from django.db import models
from django.forms import ModelForm

TITLE_CHOICES = {
    "MR": "Mr.",
    "MRS": "Mrs.",
    "MS": "Ms.",
}


class Author(models.Model):
    name = models.CharField(max_length=100)
    title = models.CharField(max_length=3, choices=TITLE_CHOICES)
    birth_date = models.DateField(blank=True, null=True)

    def __str__(self):
        return self.name


class Book(models.Model):
    name = models.CharField(max_length=100)
    authors = models.ManyToManyField(Author)


class AuthorForm(ModelForm):
    class Meta:
        model = Author
        fields = ["name", "title", "birth_date"]


class BookForm(ModelForm):
    class Meta:
        model = Book
        fields = ["name", "authors"]

通过这些模型,上面的 ModelForm 子类将大致等同于(唯一的区别是 save() 方法,这我们稍后会讨论):

from django import forms


class AuthorForm(forms.Form):
    name = forms.CharField(max_length=100)
    title = forms.CharField(
        max_length=3,
        widget=forms.Select(choices=TITLE_CHOICES),
    )
    birth_date = forms.DateField(required=False)


class BookForm(forms.Form):
    name = forms.CharField(max_length=100)
    authors = forms.ModelMultipleChoiceField(queryset=Author.objects.all())

验证 ModelForm

验证 ModelForm 主要涉及两个步骤:

  1. 验证表单

  2. 验证模型实例

Just like normal form validation, model form validation is triggered implicitly when calling is_valid() or accessing the errors attribute and explicitly when calling full_clean(), although you will typically not use the latter method in practice.

Model validation is triggered from within the form validation step right after the form's clean() method is called. First, the model's full_clean() is called with validate_unique=False and validate_constraints=False, then the model's validate_unique() and validate_constraints() methods are called in order.

Warning

Clean 过程会以各种方式去修改传递给 ModelForm 构造方法的模型实例。例如,模型上的所有日期字段都将转换为实际的日期对象。验证失败可能会使底层模型实例处于不一致状态,因此不推荐对其重用。

覆盖 clean() 方法

您可以重写模型表单上的 clean() 方法来提供额外的验证,方式和普通的表单一样。

访问模型对象对应的表单实例包含一个 instance 属性,让它可以访问对应的模型实例。

Warning

The ModelForm.clean() method sets flags that make the model validation step validate the uniqueness of model fields that are marked as unique, unique_together or unique_for_date|month|year, as well as constraints.

如果您想覆盖 clean() 方法并保持当前的验证,您必须调用父类的 clean() 方法。

与模型验证交互

作为验证过程的一部分, ModelForm 将调用模型上与表单字段对应的每个字段的 clean() 方法。如果您排除了一些模型字段,则验证将不会在这些字段上运行。更多有关字段clean及验证是如何工作的内容,请参阅 表单验证 文档。

The model's clean() method will be called before any uniqueness or constraint checks are made. See Validating objects for more information on the model's clean() hook.

有关模型的 error_messages 的注意事项

表单字段 级别或者 表单 Meta 级别定义的错误信息优先级总是高于在 模型字段 级别定义的。

模型字段 上定义的错误信息只有在 模型验证 步骤引发 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

如果一个可选字段没有出现在表单的数据中,并且您给这个模型字段设置了 default ,那么对应的模型实例会使用这个值作为结果。此行为不适用于使用以下组件的字段: CheckboxInputCheckboxSelectMultiple 或者 SelectMultiple (或者所有其 value_omitted_from_data() 方法总是返回 False 的组件),因为未勾选的复选框和未选中的 multiple> 不会出现在HTML表单提交的数据中。如果您正在设计API并且希望使用这些组件之一的字段有默认回退行为,请使用自定义表单字段或组件。

save() 方法接受一个可选参数 commit ,它的值是 True 或者 False 。如果调用 save() 的时候使用 commit=False ,那么它会返回一个尚未保存到数据库的对象。在这种情况下,需要您自己在生成的模型实例上调用 save() 。如果要在保存对象之前对对象执行自定义操作,或者要使用其中一个专用的 模型保存选项 ,这很有用。 commit 的值默认为 True

另一个使用 commit=False 的作用,您可以在模型与另一个模型有多对多关系的时候看到。如果您的模型具有多对多关系,并且在保存表单时指定了 commit=False ,Django无法立即保存多对多关系的表单数据。这是因为实例的多对多数据只有实例在数据库中存在时才能保存。

要解决这个问题,Django会在您每次使用 commit=False 保存表单时,向 ModelForm 子类添加一个 save_m2m() 方法。在您手动保存表单生成的实例后,可以调用 save_m2m() 来保存多对多的表单数据。例如:

# Create a form instance with POST data.
>>> f = AuthorForm(request.POST)

# Create, but don't save the new author instance.
>>> new_author = f.save(commit=False)

# Modify the author in some way.
>>> new_author.some_field = "some_value"

# Save the new instance.
>>> new_author.save()

# Now, save the many-to-many data for the form.
>>> f.save_m2m()

Calling save_m2m() is only required if you use save(commit=False). When you use a save() on a form, all data -- including many-to-many data -- is saved without the need for any additional method calls. For example:

# Create a form instance with POST data.
>>> a = Author()
>>> f = AuthorForm(request.POST, instance=a)

# Create and save the new author instance. There's no need to do anything else.
>>> new_author = f.save()

除了 save()save_m2m() 方法之外,ModelForm 与普通的表单工作方式一样。例如,用 is_valid() 方法来检查合法性,用 is_multipart() 方法来确定表单是否需要multipart文件上传(之后是否必须将 request.FILES 传递给表单),等等。更多相关信息,请参阅 将上传的文件绑定到表单中

选择要使用的字段

强烈建议您使用 fields 属性来显式设置所有应在表单中编辑的字段。如果不这样做,当一张表单不慎允许用户设置某些字段,尤其是在将新字段添加到模型中时,很容易导致安全问题。根据表单渲染方式的不同,甚至可能不会在网页上显示问题。

The alternative approach would be to include all fields automatically, or remove only some. This fundamental approach is known to be much less secure and has led to serious exploits on major websites (e.g. GitHub ).

但是,有两种简单的方法保证你不会出现这些安全问题:

  1. 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
            exclude = ["title"]
    

    由于 Author 模型有三个字段 nametitlebirth_date ,上例的结果是字段 namebirth_date 会呈现在表单中。

不管使用哪一种,字段会按模型中定义的顺序在表单中出现, ManyToManyField 会排在最后。

另外,Django有个规则:如果您在模型字段中定义了 editable=False任何 使用 ModelForm 给该模型创建的表单都不会包含这个字段。

Note

任何没在上面逻辑中包含的表单字段都会不被表单的 save() 方法处理。另外,如果手动将排除的字段添加回表单,它们也不会被模型实例初始化。

Django will prevent any attempt to save an incomplete model, so if the model does not allow the missing fields to be empty, and does not provide a default value for the missing fields, any attempt to save() a ModelForm with missing fields will fail. To avoid this failure, you must instantiate your model with initial values for the missing, but required fields:

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()

更多关于使用 save(commit=False) 的详细内容,请参阅 保存表单章节

覆盖默认字段

之前在 字段类型 表格中介绍的默认字段类型都是相对合适的。如果您的模型中有一个 DateField ,您可能希望在表单中将它展示为 DateField 。但 ModelForm 可以让您灵活地改变给定模型的表单字段。

要为字段指定自定义组件,请使用内部 Meta 类的 widgets 属性。它应该是一个映射字段名到组建类或组件实例的字典。

例如,如果您希望 Authorname 属性的 CharField