Django之Form、ModelForm 组件

 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
View Code
 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     密码 &nbsp;&nbsp;:<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
View Code

基于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})
views.py
 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 ]
urls.py
 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>
login.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>
test.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>
register.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)            时间间隔: % % 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')
View Code

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.
         =  (
             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
    )
View Code

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')
View Code

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>
View Code

关于对字段认证[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("输入的密码不一致!"))
View Code
 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})
View Code

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>
View Code

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))
View Code

自定义正则表达式验证

密码修改实例

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')
Django视图
 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("两次密码不一致")
Django的form验证

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})
View Code

前台

<!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>
View Code
 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})
View Code
<!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>
View Code

 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标签,注意在提交到后台是对象
View Code
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标签,注意在提交到后台是对象
View Code

 全局钩子函数

如果要想要同时对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("两次密码不一致")
View Code

  

四、渲染到模板

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>
View Code

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>
View Code

 

五、页面显示用户填完数据提交回来后台验证

数据校验

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),
]
View Code

视图

  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})
View Code

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标签,注意在提交到后台是对象
View Code

模板

班级管理(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>
View Code

添加班级

<!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>
View Code

编辑班级

<!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>
View Code

 

学生管理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>
View Code

学生添加

<!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>
View Code

学生编辑

<!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>
View Code

 

 老师管理多对多

老师显示

 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>
View Code

老师添加

<!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>
View Code

老师编辑

<!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>
View Code

 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')
django后台

 

 七、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={...})
Django之ModelForm组件(wupeiqi)

使用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>
View Code

后端视图:

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

 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='部门')
View Code

如果使用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='部门')
View Code

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')
View Code

解决方案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='部门')
View Code

自动挡虽好,但是也有缺陷;前端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路径+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>
View Code

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

转载于:https://www.cnblogs.com/lbzbky/articles/10975632.html

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值