Django之Form、ModelForm 组件
【Django的Form组件】
Django的Form主要具有一下几大功能:
- 生成HTML标签
- 验证用户数据(显示错误信息)
- HTML Form提交保留上次提交数据
- 初始化页面显示内容
Form类的使用:
1、定义规则:
1
2
3
4
|
from
django.forms import Form
from
django.forms import fields
class
xxx(Form):
xx = fields.CharField(max_lenghth=,min_lenghth=,required=True,error_message=)
|
2、使用:
1
2
3
4
5
6
7
8
9
10
11
12
|
obj = xxx(request.POST)
# 是否校验成功
v = obj.is_valid()
# html标签name属性 = Form类字段名
obj.is_valid()验证通过返回True,失败则返回False
# 所有错误信息
obj.errors
# 正确信息
obj.cleaned_data
|
登录和注册案例:
1 from django.shortcuts import render,HttpResponse,redirect 2 3 from django.forms import Form 4 from django.forms import fields 5 class LoginForm(Form): 6 # 正则验证: 不能为空,6-18 7 username = fields.CharField( 8 max_length=18, 9 min_length=6, 10 required=True, 11 error_messages={ 12 'required': '用户名不能为空', 13 'min_length': '太短了', 14 'max_length': '太长了', 15 } 16 ) 17 # 正则验证: 不能为空,16+ 18 password = fields.CharField(min_length=16,required=True) 19 # email = fields.EmailField() 20 # email = fields.GenericIPAddressField() 21 # email = fields.IntegerField() 22 23 24 def login(request): 25 if request.method == "GET": 26 return render(request,'login.html') 27 else: 28 obj = LoginForm(request.POST) 29 if obj.is_valid(): 30 # 用户输入格式正确 31 print(obj.cleaned_data) # 字典类型 32 return redirect('http://www.baidu.com') 33 else: 34 # 用户输入格式错误 35 return render(request,'login.html',{'obj':obj}) 36 37 views.py
1 <!DOCTYPE html> 2 <html lang="en"> 3 <head> 4 <meta charset="UTF-8"> 5 <title>Title</title> 6 </head> 7 <body> 8 9 <form method="POST" action="/login/"> 10 {% csrf_token %} 11 用户名:<input type="text" name="username">{{ obj.errors.username.0 }}<br> 12 密码 :<input type="password" name="password">{{ obj.errors.password.0 }}<br> 13 <input type="submit" value="提交"> 14 15 </form> 16 17 </body> 18 </html> 19 20 login.html
基于Form和Ajax提交实现用户登录案例:两种验证方式
1 from django.shortcuts import render,redirect,HttpResponse 2 from django.forms import Form 3 from django.forms import fields 4 from django.forms import widgets 5 6 class LoginForm(Form): 7 user = fields.CharField(required=True) 8 pwd = fields.CharField(min_length=18) 9 10 11 def login(request): 12 if request.method == 'GET': 13 return render(request,'login.html') 14 else: 15 obj = LoginForm(request.POST) 16 if obj.is_valid(): 17 print(obj.cleaned_data) 18 return redirect('http://www.baidu.com') 19 return render(request,'login.html',{'obj': obj}) 20 21 def ajax_login(request): 22 import json 23 ret = {'status': True,'msg': None} 24 obj = LoginForm(request.POST) 25 if obj.is_valid(): 26 print(obj.cleaned_data) 27 else: 28 # print(obj.errors) # obj.errors对象 29 ret['status'] = False 30 ret['msg'] = obj.errors 31 v = json.dumps(ret) 32 return HttpResponse(v) 33 34 # 35 # class TestForm(Form): 36 # t1 = fields.CharField( 37 # required=True, 38 # max_length=8, 39 # min_length=2, 40 # error_messages={ 41 # 'required': '不能为空', 42 # 'max_length': '太长', 43 # 'min_length': '太短', 44 # } 45 # ) 46 # t2 = fields.IntegerField( 47 # min_value=10, 48 # max_value=1000, 49 # error_messages={ 50 # 'required': 't2不能为空', 51 # 'invalid': 't2格式错误,必须是数字', 52 # 'min_value': '必须大于10', 53 # 'max_value': '必须小于1000', 54 # }, 55 # ) 56 # t3 = fields.EmailField( 57 # error_messages={ 58 # 'required': 't3不能为空', 59 # 'invalid': 't3格式错误,必须是邮箱格式', 60 # } 61 # ) 62 63 64 65 66 67 class TestForm(Form): 68 t1 = fields.CharField(required=True,max_length=8,min_length=2, 69 error_messages={ 70 'required': '不能为空', 71 'max_length': '太长', 72 'min_length': '太短', 73 } 74 ) 75 t2 = fields.EmailField() 76 77 def test(request): 78 if request.method == "GET": 79 obj = TestForm() 80 return render(request,'test.html',{'obj': obj}) 81 else: 82 obj = TestForm(request.POST) 83 if obj.is_valid(): 84 print(obj.cleaned_data) 85 else: 86 print(obj.errors) 87 return render(request,'test.html',{'obj':obj}) 88 89 90 91 class RegiterForm(Form): 92 user = fields.CharField(min_length=8) 93 email = fields.EmailField() 94 password = fields.CharField() 95 phone = fields.RegexField('139\d+') 96 97 98 def register(request): 99 if request.method == 'GET': 100 obj = RegiterForm() 101 return render(request,'register.html',{'obj':obj}) 102 else: 103 obj = RegiterForm(request.POST) 104 if obj.is_valid(): 105 print(obj.cleaned_data) 106 else: 107 print(obj.errors) 108 return render(request,'register.html',{'obj':obj})
1 """s4day77 URL Configuration 2 3 The `urlpatterns` list routes URLs to views. For more information please see: 4 https://docs.djangoproject.com/en/1.10/topics/http/urls/ 5 Examples: 6 Function views 7 1. Add an import: from my_app import views 8 2. Add a URL to urlpatterns: url(r'^$', views.home, name='home') 9 Class-based views 10 1. Add an import: from other_app.views import Home 11 2. Add a URL to urlpatterns: url(r'^$', Home.as_view(), name='home') 12 Including another URLconf 13 1. Import the include() function: from django.conf.urls import url, include 14 2. Add a URL to urlpatterns: url(r'^blog/', include('blog.urls')) 15 """ 16 from django.conf.urls import url 17 from django.contrib import admin 18 from app01 import views 19 urlpatterns = [ 20 url(r'^admin/', admin.site.urls), 21 url(r'^login/', views.login), 22 url(r'^ajax_login/', views.ajax_login), 23 url(r'^test/', views.test), 24 url(r'^register/', views.register), 25 ]
1 <!DOCTYPE html> 2 <html lang="en"> 3 <head> 4 <meta charset="UTF-8"> 5 <title></title> 6 </head> 7 <body> 8 <h1>用户登录</h1> 9 <form id="f1" action="/login/" method="POST"> 10 {% csrf_token %} 11 <p> 12 <input type="text" name="user" />{{ obj.errors.user.0 }} 13 </p> 14 <p> 15 <input type="password" name="pwd" />{{ obj.errors.pwd.0 }} 16 </p> 17 <input type="submit" value="提交" /> 18 <a οnclick="submitForm();">提交</a> 19 </form> 20 <script src="/static/jquery-1.12.4.js"></script> 21 <script> 22 function submitForm(){ 23 $('.c1').remove(); 24 $.ajax({ 25 url: '/ajax_login/', 26 type: 'POST', 27 data: $('#f1').serialize(),// user=alex&pwd=456&csrftoen=dfdf\ 28 dataType:"JSON", 29 success:function(arg){ 30 console.log(arg); 31 if(arg.status){ 32 33 }else{ 34 $.each(arg.msg,function(index,value){ 35 console.log(index,value); 36 var tag = document.createElement('span'); 37 tag.innerHTML = value[0]; 38 tag.className = 'c1'; 39 $('#f1').find('input[name="'+ index +'"]').after(tag); 40 }) 41 } 42 } 43 }) 44 } 45 </script> 46 </body> 47 </html>
1 <!DOCTYPE html> 2 <html lang="en"> 3 <head> 4 <meta charset="UTF-8"> 5 <title></title> 6 </head> 7 <body> 8 <form action="/test/" method="POST" novalidate> 9 {% csrf_token %} 10 <p> 11 {{ obj.t1 }}{{ obj.errors.t1.0 }} 12 </p> 13 <p> 14 {{ obj.t2 }}{{ obj.errors.t2.0 }} 15 </p> 16 <input type="submit" value="提交" /> 17 </form> 18 </body> 19 </html>
1 <!DOCTYPE html> 2 <html lang="en"> 3 <head> 4 <meta charset="UTF-8"> 5 <title></title> 6 </head> 7 <body> 8 9 <form action="/register/" method="POST" novalidate> 10 {% csrf_token %} 11 <p> 12 {{ obj.user }} {{ obj.errors.user.0 }} 13 </p> 14 <p> 15 {{ obj.email }} {{ obj.errors.email.0 }} 16 </p> 17 <p> 18 {{ obj.password }} {{ obj.errors.password.0 }} 19 </p> 20 <p> 21 {{ obj.phone }} {{ obj.errors.phone.0 }} 22 </p> 23 <input type="submit" value="提交" /> 24 </form> 25 </body> 26 </html>
总结:
1
2
|
- Ajax,仅用验证功能
- Form,验证功能,生成HTML标签
|
Form类的字段和插件
创建Form类时,主要涉及到 【字段】 和 【插件】,字段用于对用户请求数据的验证,插件用于自动生成HTML;
1、Django内置字段如下:
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
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
|
Field
required
=
True
, 是否允许为空
widget
=
None
, HTML插件
label
=
None
, 用于生成Label标签或显示内容
initial
=
None
, 初始值
help_text
=
'', 帮助信息(在标签旁边显示)
error_messages
=
None
, 错误信息 {
'required'
:
'不能为空'
,
'invalid'
:
'格式错误'
}
show_hidden_initial
=
False
, 是否在当前插件后面再加一个隐藏的且具有默认值的插件(可用于检验两次输入是否一直)
validators
=
[], 自定义验证规则
localize
=
False
, 是否支持本地化
disabled
=
False
, 是否可以编辑
label_suffix
=
None
Label内容后缀
CharField(Field)
max_length
=
None
, 最大长度
min_length
=
None
, 最小长度
strip
=
True
是否移除用户输入空白
IntegerField(Field)
max_value
=
None
, 最大值
min_value
=
None
, 最小值
FloatField(IntegerField)
...
DecimalField(IntegerField)
max_value
=
None
, 最大值
min_value
=
None
, 最小值
max_digits
=
None
, 总长度
decimal_places
=
None
, 小数位长度
BaseTemporalField(Field)
input_formats
=
None
时间格式化
DateField(BaseTemporalField) 格式:
2015
-
09
-
01
TimeField(BaseTemporalField) 格式:
11
:
12
DateTimeField(BaseTemporalField)格式:
2015
-
09
-
01
11
:
12
DurationField(Field) 时间间隔:
%
d
%
H:
%
M:
%
S.
%
f
...
RegexField(CharField)
regex, 自定制正则表达式
max_length
=
None
, 最大长度
min_length
=
None
, 最小长度
error_message
=
None
, 忽略,错误信息使用 error_messages
=
{
'invalid'
:
'...'
}
EmailField(CharField)
...
FileField(Field)
allow_empty_file
=
False
是否允许空文件
ImageField(FileField)
...
注:需要PIL模块,pip3 install Pillow
以上两个字典使用时,需要注意两点:
-
form表单中 enctype
=
"multipart/form-data"
-
view函数中 obj
=
MyForm(request.POST, request.FILES)
URLField(Field)
...
BooleanField(Field)
...
NullBooleanField(BooleanField)
...
ChoiceField(Field)
...
choices
=
(), 选项,如:choices
=
((
0
,
'上海'
),(
1
,
'北京'
),)
required
=
True
, 是否必填
widget
=
None
, 插件,默认select插件
label
=
None
, Label内容
initial
=
None
, 初始值
help_text
=
'', 帮助提示
ModelChoiceField(ChoiceField)
... django.forms.models.ModelChoiceField
queryset,
# 查询数据库中的数据
empty_label
=
"---------"
,
# 默认空显示内容
to_field_name
=
None
,
# HTML中value的值对应的字段
limit_choices_to
=
None
# ModelForm中对queryset二次筛选
ModelMultipleChoiceField(ModelChoiceField)
... django.forms.models.ModelMultipleChoiceField
TypedChoiceField(ChoiceField)
coerce
=
lambda
val: val 对选中的值进行一次转换
empty_value
=
'' 空值的默认值
MultipleChoiceField(ChoiceField)
...
TypedMultipleChoiceField(MultipleChoiceField)
coerce
=
lambda
val: val 对选中的每一个值进行一次转换
empty_value
=
'' 空值的默认值
ComboField(Field)
fields
=
() 使用多个验证,如下:即验证最大长度
20
,又验证邮箱格式
fields.ComboField(fields
=
[fields.CharField(max_length
=
20
), fields.EmailField(),])
MultiValueField(Field)
PS: 抽象类,子类中可以实现聚合多个字典去匹配一个值,要配合MultiWidget使用
SplitDateTimeField(MultiValueField)
input_date_formats
=
None
, 格式列表:[
'%Y--%m--%d'
,
'%m%d/%Y'
,
'%m/%d/%y'
]
input_time_formats
=
None
格式列表:[
'%H:%M:%S'
,
'%H:%M:%S.%f'
,
'%H:%M'
]
FilePathField(ChoiceField) 文件选项,目录下文件显示在页面中
path, 文件夹路径
match
=
None
, 正则匹配
recursive
=
False
, 递归下面的文件夹
allow_files
=
True
, 允许文件
allow_folders
=
False
, 允许文件夹
required
=
True
,
widget
=
None
,
label
=
None
,
initial
=
None
,
help_text
=
''
GenericIPAddressField
protocol
=
'both'
, both,ipv4,ipv6支持的IP格式
unpack_ipv4
=
False
解析ipv4地址,如果是::ffff:
192.0
.
2.1
时候,可解析为
192.0
.
2.1
, PS:protocol必须为both才能启用
SlugField(CharField) 数字,字母,下划线,减号(连字符)
...
UUIDField(CharField) uuid类型
...
|
注:UUID是根据MAC以及当前时间等创建的不重复的随机字符串
1 >>> import uuid 2 3 # make a UUID based on the host ID and current time 4 >>> uuid.uuid1() # doctest: +SKIP 5 UUID('a8098c1a-f86e-11da-bd1a-00112444be1e') 6 7 # make a UUID using an MD5 hash of a namespace UUID and a name 8 >>> uuid.uuid3(uuid.NAMESPACE_DNS, 'python.org') 9 UUID('6fa459ea-ee8a-3ca4-894e-db77e160355e') 10 11 # make a random UUID 12 >>> uuid.uuid4() # doctest: +SKIP 13 UUID('16fd2706-8baf-433b-82eb-8c7fada847da') 14 15 # make a UUID using a SHA-1 hash of a namespace UUID and a name 16 >>> uuid.uuid5(uuid.NAMESPACE_DNS, 'python.org') 17 UUID('886313e1-3b8a-5372-9b90-0c9aee199e5d') 18 19 # make a UUID from a string of hex digits (braces and hyphens ignored) 20 >>> x = uuid.UUID('{00010203-0405-0607-0809-0a0b0c0d0e0f}') 21 22 # convert a UUID to a string of hex digits in standard form 23 >>> str(x) 24 '00010203-0405-0607-0809-0a0b0c0d0e0f' 25 26 # get the raw 16 bytes of the UUID 27 >>> x.bytes 28 b'\x00\x01\x02\x03\x04\x05\x06\x07\x08\t\n\x0b\x0c\r\x0e\x0f' 29 30 # make a UUID from a 16-byte string 31 >>> uuid.UUID(bytes=x.bytes) 32 UUID('00010203-0405-0607-0809-0a0b0c0d0e0f')
2、Django内置插件:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
|
TextInput(
Input
)
NumberInput(TextInput)
EmailInput(TextInput)
URLInput(TextInput)
PasswordInput(TextInput)
HiddenInput(TextInput)
Textarea(Widget)
DateInput(DateTimeBaseInput)
DateTimeInput(DateTimeBaseInput)
TimeInput(DateTimeBaseInput)
CheckboxInput
Select
NullBooleanSelect
SelectMultiple
RadioSelect
CheckboxSelectMultiple
FileInput
ClearableFileInput
MultipleHiddenInput
SplitDateTimeWidget
SplitHiddenDateTimeWidget
SelectDateWidget
|
常用选择插件
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
|
# 单radio,值为字符串
# user = fields.CharField(
# initial=2,
# widget=widgets.RadioSelect(choices=((1,'上海'),(2,'北京'),))
# )
# 单radio,值为字符串
# user = fields.ChoiceField(
# choices=((1, '上海'), (2, '北京'),),
# initial=2,
# widget=widgets.RadioSelect
# )
# 单select,值为字符串
# user = fields.CharField(
# initial=2,
# widget=widgets.Select(choices=((1,'上海'),(2,'北京'),))
# )
# 单select,值为字符串
# user = fields.ChoiceField(
# choices=((1, '上海'), (2, '北京'),),
# initial=2,
# widget=widgets.Select
# )
# 多选select,值为列表
# user = fields.MultipleChoiceField(
# choices=((1,'上海'),(2,'北京'),),
# initial=[1,],
# widget=widgets.SelectMultiple
# )
# 单checkbox
# user = fields.CharField(
# widget=widgets.CheckboxInput()
# )
# 多选checkbox,值为列表
# user = fields.MultipleChoiceField(
# initial=[2, ],
# choices=((1, '上海'), (2, '北京'),),
# widget=widgets.CheckboxSelectMultiple
# )
|
在使用选择标签时,需要注意choices的选项可以从数据库中获取,但是由于是静态字段 ***获取的值无法实时更新***,那么需要自定义构造方法从而达到此目的。
方式一:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
|
from
django.forms
import
Form
from
django.forms
import
widgets
from
django.forms
import
fields
from
django.core.validators
import
RegexValidator
class
MyForm(Form):
user
=
fields.ChoiceField(
# choices=((1, '上海'), (2, '北京'),),
initial
=
2
,
widget
=
widgets.Select
)
def
__init__(
self
,
*
args,
*
*
kwargs):
super
(MyForm,
self
).__init__(
*
args,
*
*
kwargs)
# self.fields['user'].widget.choices = ((1, '上海'), (2, '北京'),)
# 或
self
.fields[
'user'
].widget.choices
=
models.Classes.objects.
all
().value_list(
'id'
,
'caption'
)
|
方式二:
使用django提供的ModelChoiceField和ModelMultipleChoiceField字段来实现
1
2
3
4
5
6
7
8
9
10
|
from
django
import
forms
from
django.forms
import
fields
from
django.forms
import
widgets
from
django.forms
import
models as form_model
from
django.core.exceptions
import
ValidationError
from
django.core.validators
import
RegexValidator
class
FInfo(forms.Form):
authors
=
form_model.ModelMultipleChoiceField(queryset
=
models.NNewType.objects.
all
())
# authors = form_model.ModelChoiceField(queryset=models.NNewType.objects.all())zidi
|
自定义验证规则
方式一:
1
2
3
4
5
6
7
8
9
|
from
django.forms
import
Form
from
django.forms
import
widgets
from
django.forms
import
fields
from
django.core.validators
import
RegexValidator
class
MyForm(Form):
user
=
fields.CharField(
validators
=
[RegexValidator(r
'^[0-9]+$'
,
'请输入数字'
), RegexValidator(r
'^159[0-9]+$'
,
'数字必须以159开头'
)],
)
|
方式二:
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
|
import
re
from
django.forms
import
Form
from
django.forms
import
widgets
from
django.forms
import
fields
from
django.core.exceptions
import
ValidationError
# 自定义验证规则
def
mobile_validate(value):
mobile_re
=
re.
compile
(r
'^(13[0-9]|15[012356789]|17[678]|18[0-9]|14[57])[0-9]{8}$'
)
if
not
mobile_re.match(value):
raise
ValidationError(
'手机号码格式错误'
)
class
PublishForm(Form):
title
=
fields.CharField(max_length
=
20
,
min_length
=
5
,
error_messages
=
{
'required'
:
'标题不能为空'
,
'min_length'
:
'标题最少为5个字符'
,
'max_length'
:
'标题最多为20个字符'
},
widget
=
widgets.TextInput(attrs
=
{
'class'
:
"form-control"
,
'placeholder'
:
'标题5-20个字符'
}))
# 使用自定义验证规则
phone
=
fields.CharField(validators
=
[mobile_validate, ],
error_messages
=
{
'required'
:
'手机不能为空'
},
widget
=
widgets.TextInput(attrs
=
{
'class'
:
"form-control"
,
'placeholder'
: u
'手机号码'
}))
email
=
fields.EmailField(required
=
False
,
error_messages
=
{
'required'
: u
'邮箱不能为空'
,
'invalid'
: u
'邮箱格式错误'
},
widget
=
widgets.TextInput(attrs
=
{
'class'
:
"form-control"
,
'placeholder'
: u
'邮箱'
}))
|
方法三:自定义方法
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
|
from
django
import
forms
from
django.forms
import
fields
from
django.forms
import
widgets
from
django.core.exceptions
import
ValidationError
from
django.core.validators
import
RegexValidator
class
FInfo(forms.Form):
username
=
fields.CharField(max_length
=
5
,
validators
=
[RegexValidator(r
'^[0-9]+$'
,
'Enter a valid extension.'
,
'invalid'
)], )
email
=
fields.EmailField()
def
clean_username(
self
):
"""
Form中字段中定义的格式匹配完之后,执行此方法进行验证
:return:
"""
value
=
self
.cleaned_data[
'username'
]
if
"666"
in
value:
raise
ValidationError(
'666已经被玩烂了...'
,
'invalid'
)
return
value
|
方式四:同时生成多个标签进行验证
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
|
from
django.forms
import
Form
from
django.forms
import
widgets
from
django.forms
import
fields
from
django.core.validators
import
RegexValidator
############## 自定义字段 ##############
class
PhoneField(fields.MultiValueField):
def
__init__(
self
,
*
args,
*
*
kwargs):
# Define one message for all fields.
error_messages
=
{
'incomplete'
:
'Enter a country calling code and a phone number.'
,
}
# Or define a different message for each field.
f
=
(
fields.CharField(
error_messages
=
{
'incomplete'
:
'Enter a country calling code.'
},
validators
=
[
RegexValidator(r
'^[0-9]+$'
,
'Enter a valid country calling code.'
),
],
),
fields.CharField(
error_messages
=
{
'incomplete'
:
'Enter a phone number.'
},
validators
=
[RegexValidator(r
'^[0-9]+$'
,
'Enter a valid phone number.'
)],
),
fields.CharField(
validators
=
[RegexValidator(r
'^[0-9]+$'
,
'Enter a valid extension.'
)],
required
=
False
,
),
)
super
(PhoneField,
self
).__init__(error_messages
=
error_messages, fields
=
f, require_all_fields
=
False
,
*
args,
*
*
kwargs)
def
compress(
self
, data_list):
"""
当用户验证都通过后,该值返回给用户
:param data_list:
:return:
"""
return
data_list
############## 自定义插件 ##############
class
SplitPhoneWidget(widgets.MultiWidget):
def
__init__(
self
):
ws
=
(
widgets.TextInput(),
widgets.TextInput(),
widgets.TextInput(),
)
super
(SplitPhoneWidget,
self
).__init__(ws)
def
decompress(
self
, value):
"""
处理初始值,当初始值initial不是列表时,调用该方法
:param value:
:return:
"""
if
value:
return
value.split(
','
)
return
[
None
,
None
,
None
]
|
初始化数据
在Web应用程序中开发编写功能时,时常用到获取数据库中的数据并将值初始化在HTML中的标签上。
1、Form
from django.forms import Form from django.forms import widgets from django.forms import fields from django.core.validators import RegexValidator class MyForm(Form): user = fields.CharField() city = fields.ChoiceField( choices=((1, '上海'), (2, '北京'),), widget=widgets.Select )
2、Views
from django.shortcuts import render, redirect from .forms import MyForm def index(request): if request.method == "GET": values = {'user': 'root', 'city': 2} obj = MyForm(values) return render(request, 'index.html', {'form': obj}) elif request.method == "POST": return redirect('http://www.google.com') else: return redirect('http://www.google.com')
3、HTML
<form method="POST" enctype="multipart/form-data"> {% csrf_token %} <p>{{ form.user }} {{ form.user.errors }}</p> <p>{{ form.city }} {{ form.city.errors }}</p> <input type="submit"/> </form>
关于对字段认证[clean,clean_字段]的补充(关于错误信息)
1 class RegesterForm(Form): 2 username = fields.CharField( 3 widget=widgets.TextInput( 4 attrs={"class": "form-control", 5 "placeholder": "用户名", 6 }), 7 error_messages={ 8 "required": "内容不能为空", 9 "invalid": "格式错误,请重新输入!", }) 10 11 nickname = fields.CharField( 12 widget=widgets.TextInput( 13 attrs={"class": "form-control", 14 "placeholder": "即昵称", 15 }), 16 error_messages={ 17 "required": "内容不能为空", 18 "invalid": "格式错误,请重新输入!", }) 19 20 email = fields.EmailField( 21 widget=widgets.TextInput( 22 attrs={ 23 "class": "form-control", 24 "placeholder": "请输入您查用邮箱", 25 }), 26 error_messages = { 27 "required": "内容不能为空", 28 "invalid": "格式错误,请重新输入!",} 29 ) 30 31 password = fields.CharField( 32 widget=widgets.PasswordInput( 33 attrs={"class": "form-control", 34 "placeholder": "密码,必须包含数字,字母!", 35 }), 36 error_messages={ 37 "required": "内容不能为空", 38 "invalid": "格式错误,请重新输入!", }) 39 40 passwords = fields.CharField( 41 widget=widgets.PasswordInput( 42 attrs={"class": "form-control", 43 "placeholder": "请输入确认密码", 44 }), 45 error_messages={ 46 "required": "内容不能为空", 47 "invalid": "格式错误,请重新输入!", }) 48 49 avatar = fields.FileField( 50 widget=widgets.FileInput( 51 attrs={'id':"imgSelect",}), 52 ) 53 code = fields.CharField( 54 widget=widgets.TextInput( 55 attrs={ 56 "class": "form-control", 57 "placeholder": "验证码", 58 },) 59 ) 60 def __init__(self,request,*args,**kwargs): 61 super(RegesterForm,self).__init__(*args,**kwargs) 62 self.request = request 63 64 def clean_code(self): 65 input_code = self.cleaned_data["code"] 66 session_code = self.request.session.get("code") 67 if input_code.upper() == session_code.upper(): 68 return input_code 69 raise ValidationError("验证码错误,请重新输入!") 70 #自定义字段验证方法,获取错误信息的方式不变,还是 对象.errors.字段名.0 71 72 def clean(self): 73 p1 = self.cleaned_data.get("password") 74 p2 = self.cleaned_data.get("passwords") 75 if p1 == p2: 76 return self.cleaned_data 77 else: 78 self.add_error("passwords",ValidationError("输入的密码不一致!"))
1 对于Form组件的错误信息: 2 注意再注意: 3 默认错误信息方式: raise ValidationError("输入的密码不一致!") 4 自定义对已拿到所有字段数据进行验证的时候,这种方式在获取错误信息时就发生改变,查看源码发现如果有错误的话,他会执行self.add_error(None, e) 5 通过方法给字段None添加错误信息,查看add_error的源码以及执行过程,发现None = NON_FIELD_ERRORS。也就是说会默认给__all__字段或 6 NON_FIELD_ERRORS写上错误信息。原因:内部定义的NON_FIELD_ERRORS="__all__", 7 获取方式: 8 前端获取 是通过obj.non_field_errors 9 后台获取是通过 obj.errors["__all__"] or obj.errors[NON_FIELD_ERRORS] 10 11 我们知道,当报错的时候,内部会自动去添加错误信息,若是我们能否手动指定某个字段呢?答案是肯定的。 12 这样我们自己添加异常的错误信息,就能直接通过{{obj.errors.passwords.0}}获取到与其他的无疑。 13 语法: 14 self.add_error('字段名称', 错误异常对象)
以上代码是我写用户注册时的form组件,由于牵扯到对输入字段的认证问题!如果选择在视图函数中,那代码的重复率可就老了去了!!!所以就使用了Form组件中的字段认证的方法!!!
前面已经介绍过Form组件中两种验证方式的函数,一个对字段的 clean_字段;另一个是等所有字段验证之后,对所有验证过的clean_data进行更高的验证:clean;今天我要说的是关于验证过程中,产生和收集错误信息的问题。详见上面代码!!!
-------------------------------------------------------------------------------------bottom another------------------------------------------------------------------------------------------
一、Form组件:
django框架提供了一个form类,来处理web开发中的表单相关事项。众所周知,form最常做的是对用户输入的内容进行验证,为此django的forms类提供了全面的内容验证和保留用户上次输入数据的支持。
form组件有2大大功能
对用户提交的内容进行验证(from表单/Ajax)
保留用户上次输入的内容
1、对用户提交的数据进行验证
form组件验证的原理
1.obj=Form()form组件类实例化时找到类中所有的字段 把这些字段 变成组合成字典;
self.fields={‘user’:正则表达式1,‘pwd’:正则表达式2}
2.循环self.fields字典(自己写的字段)
for k,v in self.fields.items():
K是user,pwd
v是正则表达式
3.每次循环通过self.fields字典的键, 一个一个的去get前端POST提交的数据 得到用户输入数据;
input_value= request.post.get(‘k’)(所以form字段的名称,要和前端的name属性匹配)
4.每次拿到用户输入的数据 (input_value)和进行正则表达式匹配;
5.匹配成功flag=True 匹配失败flag=falsh,最后 return flag obj.is_valid=flag。
如果For自带的规则和正则满足不了验证需求,可在Form类中自定义方法,做扩展。
6.每个字段验证通过后,每个字段执执行self.clean_filelds函数(自定义 对Form类中的字段做单独验证,比如去数据库查询判断一下用户提交的数据是否存在?)
7. 执行Form组件的clean_form方法进行整体验证!(既然每个字段都验证了,就可以对用户提交的数据做整体验证了!比如进行联合唯一的验证)
8.最后执行类似 clean_form的post_clean方法结束验证。(一般不使用post_clean做自定义过滤,clean_form方法完全可以解决)
form表单提交验证(form表单(会发起 get)提交刷新失去上次内容)
1 from django.shortcuts import render,HttpResponse,redirect 2 from django.forms import Form 3 from django.forms import fields 4 5 class Login(Form): 6 #from验证规则 用户名 6-10字符 required不能为空 7 name=fields.CharField(max_length=10, 8 min_length=6, 9 required=True, 10 error_messages={ 11 'required':'用户名不能为空', #error_messages参数 自定义错误信息 12 'min_length':'太短了', 13 'max_length': "太长了", 14 } 15 16 ) 17 # z注意name 必须和 from表单提交的一致,要么二则怎么对比校验呢 18 pwd= fields.CharField(min_length=3, 19 required=True, 20 error_messages={ 21 'required': '密码不能为空', # error_messages参数 自定义错误信息 22 'min_length': '太短了', 23 'max_length': "太长了", 24 } 25 26 27 28 ) 29 30 31 def index(request): 32 if request.method=='GET': 33 return render(request,'login.html') 34 else: 35 obj=Login(request.POST) #把客户端提交来的form表单和 和匹配规则放在一起 36 res=obj.is_valid() #自动校验 给出结果 True 或者 False 37 if res: #验证成功后obj.cleaned_data获取成功的数据,字典类型正好对应数据 的批量操作 38 print(obj.cleaned_data) 39 return redirect('http://www.baidu.com') #obj.errors获取错误信息(对象类型)就可以传到前端显示了! 40 else: 41 return render(request,'login.html',{'obj':obj})
Ajax提交验证(不会刷新,上次输入内容自动保留)
Django的form验证功能不仅限于对form表单提交的数据验证,同样适用于ajax提交方式
1 <!DOCTYPE html> 2 <html lang="en"> 3 <head> 4 <meta charset="UTF-8"> 5 <title>ajx提交</title> 6 </head> 7 <body> 8 <form method="post" action="/aja_login/" id="f1"> 9 {%csrf_token%} 10 <p>用户:<input type="text" name="name"></p> 11 <p>密码:<input type="password" name="pwd"></p> 12 <p><input type="button" οnclick="Ajxform()" value="aja提交"></p> 13 </form> 14 </body> 15 <script src="/static/zhanggen.js"></script> 16 <script> function Ajxform(){ 17 $('.c1').remove() 18 $.ajax({ 19 url:'/alogin/', 20 type:'POST', 21 dataType:'JSON', 22 data:$('#f1').serialize(), 23 success:function (args) { 24 if (args.status){ } 25 else{ 26 {# {status: false, msg: Object}#} 27 {# console.log(args);#} 28 {# Jquery循环服务端 传过来的 错误信息对象#} 29 $.each(args.msg,function (index,value) { 30 console.log(index,value); 31 {# index----> name ["太短了"]#} 32 {# value-----pwd["密码不能为空"]#} 33 var tag=document.createElement('span'); 34 tag.innerHTML= value[0]; 35 tag.className='c1'; 36 console.log(index); 37 {# 寻找input下 属性为 name 和pwd的标签(字符串拼接) 在他们后半加 上tag标签也就是错误 信息 #} 38 $('#f1').find('input[name="'+ index +'"]').after(tag) 39 40 }) 41 } 42 43 }})} 44 </script> 45 </html>
Views
1 from django.shortcuts import render,HttpResponse,redirect 2 from django.forms import Form 3 from django.forms import fields 4 import json 5 class Login(Form): 6 #from验证规则 用户名 6-10字符 required不能为空 7 name=fields.CharField(max_length=10, 8 min_length=6, 9 required=True, 10 error_messages={ 11 'required':'用户名不能为空', #error_messages参数 自定义错误信息 12 'min_length':'太短了', 13 'max_length': "太长了", 14 } 15 16 ) 17 # z注意name 必须和 from表单提交的一致,要么二则怎么对比校验呢 18 pwd= fields.CharField(min_length=3, 19 required=True, 20 error_messages={ 21 'required': '密码不能为空', # error_messages参数 自定义错误信息 22 'min_length': '太短了', 23 'max_length': "太长了",}) 24 25 26 27 def agx_login(request): 28 ret={'status':True,'msg':None} 29 if request.method=='GET': 30 return render(request,'ajalogin.html') 31 else: 32 obj=Login(request.POST) 33 ret['status']=False 34 ret['msg']=obj.errors 35 return HttpResponse(json.dumps(ret))
自定义正则表达式验证
密码修改实例
password_complexity='^(?:(?=.*[A-Z])(?=.*[a-z])(?=.*[0-9])).+$' #密码复杂性要求:密码必须包含数字、大、写字母
1 {% extends "arya/layout.html" %} 2 {% block out_css %} 3 <link rel="stylesheet" href="/arya/static/arya/css/form-control.css"/> 4 {% endblock %} 5 6 {% block content %} 7 {% csrf_token %} 8 <div> 9 <div class="row"> 10 <a class="btn btn-default" href="{{ request.META.HTTP_REFERER }}">返回</a> 11 <div class="col-md-5 col-md-offset-3"> 12 <form> 13 <div class="form-group"> 14 <label for="exampleInputPassword1">请输入原始密码</label> 15 <input name="old_pwd" type="password" class="form-control" id="exampleInputPassword0" 16 placeholder="原始密码"> 17 </div> 18 <div class="form-group"> 19 <label for="exampleInputPassword1">新的密码</label> 20 <input name="first_new_pwd" type="password" class="form-control" id="exampleInputPassword1" 21 placeholder="新的密码"> 22 </div> 23 <div class="form-group"> 24 <label for="exampleInputPassword1">再次输入新密码</label> 25 <input name="second_new_pwd" type="password" class="form-control" id="exampleInputPassword2" 26 placeholder="再次输入新密码"> 27 </div> 28 29 <button id="submit_pwd" type="button" class="btn btn-default">提交</button> 30 31 </form> 32 <br> 33 <p style="color: red" id="__all__" class="error_msg"></p> 34 <br> 35 <p>设置密码时请符合以下规则:最小长度8、包含大小写英文字母、数字。</p> 36 </div> 37 </div> 38 </div> 39 {# </section>#} 40 <script> 41 $('#submit_pwd').click(function () { 42 var $csrf = $("[name='csrfmiddlewaretoken']").val(); 43 var $old_pwd = $('[name="old_pwd"]').val(); 44 var $first_new_pwd = $('[name="first_new_pwd"]').val(); 45 var $second_new_pwd = $('[name="second_new_pwd"]').val(); 46 47 var pwd_formdata = new FormData(); 48 pwd_formdata.append('csrfmiddlewaretoken', $csrf); 49 pwd_formdata.append('old_pwd', $old_pwd); 50 pwd_formdata.append('first_new_pwd', $first_new_pwd); 51 pwd_formdata.append('second_new_pwd', $second_new_pwd); 52 53 54 $.ajax({ 55 type: 'post', 56 data: pwd_formdata, 57 processData: false, 58 contentType: false, 59 success: function (data) { 60 var response = JSON.parse(data); 61 var $error_p = $('<p class="error_msg" style="color:red">'+ '</p>'); 62 63 if (response.status == 200) { 64 window.location = "/login/" 65 } 66 67 if (response.status == 404) { 68 var error_p =$error_p.text(response.msg.old_pwd); 69 $('[name="old_pwd"]').after(error_p) 70 } 71 else { 72 $.each(response.msg, function (k, v) { 73 var $input_tag = $("[name=" + k +"]"); 74 var error_p = '<p style="color: red" class="error_msg">'+v[0]+'</p>'; 75 $input_tag.after(error_p); 76 console.log(response.msg); 77 if (k == '__all__') { 78 $('#__all__').text(v[0]) 79 } 80 }); 81 } 82 setTimeout("$('.error_msg').remove()", 3000); 83 } 84 }) 85 }) 86 </script> 87 {% endblock %}
1 def changepwd(request): #修改密码视图 2 if request.method=='POST': 3 current_user = request.session.get('username') 4 response_info = {'status':200,'msg':None} 5 user_db_obj = models.UserInfo.objects.filter(username=current_user).first() 6 if not user_db_obj or user_db_obj.password != make_md5(request.POST.get('old_pwd').strip()): 7 response_info['status'] = 404 8 response_info['msg'] = {'old_pwd': '原始密码错误.'} 9 else: 10 obj = FormCheck.Set_password(request.POST) 11 if not obj.is_valid(): 12 response_info['status'] = 403 13 response_info['msg']=obj.errors 14 else: 15 user_db_obj.password = make_md5(request.POST.get('second_new_pwd').strip()) 16 user_db_obj.save() 17 return HttpResponse(json.dumps(response_info, ensure_ascii=False)) 18 return render(request,'woke_order/changepwd.html')
1 from django.forms import Form 2 from django.forms import fields 3 from django.forms import widgets 4 from cmdb.models import * 5 import re 6 from django.core.exceptions import ValidationError 7 8 class Set_password(Form): 9 password_complexity='^(?:(?=.*[A-Z])(?=.*[a-z])(?=.*[0-9])).+$' 10 11 old_pwd=fields.RegexField(password_complexity, 12 required=True, 13 min_length=8, 14 error_messages={'invalid':'不满足密码复杂性要求','required':'密码不能为空!','min_length':'密码长度少于8位'}, 15 ) 16 17 first_new_pwd=fields.RegexField(password_complexity, 18 required=True, 19 min_length=8, 20 error_messages={'invalid':'不满足密码复杂性要求','required':'密码不能为空!','min_length':'密码长度少于8位'}, 21 ) 22 23 second_new_pwd =fields.RegexField(password_complexity, 24 required=True, 25 min_length=8, 26 error_messages={'invalid':'不满足密码复杂性要求','required':'密码不能为空!','min_length':'密码长度少于8位'}, 27 ) 28 29 def clean(self): 30 first_new_pwd=self.cleaned_data.get('first_new_pwd') 31 second_new_pwd=self.cleaned_data.get('second_new_pwd') 32 if first_new_pwd and second_new_pwd: 33 if first_new_pwd.strip() == second_new_pwd.strip(): 34 return self.cleaned_data 35 raise ValidationError("两次密码不一致")
IP 和端口
ipaddr_validate="^((?:(2[0-4]\d)|(25[0-5])|([01]?\d\d?))\.){3}(?:(2[0-4]\d)|(255[0-5])|([01]?\d\d?))$" port_validate='^([0-9]|[1-9]\d|[1-9]\d{2}|[1-9]\d{3}|[1-5]\d{4}|6[0-4]\d{3}|65[0-4]\d{2}|655[0-2]\d|6553[0-5])$'
1 from django.forms import Form,fields 2 from django.forms import widgets,forms 3 import re 4 ipaddr_validate="^((?:(2[0-4]\d)|(25[0-5])|([01]?\d\d?))\.){3}(?:(2[0-4]\d)|(255[0-5])|([01]?\d\d?))$" 5 port_validate='^([0-9]|[1-9]\d|[1-9]\d{2}|[1-9]\d{3}|[1-5]\d{4}|6[0-4]\d{3}|65[0-4]\d{2}|655[0-2]\d|6553[0-5])$' 6 class dbinfo_create(Form): 7 data_mode_type=fields.CharField(required=True,error_messages={'required':'数据库模型不能为空.'}) 8 database_type=fields.CharField(required=True,error_messages={'required':'数据库类型不能为空'}) 9 host=fields.RegexField(ipaddr_validate,required=True,error_messages={'required':'IP不能为空','invalid':'不合法的IP地址'}) 10 port=fields.RegexField(port_validate,required=True,error_messages={'required':'端口不能为空.','invalid':'端口无效'}) 11 # instance_nikename=fields.CharField(max_length=20,error_messages={'required':'端口不能为空.',"max_length":"标题不能超过20个字"}) 12 db_business=fields.CharField(required=True,error_messages={'required':'请说明所属业务线'}) 13 DBA=fields.CharField(required=True,error_messages={'required':'请说明DBA'}) 14 responsible_person=fields.CharField(required=True, error_messages={'required':'请选择相关责任人!'})
2、动态生成HTML标签,保留用户上次输入的内容。
如何保留用户上次输入的内容?
由于form表单submit之后(发送post请求) 数据提交到 后端,不管前端输入的数据是否正确,服务端也要响应,所以页面会刷新;
所以无法保留用户上次输入的内容;如何解决呢?
1、把定义的定义的Form类,实例化(obj=Login() )内部调用一个__str__的方法,如果没有传值 返回<input type="text" name=“字段”>name='字段名空的input标签
2、把这个实例化之后的对象传到前端显示,让用户输入值;用户输入值通过post方法提交到后台。
3、如果后台实例化一个对象 obj=Login(request.POST)传入了值, <input type="text" name=“字段” value='request.post的数据'>然后后端再返回客户端就可以看到用户输入的值了!
保留用户上次输入的内容 是利用了 obj=Login(request.POST)接收了用户输入的值
视图
1 from django import forms 2 3 class Myform(forms.Form): #1、写1个继承forms.Form的类,定制form表单的数据类型; 4 user=forms.CharField(max_length=32,min_length=3,label='用户名', 5 error_messages={'required':'不能为空'}, 6 widget=forms.TextInput(attrs={'class':'sb','placeholder':'用户名'},) 7 ) 8 age=forms.IntegerField(label='年龄',error_messages={'required':'不能为空'},) 9 email=forms.EmailField(label='邮箱',error_messages={'required':'不能为空'},) 10 11 def register2(request): 12 if request.method=='GET': 13 forms_obj=Myform() #2、实例化类,把对象渲染到模板 14 return render(request,'form——register.html',{'forms_obj':forms_obj}) 15 else: 16 forms_obj=Myform(request.POST) #3、把提交的数据封装成form对象 17 if forms_obj.is_valid(): #4、使用 form对象的.is_valid()方法,校验提交过来的数据是否符合验证规则 18 data=forms_obj.cleaned_data #5、获取验证通过的数据(字典类型,可直接 **dict插入数据库) 19 # User.objects.create_user(**data) 20 return HttpResponse('OK') 21 else: 22 print(forms_obj.cleaned_data) #6、由于用户在form表单提交了值,利用这一点, 23 # 把forms_obj=Myform(request.POST)渲染到前端就可以保存用户输入的值 24 return render(request, 'form——register.html', {'forms_obj':forms_obj})
前台
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Django_form验证</title> </head> <body> <form action="/form/" method="post" novalidate> {% csrf_token %} <p>用户:{{ forms_obj.user }}{{ forms_obj.errors.user.0 }}</p> <p>年龄:{{ forms_obj.age }}{{ forms_obj.errors.age.0}}</p> <p>邮箱:{{ forms_obj.email }}{{ forms_obj.errors.email.0}}</p> <button>提交</button> </form> </body> </html>
1 from django.shortcuts import render,HttpResponse,redirect 2 from django.forms import Form 3 from django.forms import fields 4 import json 5 class Login(Form): 6 #from验证规则 用户名 6-10字符 required不能为空 7 name=fields.CharField(max_length=10, 8 min_length=6, 9 required=True, 10 error_messages={ 11 'required':'用户名不能为空', #error_messages参数 自定义错误信息 12 'min_length':'太短了', 13 'max_length': "太长了", 14 } 15 16 ) 17 # z注意name 必须和 from表单提交的一致,要么二则怎么对比校验呢 18 pwd= fields.CharField(min_length=3, 19 required=True, 20 error_messages={ 21 'required': '密码不能为空', # error_messages参数 自定义错误信息 22 'min_length': '太短了', 23 'max_length': "太长了",}) 24 25 26 def index(request): 27 ret={'status':True,'msg':None} 28 if request.method=='GET': 29 obj=Login() #自动生成空白的input标签 发送给客户端) 30 return render(request,'login.html',{'obj':obj}) 31 else: 32 obj=Login(request.POST) #把客户端提交来的form表单和 和匹配规则放在一 33 res=obj.is_valid() #自动生成空白的input标签 发送 34 if res: #验证成功后obj.cleaned_data获取成功的数据,字典类型正好对应数据 的批量操作 35 return HttpResponse('OK') #obj.errors获取错误信息(对象类型)就可以传到前端显示了! 36 else: 37 return render(request,'login.html',{'obj':obj})
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>登录页面</title> </head> <body> <form method="post" action="/login/" id="f1" novalidate > {%csrf_token%} <h1>用户登录</h1> <p>用户名 {{obj.name}}{{ obj.name.errors.0}}</p> <p>密码:{{ obj.pwd}}{{ obj.pwd.errors.0}}</p> <p><input type="submit" value="登录"></p> </form> </body> </html>
3、承上启下 form组件的套路(执行流程):
(1)在后端定义类和字段,实例化Form类;
(2)到用户 发送get请求时,服务端渲染到模板(空标签/默认值)发送到客户端显示
(3)客户端填数据,POST提交到后端;
(4)后端验证,返回结果给前端;(切记Form组件是在后端生成,发送给客户端显示,客户端填完数据在发回服务端!)
二、form组件使用:
1、导入Form插件
from django.forms import Form,fields
from django.forms import widgets
1 class Class_form(Form): 2 title=fields.RegexField('全栈\d+', 3 # initial='全栈', #设置input标签中的默认值 4 min_length=2, 5 required=True, 6 error_messages={'invalid':"必须以全栈开头", 7 'min_length':'太短了', 8 'required':"不能为空", 9 } 10 ) 11 class Students(Form): 12 name=fields.CharField(required=True, 13 widget=widgets.TextInput(attrs={'class':'form-control'}), 14 error_messages={'required':'姓名不能为空'}) 15 sex=fields.CharField(required=True,error_messages={'required':'不能为空'}, 16 widget = widgets.TextInput(attrs={'class': 'form-control'}) 17 18 ) 19 cls_id=fields.IntegerField(widget=widgets.Select(choices=models.Classes.objects.values_list('id','title'), 20 attrs = {'class': 'form-control'} 21 22 ), 23 24 ) 25 class teacher_form(Form): 26 tname=fields.CharField(required=True,error_messages={'required':"姓名不能为空"} ) 27 # classes=fields.CharField( #多选 不能用这个插件不使用request.post.getlist()取值 28 # widget=widgets.Select(choices=models.Classes.objects.values_list(), 29 # attrs={'multiple':'multiple'}) 30 # ) 31 # classes=fields.CharField(widget=widgets.SelectMultiple( 32 # # 如果在fields.CharField字段该插件,得到会是{'tname': 'ww', 'classes': "['2', '3']"}字符串 33 # choices= models.Classes.objects.values_list('id','title')) ) 34 classes=fields.MultipleChoiceField( 35 choices=models.Classes.objects.values_list('id','title'), 36 widget=widgets.SelectMultiple,error_messages={'required':'选择不能为空'}) 37 def __int__(self,*args,**kwargs): #解决数据不同步的bug,每次form组件实例化时 都重新去数据库拿数据 38 super(teacher_form,self).__init__(*args,**kwargs) 39 self.fields['classes'].widget.choices=models.Classes.objects.values_list('id','title') 40 41 42 43 class Test_form(Form): 44 name=fields.CharField() #动态生成 text类型的input标签 45 text=fields.CharField(widget=widgets.Textarea,) #动态生成文本框 46 age=fields.CharField(widget=widgets.CheckboxInput) #动态生成单选框 47 holby=fields.MultipleChoiceField( #MultipleChoiceField动态生成复选框 48 choices=[(1,'篮球'),(2,"足球"),(3,"高俅")], 49 widget=widgets.CheckboxSelectMultiple) 50 sex=fields.MultipleChoiceField( 51 choices=[(1,'男'),(2,'女')], 52 widget=widgets.RadioSelect 53 ) 54 55 select=fields.ChoiceField(choices=[(2, '北京'), (3, '唐县')]) #通过ChoiceField字段动态生成单选框 56 # 通过form组件的MultipleChoiceField字段 57 mselect=fields.MultipleChoiceField(choices=[(1,'管家佐'),(2,'万宝利') ], 58 widget=widgets.SelectMultiple) 59 file=fields.FileField() #通过form组件的.FileField动态生成 文件上传input标签,注意在提交到后台是对象
2、定义类和字段(验证规则) 扩展方法
class Form_login(Form):
字段 参数
user=fields.RegexField(正则表达式,验证规则,error_messages={错误信息},widget=html标签插件,attrs = {'标签插件的属性'})
三、form的钩子函数
Django的form在obj.is_valid()方法内提供2个钩子函数,以便我们随时调用他自定制一些复杂的验证规则;
局部钩子函数
1 class Class_form(Form): 2 title=fields.RegexField('全栈\d+', 3 # initial='全栈', #设置input标签中的默认值 4 min_length=2, 5 required=True, 6 error_messages={'invalid':"必须以全栈开头", 7 'min_length':'太短了', 8 'required':"不能为空", 9 } 10 ) 11 class Students(Form): 12 name=fields.CharField(required=True, 13 widget=widgets.TextInput(attrs={'class':'form-control'}), 14 error_messages={'required':'姓名不能为空'}) 15 sex=fields.CharField(required=True,error_messages={'required':'不能为空'}, 16 widget = widgets.TextInput(attrs={'class': 'form-control'}) 17 18 ) 19 cls_id=fields.IntegerField(widget=widgets.Select(choices=models.Classes.objects.values_list('id','title'), 20 attrs = {'class': 'form-control'} 21 22 ), 23 24 ) 25 class teacher_form(Form): 26 tname=fields.CharField(required=True,error_messages={'required':"姓名不能为空"} ) 27 # classes=fields.CharField( #多选 不能用这个插件不使用request.post.getlist()取值 28 # widget=widgets.Select(choices=models.Classes.objects.values_list(), 29 # attrs={'multiple':'multiple'}) 30 # ) 31 # classes=fields.CharField(widget=widgets.SelectMultiple( 32 # # 如果在fields.CharField字段该插件,得到会是{'tname': 'ww', 'classes': "['2', '3']"}字符串 33 # choices= models.Classes.objects.values_list('id','title')) ) 34 classes=fields.MultipleChoiceField( 35 choices=models.Classes.objects.values_list('id','title'), 36 widget=widgets.SelectMultiple,error_messages={'required':'选择不能为空'}) 37 def __int__(self,*args,**kwargs): #解决数据不同步的bug,每次form组件实例化时 都重新去数据库拿数据 38 super(teacher_form,self).__init__(*args,**kwargs) 39 self.fields['classes'].widget.choices=models.Classes.objects.values_list('id','title') 40 41 42 43 class Test_form(Form): 44 name=fields.CharField() #动态生成 text类型的input标签 45 text=fields.CharField(widget=widgets.Textarea,) #动态生成文本框 46 age=fields.CharField(widget=widgets.CheckboxInput) #动态生成单选框 47 holby=fields.MultipleChoiceField( #MultipleChoiceField动态生成复选框 48 choices=[(1,'篮球'),(2,"足球"),(3,"高俅")], 49 widget=widgets.CheckboxSelectMultiple) 50 sex=fields.MultipleChoiceField( 51 choices=[(1,'男'),(2,'女')], 52 widget=widgets.RadioSelect 53 ) 54 55 select=fields.ChoiceField(choices=[(2, '北京'), (3, '唐县')]) #通过ChoiceField字段动态生成单选框 56 # 通过form组件的MultipleChoiceField字段 57 mselect=fields.MultipleChoiceField(choices=[(1,'管家佐'),(2,'万宝利') ], 58 widget=widgets.SelectMultiple) 59 file=fields.FileField() #通过form组件的.FileField动态生成 文件上传input标签,注意在提交到后台是对象
全局钩子函数
如果要想要同时对2个form字段进行验证,就需要全局钩子函数(应用 验证2次输入的密码是否一致),可以调用他们自定制复杂的form验证规则,
问题1: 注册页面输入为空,报错:keyError:找不到password
def clean(self): print("---",self.cleaned_data) # if self.cleaned_data["password"]==self.cleaned_data["repeat_password"]: # 报错原因:self.cleaned_data是干净数据,如果页面没有输入内容,则self.cleaned_data没有password。 改如下: if self.cleaned_data.get("password")==self.cleaned_data.get("repeat_password"): return self.cleaned_data else: raise ValidationError("两次密码不一致")
四、渲染到模板
1.简单粗暴型
注意:模板语言{{form_obj.as_p}},一定要在设置lable参数
class UserInfoForm(Form): name=fields.CharField(required=True,error_messages={'reqired':'用户名不能为空'},label='姓名') email=fields.EmailField(required=True,error_messages={'reqired':'用户名不能为空'},label='邮箱') pary=fields.ChoiceField(choices=[(1,"技术部"),(2,'销售部'),(3,'市场部'),],label='部门')
{{obj.as_p}}以P标签的形式全部显示 <table> 注意加table标签形式全部显示 {{obj.as_table}} </table> <ul>注意加ul标签形式全部显示 {{obj.as_table}} </ul>
2.灵活定制型
<p>姓名:{{ obj.name }}</p> <p>性别:{{ obj.sex }}</p> <p>爱好: {{ obj.holby }}</p> <p>婚姻状况:{{ obj.age }}</p> <p>个人简介 {{ obj.text }}</p> <p>工作地点: {{ obj.select }}</p> <p>居住地点:{{ obj.mselect }}</p> <p>资料上传:{{obj.file}}</p>
五、页面显示用户填完数据提交回来后台验证
数据校验
obj=classForm_login(request.POST )
默认校验:obj=classForm_login(data={} ) 含有错误信息: obj=Class_form(initial={'title':class_obj.title})只有html标签
obj.is_valid() 获取校验结果,验证通过返回True,失败则返回False
obj.errors获取错误信息
obj.cleand_data 获取正确的数据
六、基于Form组件的学生管理系统(项目)
路由
from django.conf.urls import url from django.contrib import admin from app01 import views urlpatterns = [ url(r'^classes/',views.classes), url(r'^classes_add(\d+)/',views.classes_add), url(r'^classes_edit(\d+)/',views.classes_edit), url(r'^students/',views.students), url(r'^students_add(\d+)/',views.students_add), url(r'^students.edit(\d+)/',views.students_edit), url(r'^teachers/',views.teacher), url(r'^teacher_add/',views.teacher_add), url(r'^teacher_edit(\d+)/',views.teacher_edit), url(r'^test/',views.test), ]
视图
1 def classes(request): 2 if request.method=='GET': 3 c_list=models.Classes.objects.all() 4 return render(request,'class_list.html',{'clist':c_list}) 5 6 def classes_add(request,args): 7 if request.method=='GET': 8 obj=Class_form() 9 return render(request,'class_add.html',{'obj':obj}) 10 else: 11 obj=Class_form(request.POST) 12 if obj.is_valid(): 13 models.Classes.objects.create(**obj.cleaned_data ) 14 return redirect('/classes/') 15 else: 16 print('NI') 17 return render(request,'class_add.html',{'obj':obj}) 18 19 def classes_edit(request,args): 20 if request.method=='GET': 21 class_obj=models.Classes.objects.filter(id=args).first() 22 obj=Class_form(initial={'title':class_obj.title}) 23 return render(request,'class_edit.html',{'obj':obj,'nid':args}) 24 else: 25 obj = Class_form(request.POST) 26 if obj.is_valid(): 27 models.Classes.objects.filter(id=args).update(**obj.cleaned_data) 28 return redirect('/classes/') 29 else: 30 return render(request, 'class_edit.html', {'obj': obj, 'nid': args}) 31 32 33 def students(request): 34 if request.method=='GET': 35 students=models.Student.objects.all() 36 return render(request,'students.html',{'student':students}) 37 38 def students_add(request,nid): 39 if request.method=='GET': 40 obj=Students() 41 return render(request,'student_add.html',{'obj':obj,'nid':nid}) 42 else: 43 obj=Students(request.POST) 44 if obj.is_valid(): 45 models.Student.objects.create(**obj.cleaned_data) 46 return redirect('/students/') 47 else: 48 return render(request,'student_add.html',{'obj':obj,'nid':nid}) 49 50 51 def students_edit(request,nid): 52 if request.method=='GET': 53 # print(models.Student.objects.filter(id=nid).values('name','sex','cls_id').first()) 54 obj=Students(initial=models.Student.objects.filter(id=nid).values('name','sex','cls_id').first()) 55 return render(request,'student_edit.html',{'obj':obj,'nid':nid}) 56 else: 57 obj1=Students(request.POST) 58 if obj1.is_valid(): 59 models.Student.objects.filter(id=nid).update(**obj1.cleaned_data) 60 return redirect('/students/') 61 else: 62 return render(request, 'student_edit.html', {'obj': obj1, 'nid': nid}) 63 64 def teacher(request): 65 if request.method=='GET': 66 teacher_list=models.teacher.objects.all() 67 return render(request,'teacher_list.html',{'tlist':teacher_list}) 68 69 def teacher_add(request): 70 if request.method=='GET': 71 print(request.method) 72 obj=teacher_form() 73 return render(request,'teacher_add.html',{'obj':obj}) 74 else: 75 obj=teacher_form(request.POST) 76 if obj.is_valid(): 77 classe_ids=obj.cleaned_data.pop('classes') #老师任教的班级 78 tname=obj.cleaned_data #老师的姓名 79 create_teacher=models.teacher.objects.create(**tname) 80 create_teacher.c2t.add(*classe_ids) 81 return redirect('/teachers/') 82 else: 83 return render(request,'teacher_add.html',{'obj':obj}) 84 85 86 def teacher_edit(request,arg): 87 if request.method=='GET': 88 teacher_obj=models.teacher.objects.filter(id=arg).first() 89 classes_ids=teacher_obj.c2t.values_list('id') 90 cid_list=list(zip(*classes_ids))[0] if list(zip(*classes_ids)) else[] 91 # print(cid_list) 元组 92 obj=teacher_form(initial={'tname':teacher_obj.tname,'classes':cid_list}) 93 return render(request,'teacher_edit.html',{'obj':obj,'nid':arg} ) 94 else: 95 obj=teacher_form(request.POST) 96 if obj.is_valid(): 97 teacher_name=obj.cleaned_data.pop('tname') 98 models.teacher.objects.filter(id=arg).update(tname=teacher_name) 99 obj1=models.teacher.objects.filter(id=arg).first() 100 classes=obj.cleaned_data.pop('classes') 101 obj1.c2t.set(classes) 102 return redirect('/teachers/') 103 else: 104 return render(request,'teacher_add.html',{'obj':obj}) 105 106 107 def test(request): 108 if request.method=='GET': 109 obj=Test_form(initial={'holby':[ 1,2],'name':'张根','text':'我来自太行山','sex':1}) 110 # # print(models.Student.cls.objects.values_list()) 111 # obj=models.Student.objects.filter(id=1).first() 112 # print(obj.cls.title) 113 return render(request,'test.html',{'obj':obj})
form组件
1 from django.shortcuts import render,HttpResponse,redirect 2 from app01 import models 3 from django.forms import Form,fields 4 from django.forms import widgets 5 6 class Class_form(Form): 7 title=fields.RegexField('全栈\d+', 8 # initial='全栈', #设置input标签中的默认值 9 min_length=2, 10 required=True, 11 error_messages={'invalid':"必须以全栈开头", 12 'min_length':'太短了', 13 'required':"不能为空", 14 } 15 ) 16 class Students(Form): 17 name=fields.CharField(required=True, 18 widget=widgets.TextInput(attrs={'class':'form-control'}), 19 error_messages={'required':'姓名不能为空'}) 20 sex=fields.CharField(required=True,error_messages={'required':'不能为空'}, 21 widget = widgets.TextInput(attrs={'class': 'form-control'}) 22 23 ) 24 cls_id=fields.IntegerField(widget=widgets.Select(choices=models.Classes.objects.values_list('id','title'), 25 attrs = {'class': 'form-control'} 26 27 ), 28 29 ) 30 class teacher_form(Form): 31 tname=fields.CharField(required=True,error_messages={'required':"姓名不能为空"} ) 32 # classes=fields.CharField( #多选 不能用这个插件不使用request.post.getlist()取值 33 # widget=widgets.Select(choices=models.Classes.objects.values_list(), 34 # attrs={'multiple':'multiple'}) 35 # ) 36 # classes=fields.CharField(widget=widgets.SelectMultiple( 37 # # 如果在fields.CharField字段该插件,得到会是{'tname': 'ww', 'classes': "['2', '3']"}字符串 38 # choices= models.Classes.objects.values_list('id','title')) ) 39 classes=fields.MultipleChoiceField( 40 choices=models.Classes.objects.values_list('id','title'), 41 widget=widgets.SelectMultiple,error_messages={'required':'选择不能为空'}) 42 def __int__(self,*args,**kwargs): #解决数据不同步的bug,每次form组件实例化时 都重新去数据库拿数据 43 super(teacher_form,self).__init__(*args,**kwargs) 44 self.fields['classes'].choices=models.Classes.objects.values_list('id','title') 45 46 47 48 class Test_form(Form): 49 name=fields.CharField() #动态生成 text类型的input标签 50 text=fields.CharField(widget=widgets.Textarea,) #动态生成文本框 51 age=fields.CharField(widget=widgets.CheckboxInput) #动态生成单选框 52 holby=fields.MultipleChoiceField( #MultipleChoiceField动态生成复选框 53 choices=[(1,'篮球'),(2,"足球"),(3,"高俅")], 54 widget=widgets.CheckboxSelectMultiple) 55 sex=fields.MultipleChoiceField( 56 choices=[(1,'男'),(2,'女')], 57 widget=widgets.RadioSelect 58 ) 59 60 select=fields.ChoiceField(choices=[(2, '北京'), (3, '唐县')]) #通过ChoiceField字段动态生成单选框 61 # 通过form组件的MultipleChoiceField字段 62 mselect=fields.MultipleChoiceField(choices=[(1,'管家佐'),(2,'万宝利') ], 63 widget=widgets.SelectMultiple) 64 file=fields.FileField() #通过form组件的.FileField动态生成 文件上传input标签,注意在提交到后台是对象
模板
班级管理(1对1)
班级显示
1 <!DOCTYPE html> 2 <html lang="en"> 3 <head> 4 <meta charset="UTF-8"> 5 <title>班级列表</title> 6 <link rel="stylesheet" href="/static/bootstrap-3.3.5-dist/css/bootstrap.css"> 7 </head> 8 <body> 9 <div style="width: 500px;margin: 0 auto"> 10 <h1>班级列表</h1> 11 <ul> 12 {% for obj in clist %} 13 <li>{{ obj.title }} 14 <a href="/classes_add{{ obj.id }}/">添加</a> 15 <a href="/classes_edit{{ obj.id }}/">编辑</a> 16 </li> 17 {% endfor %} 18 </ul> 19 </div> 20 </body> 21 </html>
添加班级
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>添加班级</title> </head> <body> <h1>添加班级</h1> <form action='/classes_add{{2}}/' method="post" novalidate> {% csrf_token %} <p>班级添加:{{ obj.title}} {{ obj.errors.title.0}}</p> <p><input type="submit" value="提交"></p> </form> </body> </html>
编辑班级
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>编辑班级</title> </head> <body> <form action="/classes_edit{{nid}}/" method="post"> {% csrf_token %} <p>编辑班级:{{ obj.title }}{{obj.title.errors.0}} </p> <input type="submit" value="提交"> </form> </body> </html>
学生管理1对多
学生显示
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>学生管理</title> </head> <body> <h1>学生管理</h1> <ul> {% for row in student %} <li>{{row.name}} {{row.sex}} <a href="/students_add{{row.id}}/">添加</a> <a href="/students.edit{{row.id}}/">编辑</a> </li> {% endfor %} </ul> </body> </html>
学生添加
<!DOCTYPE html> <html lang="en"> <head> <h1>添加学生</h1> <meta charset="UTF-8"> <title>添加学生</title> </head> <body> <form action="/students_add{{ nid}}/" method="post" novalidate> {% csrf_token%} <p>姓名:{{ obj.name}}{{obj.name.errors.0}}</p> <p>性别:{{ obj.sex}}{{obj.sex.errors.0}} </p> <p>班级:{{ obj.cls_id }}{{ obj.errors.0}} </p> <p><input type="submit" value="提交"></p> </form> </body> </html>
学生编辑
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>老师编辑</title> </head> <body> <form action="/teacher_edit{{ nid }}/" method="post"> {% csrf_token %} <p>老师姓名:{{obj.tname }}</p> <p>班级:{{ obj.classes }}</p> <input type="submit" value="提交"> </form> </body> </html>
老师管理多对多
老师显示
1 <!DOCTYPE html> 2 <html lang="en"> 3 <head> 4 <meta charset="UTF-8"> 5 <title>老师列表</title> 6 </head> 7 <body> 8 <table border="1"> 9 <thead> 10 <tr><td>ID</td><td>老师</td><td>任教班级</td><td colspan="3">操作</td></tr> 11 </thead> 12 <tbody> 13 {% for teacher in tlist %} 14 <tr> 15 <td>{{ teacher.id }}</td> 16 <td>{{teacher.tname}}</td> 17 <td> 18 {% for row in teacher.c2t.values %} 19 {{ row.title }} 20 {% endfor %} 21 </td> 22 <td><a href="/teacher_add/">添加</a></td> 23 <td><a href="/teacher_edit{{ teacher.id}}/">编辑</a></td> 24 </tr> 25 {% endfor %} 26 </tbody> 27 </table> 28 </body> 29 </html>
老师添加
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>添加老师</title> </head> <body> <form action="/teacher_add/" method="post" novalidate> {% csrf_token %} <p>老师姓名:{{ obj.tname }}{{obj.tname.errors.0}}</p> <p>任教班级:{{ obj.classes }}{{obj.classes.errors.0}}</p> <p><input type="submit" value="提交"></p> </form> </body> </html>
老师编辑
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>老师编辑</title> </head> <body> <form action="/teacher_edit{{ nid }}/" method="post"> {% csrf_token %} <p>老师姓名:{{obj.tname }}</p> <p>班级:{{ obj.classes }}</p> <input type="submit" value="提交"> </form> </body> </html>
PS:Form验证组件验证ajax提交的json
Django的form组件,不仅可以对 浏览器端 提交过来的form表单数据做验证,还可以对ajax提交的 json数据 做验证,但是需要在发送之前获取CSRF-tocken设置在请求头中;
1 $.ajax({ 2 type: 'POST', 3 async: false, 4 cache: false, 5 url: '{% url "instance_add" %}', 6 headers:{"X-CSRFToken":$.cookie('csrftoken')}, 7 contentType: "application/json; charset=utf-8", 8 dataType: "json", 9 traditional:true, 10 data:JSON.stringify({ 11 "data_mode_type": $data_mode_type, 12 'database_type': $database_type, 13 'host': $host, 14 'port': $port, 15 'instance_nikename': $instance_nikename, 16 'db_business': $db_business, 17 'DBA': $DBA, 18 'responsible_person': $responsible_person 19 }), 20 success: function (data) { 21 alert(1) 22 } 23 }); 24 })
from django.shortcuts import render,HttpResponse,redirect from DB_auto.form_validate.add_dbinfo import dbinfo_create import json def instance_add(request): if request.method=='POST': json_data=json.loads(request.body.decode('utf-8')) obj=dbinfo_create(json_data) if obj.is_valid(): print(obj.cleaned_data) else: print(obj.errors) return render(request,'add_dbinfo.html')
七、ModelForm
使用Django开发web程序阶段回顾:
1.手动对单表进行增、删、该、查,手动把ORM操作获取的数据渲染到模板;(阶段1)
2.Form组件(类),自动生成标签(input、select),并对用户输入的数据做规则验证;(阶段2)
3.ModelForm顾名思义就Form和Django的Model数据库模型结合体,可以简单、方便得对数据库进行增加、编辑操作和验证标签的生成;
Form组件和ModelForm的区别
ModelForm是Django Model.py和Form组件的结合体,可以简单/快速使用 Form验证和数据库操作功能,但不如Form组件灵活,如果在使用Django做web开发过程中验证的数据和数据库字段相关(可以对表进行增、删、改操,注意 Many to many字段,也可以级联操作第3张关系表;),建议优先使用ModelForm,用起来更方便些,但是在使用ModelForm的时候慎用fields='__all__',获取数据库所有字段势必造成性能损耗;
1 ModelForm 2 a. class Meta: 3 model, # 对应Model的 4 fields=None, # 字段 5 exclude=None, # 排除字段 6 labels=None, # 提示信息 7 help_texts=None, # 帮助提示信息 8 widgets=None, # 自定义插件 9 error_messages=None, # 自定义错误信息(整体错误信息from django.core.exceptions import NON_FIELD_ERRORS) 10 field_classes=None # 自定义字段类 (也可以自定义字段) 11 localized_fields=('birth_date',) # 本地化,如:根据不同时区显示数据 12 如: 13 数据库中 14 2016-12-27 04:10:57 15 setting中的配置 16 TIME_ZONE = 'Asia/Shanghai' 17 USE_TZ = True 18 则显示: 19 2016-12-27 12:10:57 20 b. 验证执行过程 21 is_valid -> full_clean -> 钩子 -> 整体错误 22 23 c. 字典字段验证 24 def clean_字段名(self): 25 # 可以抛出异常 26 # from django.core.exceptions import ValidationError 27 return "新值" 28 d. 用于验证 29 model_form_obj = XXOOModelForm() 30 model_form_obj.is_valid() 31 model_form_obj.errors.as_json() 32 model_form_obj.clean() 33 model_form_obj.cleaned_data 34 e. 用于创建 35 model_form_obj = XXOOModelForm(request.POST) 36 #### 页面显示,并提交 ##### 37 # 默认保存多对多 38 obj = form.save(commit=True) 39 # 不做任何操作,内部定义 save_m2m(用于保存多对多) 40 obj = form.save(commit=False) 41 obj.save() # 保存单表信息 42 obj.save_m2m() # 保存关联多对多信息 43 44 f. 用于更新和初始化 45 obj = model.tb.objects.get(id=1) 46 model_form_obj = XXOOModelForm(request.POST,instance=obj) 47 ... 48 49 PS: 单纯初始化 50 model_form_obj = XXOOModelForm(initial={...})
使用ModelForm
前端:
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> {{ form_obj.as_p }} {#<p>姓名:{{form_obj.name }}</p>#} </body> </html>
后端视图:
1 from app02 import models 2 from django.forms import ModelForm 3 class UserModalForm(ModelForm): 4 class Meta: 5 model=models.UserInfo #(该字段必须为 model 数据库中表) 6 fields= '__all__' #(该字段必须为 fields 数据库中表) 7 8 def add(request): 9 # 实例化models_form 10 if request.method=='GET': 11 obj = UserModalForm() 12 return render(request,'rbac/user_add.html',locals()) 13 else: 14 obj=UserModalForm(request.POST) 15 if obj.is_valid(): 16 data=obj.cleaned_data 17 obj.save() #form验证通过直接 添加用户信息到数据库 18 return render(request, 'rbac/user_add.html', locals())
model.py:
使用使用Form组件和ModelForm注意事项:
1、model.py一点要写verbose_name='名称'参数,才会在前端显示P标签的标题;
from django.db import models class Department(models.Model): title=models.CharField(max_length=32,verbose_name='部门名称') def __str__(self): return self.title class UserInfo(models.Model): name=models.CharField(max_length=32,verbose_name='姓名') emai=models.EmailField(max_length=32,verbose_name='邮箱') pary=models.ForeignKey(Department,verbose_name='部门')
如果使用Form组件在前端显示标题,可以设置Form类中的lable参数
class UserInfoForm(Form): name=fields.CharField(required=True,error_messages={'reqired':'用户名不能为空'},label='姓名') email=fields.EmailField(required=True,error_messages={'reqired':'用户名不能为空'},label='邮箱') pary=fields.ChoiceField(choices=[(1,"技术部"),(2,'销售部'),(3,'市场部'),],label='部门')
2、pary=fields.ChoiceField(choices=models.Department.objects.values_list('pk','title'),label='部门'),前端select标签不能随数据库操作实时更新;
在Department表添加一条数据之后,前端select标签中的数据不能随数据库实时更新;
原因:
不管是ModelForm还是Form组件本质就是个类,fields(字段)本质就是类中的1个静态属性,在类第一次加载时赋值,永远不会更新;
解决方案1(手动档):
重写__init__方法,每次实例化对象,就去获取一次数据库内容,重新赋值;
class UserInfoForm(Form): name=fields.CharField(required=True,error_messages={'reqired':'用户名不能为空'},label='姓名') email=fields.EmailField(required=True,error_messages={'reqired':'用户名不能为空'},label='邮箱') pary=fields.ChoiceField(label='部门') # pary=fields.ChoiceField(choices=models.Department.objects.values_list('pk','title'),label='部门') def __init__(self,*args,**kwargs): super(UserInfoForm,self).__init__(*args,**kwargs) self.fields['pary'].choices=models.Department.objects.values_list('pk','title')
解决方案2(自动档)
导入ModelChoiceField 模块 from django.forms.models import ModelChoiceField
使用ModelChoiceField 字段
from django.forms import fields from django.forms import widgets from django.forms import ModelForm from django.forms.models import ModelChoiceField class UserInfoForm(Form): name=fields.CharField(required=True,error_messages={'reqired':'用户名不能为空'},label='姓名') email=fields.EmailField(required=True,error_messages={'reqired':'用户名不能为空'},label='邮箱') #方案1 pary=ModelChoiceField(queryset=models.Department.objects.all(),label='部门')
自动挡虽好,但是也有缺陷;前端option标签的内容,需要借助model.py中的__str__方法,生成;
八、扩展 widgets之富文本编辑框
如果你的web应用涉及到了发表文章、表情评论就必须使用富文本编辑框,小编调研了两款 Kindeditor、CKeditor;
Kindeditor
1.Kindeditor简介
http://kindeditor.net/docs/usage.html
2.下载Kindeditor编辑器
http://www.kindsoft.net/down.php
3.引入Kindeditor编辑器
<script src="/static/kindeditor-4.1.10/kindeditor-all.js"></script>
4.使用Kindeditor编辑器
a.创建textarea 标签
<textarea name="content" id="a" cols="30" rows="10"></textarea>
b.初始化Kindeditor
<script> var edit=KindEditor.create('#a',{ width:'700px', height:'500px', uploadJson:'/upload_file/', extraFileUploadParams:{ 'csrfmiddlewaretoken':'{{ csrf_token }}',}}) </script>
c. Kindeditor功能定制
<script> var edit = KindEditor.create('#a', { items: [ //填写自己想要的功能 'fontname', 'fontsize', '|', 'forecolor', 'hilitecolor', 'bold', 'italic', 'underline', 'removeformat', '|', 'justifyleft', 'justifycenter', 'justifyright', 'insertorderedlist', 'insertunorderedlist', '|', 'emoticons', 'image', 'link'], width: '700px', height: '500px', uploadJson: '/upload_file/', extraFileUploadParams: { 'csrfmiddlewaretoken': '{{ csrf_token }}', } }) </script>
d.kindeditor的API
// 取得HTML内容 html = editor.html(); // 同步数据后可以直接取得textarea的value editor.sync(); html = document.getElementById('editor_id').value; // 原生API html = K('#editor_id').val(); // KindEditor Node API html = $('#editor_id').val(); // jQuery // 设置HTML内容 editor.html('HTML内容');
CKeditor结合Django Form
1.下载 django-ckeditor 和pillow
pip install django-ckeditor
pip install pillow
2.setings
INSTALLED_APPS = [ 'django.contrib.admin', 'django.contrib.auth', 'django.contrib.contenttypes', 'django.contrib.sessions', 'django.contrib.messages', 'django.contrib.staticfiles', 'rbac.apps.RbacConfig', 'arya.apps.AryaConfig', 'cmdb.apps.CmdbConfig', 'rest_framework', 'ckeditor', # 'ckeditor_uploader' ]
CKEDITOR_CONFIGS = { 'default': { 'toolbar': ( # ['div','Source','-','Save','NewPage','Preview','-','Templates'], # ['Cut','Copy','Paste','PasteText','PasteFromWord','-','Print','SpellChecker','Scayt'], # ['Undo','Redo','-','Find','Replace','-','SelectAll','RemoveFormat'], # ['Form','Checkbox','Radio','TextField','Textarea','Select','Button', 'ImageButton','HiddenField'], # ['Bold','Italic','Underline','Strike','-','Subscript','Superscript'], # ['NumberedList','BulletedList','-','Outdent','Indent','Blockquote'], # ['JustifyLeft','JustifyCenter','JustifyRight','JustifyBlock'], # ['Link','Unlink','Anchor'], ['Smiley',], # ['Styles','Format','Font','FontSize'], # ['TextColor','BGColor'], # ['Maximize','ShowBlocks','-','About', 'pbckcode'], ), } }
3. 收集Django 项目所有 静态文件 到的目录static目录 / 直接下载ckeditor到项目static
注意:此步骤如果提示 需要你设置MEDIA_URL目录 和STATIC_ROOT 就先暂且设置一下,最终目的是把CKeditor的js插件copy到你的静态文件存放目录下;
4.后端生成 textarea +加前端 ckedit 插件生成 富文本编辑
from ckeditor.widgets import CKEditorWidget class comment(Form): #评论框 content =fields.CharField(widget=CKEditorWidget)
{{ obj.content|safe }} <script src="/static/pligin/ckeditor/ckeditor-init.js"></script> <script src="/static/pligin/ckeditor/ckeditor/ckeditor.js"></script>
5.CKEditorWidget API
<script> $('.root_reply_btn').click(function () { var $reply_user = $(this).attr('name'); var $pid = $(this).attr('pid'); $('#comment_submit').attr('pid', $pid); CKEDITOR.instances.id_content.insertText('回复' + $reply_user + ':'); CKEDITOR.instances.id_content.insertHtml('<br>'); CKEDITOR.instances.id_content.focus(); #使CKEditorWidget进入编辑状态 (MD在后台测试了半天!!) });
CKEDITOR.instances.id_content.document.$.body.firstChild.textContent 获取p标签的文本内容 </script>
6.完成效果
博客链接:http://www.cnblogs.com/wupeiqi/articles/6144178.html
https://www.cnblogs.com/SHENGXIN/p/7643554.html
http://www.cnblogs.com/yuanchenqi/articles/7439088.html#3770465
DjangoForm补充:http://www.cnblogs.com/yuanchenqi/articles/7487059.html