django模型表單ModelForm


如果你正在構建一個數據庫驅動的應用,那么你可能會有與Django的模型緊密映射的表單。比如,你有個BlogComment模型,並且你還想創建一個表單讓大家提交評論到這個模型中。在這種情況下,寫一個forms.Form類,然后在表單類中定義字段,這種一般創建表單的做法是冗余的,因為你已經在ORM模型model中定義了字段的屬性和功能,完全沒必要重新寫一遍字段。

一、核心用法

基於這個原因,Django提供一個輔助類幫助我們利用Django的ORM模型model創建Form。

像下面這樣:

>>> from django.forms import ModelForm >>> from myapp.models import Article # 創建表單類 >>> class ArticleForm(ModelForm): ... class Meta: ... model = Article ... fields = ['pub_date', 'headline', 'content', 'reporter'] # 創建一個表單,用於添加文章 >>> form = ArticleForm() # 創建表單修改已有的文章 >>> article = Article.objects.get(pk=1) >>> form = ArticleForm(instance=article) 

用法的核心是:

  1. 首先從django.forms導入ModelForm;
  2. 編寫一個自己的類,繼承ModelForm;
  3. 在新類里,設置元類Meta;
  4. 在Meta中,設置model屬性為你要關聯的ORM模型,這里是Article;
  5. 在Meta中,設置fields屬性為你要在表單中使用的字段列表;
  6. 列表里的值,應該是ORM模型model中的字段名。

上面的例子中,因為model和form比較簡單,字段數量少,看不出這么做的威力和效率。但如果是那種大型項目,每個模型的字段數量幾十上百,這么做的收益將非常巨大,而且后面還有一招提高效率的大殺器,也就是一步保存數據的操作。

二、 字段類型

生成的Form類中將具有和指定的模型字段對應的表單字段,順序為fields屬性列表中指定的順序。

每個模型字段有一個對應的默認表單字段。比如,模型中的CharField表現成表單中的CharField。模型中的ManyToManyField字段會表現成MultipleChoiceField字段。下面是完整的映射列表:

模型字段 表單字段
AutoField 在Form類中無法使用
BigAutoField 在Form類中無法使用
BigIntegerField IntegerField,最小-9223372036854775808,最大9223372036854775807.
BooleanField BooleanField
CharField CharField,同樣的最大長度限制。如果model設置了null=True,Form將使用empty_value
CommaSeparatedIntegerField CharField
DateField DateField
DateTimeField DateTimeField
DecimalField DecimalField
EmailField EmailField
FileField FileField
FilePathField FilePathField
FloatField FloatField
ForeignKey ModelChoiceField
ImageField ImageField
IntegerField IntegerField
IPAddressField IPAddressField
GenericIPAddressField GenericIPAddressField
ManyToManyField ModelMultipleChoiceField
NullBooleanField NullBooleanField
PositiveIntegerField IntegerField
PositiveSmallIntegerField IntegerField
SlugField SlugField
SmallIntegerField IntegerField
TextField CharField,並帶有widget=forms.Textarea參數
TimeField TimeField
URLField URLField

可以看出,Django在設計model字段和表單字段時存在大量的相似和重復之處。

ManyToManyField和 ForeignKey字段類型屬於特殊情況:

  • ForeignKey被映射成為表單類的django.forms.ModelChoiceField,它的選項是一個模型的QuerySet,也就是可以選擇的對象的列表,但是只能選擇一個。

  • ManyToManyField被映射成為表單類的django.forms.ModelMultipleChoiceField,它的選項也是一個模型的QuerySet,也就是可以選擇的對象的列表,但是可以同時選擇多個,多對多嘛。

