模型表单ModelForm
一、基本用法
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
|
from django import forms from django.utils.safestring import mark_safe from django.core.exceptions import ValidationError from rbac import models from django.utils.translation import ugettext_lazy ICON_LIST = [ [ ‘fa-hand-scissors-o‘ , ‘<i aria-hidden="true" class="fa fa-hand-scissors-o"></i>‘ ], [ ‘fa-hand-spock-o‘ , ‘<i aria-hidden="true" class="fa fa-hand-spock-o"></i>‘ ], ] for item in ICON_LIST: item[ 1 ] = mark_safe(item[ 1 ]) class BootStrapModelForm(forms.ModelForm): def __init__( self , * args, * * kwargs): super (BootStrapModelForm, self ).__init__( * args, * * kwargs) # 统一给ModelForm生成字段添加样式 for name, field in self .fields.items(): field.widget.attrs[ ‘class‘ ] = ‘form-control‘ """ 基本用法: 首先从django.forms导入ModelForm; 编写一个自己的类,继承ModelForm; 在新类里,设置元类Meta; 在Meta中,设置model属性为你要关联的ORM模型,这里是Menu; 在Meta中,设置fields属性为你要在表单中使用的字段列表;列表里的值,应该是ORM模型model中的字段名。 """ class UserModelForm(BootStrapModelForm): confirm_password = forms.CharField(label = ‘确认密码‘ ) # class Meta: model = models.UserInfo fields = [ ‘name‘ , ‘email‘ , ‘password‘ , ‘confirm_password‘ , ‘icon‘ ] # fields = ‘__all__‘ #表示将映射的模型中的全部字段都添加到表单类中来 exclude = [ ‘pid‘ ] #表示将model中,除了exclude属性中列出的字段之外的所有字段,添加到表单类中作为表单字段。 widgets = { ‘name‘ : forms.TextInput(attrs = { ‘class‘ : ‘form-control‘ }), choices = ICON_LIST, attrs = { ‘class‘ : ‘clearfix‘ } ) } labels = { ‘name‘ : ugettext_lazy( ‘Writer‘ ), } help_texts = { ‘name‘ : ugettext_lazy( ‘Some useful help text.‘ ), } error_messages = { ‘name‘ : { ‘max_length‘ : ugettext_lazy( "This writer‘s name is too long." ), }, } def clean_confirm_password( self ): """ 检测密码是否一致 :return: """ password = self .cleaned_data[ ‘password‘ ] confirm_password = self .cleaned_data[ ‘confirm_password‘ ] if password ! = confirm_password: raise ValidationError( ‘两次密码输入不一致‘ ) return confirm_password # 可以在实例化一个表单时通过指定initial参数来提供表单中数据的初始值。 |
二、字段类型
生成的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 |
-
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参数,则不会添加空选项到选单中。
三、完整示例
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
|
#models.py from django.db import models 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) #myforms.py from django import forms class AuthorForm(forms.ModelForm): class Meta: model = models.Author fields = [ ‘name‘ , ‘title‘ , ‘birth_date‘ ] class BookForm(forms.ModelForm): class Meta: model = models.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的验证
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
|
def menu_add(request): if request.method = = ‘POST‘ : form = MenuModelForm(data = request.POST) if form.is_valid(): form.save() return redirect(memory_reverse(request, ‘rbac:menu_list‘ )) form = MenuModelForm() def menu_edit(request, pk): obj = models.Menu.objects. filter ( id = pk).first() if not obj: return HttpResponse( ‘菜单不存在‘ ) if request.method = = ‘POST‘ : form = MenuModelForm(instance = obj, data = request.POST) if form.is_valid(): form.save() return redirect(memory_reverse(request, ‘rbac:menu_list‘ )) form = MenuModelForm(instance = obj) return render(request, ‘rbac/change.html‘ , { ‘form‘ : form}) |