模型表单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'
}),
'icon'
: forms.RadioSelect(
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()
return
render(request,
'rbac/change.html'
, {
'form'
: form})
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})
|
模型表单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'
}),
'icon'
: forms.RadioSelect(
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()
return
render(request,
'rbac/change.html'
, {
'form'
: form})
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})
|