同時,在表單屬性設置上,還有下面的映射關系:

  • 如果模型字段設置blank=True,那么表單字段的required設置為False。 否則,required=True。
  • 表單字段的label屬性根據模型字段的verbose_name屬性設置,並將第一個字母大寫。
  • 如果模型的某個字段設置了editable=False屬性,那么它表單類中將不會出現該字段。道理很簡單,都不能編輯了,還放在表單里提交什么?
  • 表單字段的help_text設置為模型字段的help_text
  • 如果模型字段設置了choices參數,那么表單字段的widget屬性將設置成Select框,其選項來自模型字段的choices。選單中通常會包含一個空選項,並且作為默認選擇。如果該字段是必選的,它會強制用戶選擇一個選項。 如果模型字段具有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): # __unicode__ on Python 2 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主要分兩步:

  • 驗證表單
  • 驗證模型實例

與普通的表單驗證類似,模型表單的驗證也是調用is_valid()方法或訪問errors屬性。模型的驗證(Model.full_clean())緊跟在表單的clean()方法調用之后。通常情況下,我們使用Django內置的驗證器就好了。如果需要,可以重寫模型表單的clean()來提供額外的驗證,方法和普通的表單一樣。

五、ModelForm的字段選擇

強烈建議使用ModelForm的fields屬性,在賦值的列表內,一個一個將要使用的字段添加進去。這樣做的好處是,安全可靠。

然而,有時候,字段太多,或者我們想偷懶,不願意一個一個輸入,也有簡單的方法:

__all__:

將fields屬性的值設為__all__,表示將映射的模型中的全部字段都添加到表單類中來。

from django.forms import ModelForm class AuthorForm(ModelForm): class Meta: model = Author fields = '__all__' 

exclude屬性:

表示將model中,除了exclude屬性中列出的字段之外的所有字段,添加到表單類中作為表單字段。

class PartialAuthorForm(ModelForm): class Meta: model = Author exclude = ['title'] 

因為Author模型有3個字段name、birth_datebirth_date,上面的例子會讓title和name出現在表單中。

六、自定義ModelForm字段

在前面,我們有個表格,展示了從模型到模型表單在字段上的映射關系。通常,這是沒有什么問題,直接使用,按默認的來就行了。但是,有時候可能這種默認映射關系不是我們想要的,或者想進行一些更加靈活的定制,那怎么辦呢?

使用Meta類內部的widgets屬性!

widgets屬性接收一個數據字典。其中每個元素的鍵必須是模型中的字段名之一,鍵值就是我們要自定義的內容了,具體格式和寫法,參考下面的例子。

例如,如果你想要讓Author模型中的name字段的類型從CharField更改為<textarea>,而不是默認的<input type="text">,可以如下重寫字段的Widget:

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}), # 關鍵是這一行 } 

上面還展示了添加樣式參數的格式。

如果你希望進一步自定義字段,還可以指定Meta類內部的error_messageshelp_textslabels屬性,比如:

from django.utils.translation import ugettext_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, } 

最后,如果你想完全控制一個字段,包括它的類型,驗證器,是否必填等等。可以顯式地聲明或指定這些性質,就像在普通表單中一樣。比如,如果想要指定某個字段的驗證器,可以顯式定義字段並設置它的validators參數:

from django.forms import ModelForm, CharField 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是可以被繼承的。子模型表單可以添加額外的方法和屬性,比如下面的例子:

>>> class EnhancedArticleForm(ArticleForm):
...     def clean_pub_date(self):
...         ...

以上創建了一個ArticleForm的子類EnhancedArticleForm,並增加了一個clean_pub_date方法。

還可以修改Meta.fieldsMeta.exclude列表,只要繼承父類的Meta類,如下所示:

>>> class RestrictedArticleForm(EnhancedArticleForm):
...     class Meta(ArticleForm.Meta):
...         exclude = ('body',)

九、提供初始值

可以在實例化一個表單時通過指定initial參數來提供表單中數據的初始值。

>>> article = Article.objects.get(pk=1)
>>> article.headline
'My headline'
>>> form = ArticleForm(initial={'headline': 'Initial headline'}, instance=article)
>>> form['headline'].value()
'Initial headline'


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM