Form组件

发布时间 2023-12-06 17:34:56作者: 木屐呀
 1Django的Form主要具有一下几大功能:
  • 生成HTML标签
  • 验证用户数据(显示错误信息)
  • HTML Form提交保留上次提交数据
  • 初始化页面显示内容
-对用户请求的验证
 2     -Ajax  ------------------------
 3     -Form  ------------------------
 4 -生成HTML代码(保留上次输入内容)
 5 
 6 a.创建一个类
 7 b.类中创建字段(包含正则表达式)
 8 c.GET
 9     obj = Fr()
10     obj.user => 自动生成HTML
11     
12 d.POST
13     obj = Fr(request.POST)
14     if obj.is_valid():
15         obj.cleaned_data
16     else:
17         obj.errors
18         return  ... {'obj':obj}
19 
20 - 初始化默认值

初步认识:

py文件

 1 from django.conf.urls import url
 2 from django.contrib import admin
 3 from APP01 import views
 4 from APP02 import views as v2
 5 
 6 urlpatterns = [
 7     url(r'^admin/', admin.site.urls),
 8     url(r'^users/', views.user),
 9     url(r'^add_user/', views.add_user),
10     url(r'^edit_user-(\d+)/', views.edit_user),
11 
12     url(r'^test01/', v2.test01),
13     url(r'^test02/', v2.test02),
14 
15 ]
urls.py
1 from django.db import models
2 
3 # Create your models here.
4 
5 class UserInfo(models.Model):
6     username = models.CharField(max_length=32)
7     email = models.EmailField(max_length=32)
models.py
1 from django import forms
2 from django.forms import fields
3 
4 class UserForm(forms.Form):
5     username = fields.CharField()
6     email = fields.EmailField()
form01.py
 1 from django.shortcuts import render,HttpResponse,redirect
 2 from APP01 import models
 3 # Create your views here.
 4 
 5 
 6 def user(request):
 7     user_list = models.UserInfo.objects.all()
 8     return  render(request,"users.html",{'user_list':user_list})
 9 
10 from APP01.form01 import UserForm
11 def add_user(request):
12     if request.method == "GET":
13         obj = UserForm()
14         return render(request,"add_user.html",{'obj':obj})
15     else:
16         obj = UserForm(request.POST)
17         if obj.is_valid():
18             print(obj.cleaned_data)
19             # 若Form组件字段与数据库表的字段不一样
20             # models.UserInfo.objects.create(
21             #     username = obj.cleaned_data['user'],
22             #     email = obj.cleaned_data['email']
23             # )
24             # 若Form组件字段与数据库表的字段一样
25             models.UserInfo.objects.create(**obj.cleaned_data)
26             return redirect('/users/')
27         else:
28             print(obj.errors)
29             return render(request,"add_user.html",{'obj':obj})
30 
31 def edit_user(request,nid):
32     if request.method == "GET":
33         # nid = request.GET.get('nid')
34         data = models.UserInfo.objects.filter(id=nid).first()
35         obj = UserForm({'username':data.username,'email':data.email})
36         return render(request,"edit_user.html",{'obj':obj,'nid':nid})
37     else:
38         obj = UserForm(request.POST)
39         if obj.is_valid():
40             print(obj.cleaned_data)
41             models.UserInfo.objects.filter(id=nid).update(**obj.cleaned_data)
42             return redirect('/users/')
43         else:
44             print(obj.errors)
45             return render(request,"edit_user.html",{'obj':obj,'nid':nid})
views.py

HTML文件

 1 <!DOCTYPE html>
 2 <html lang="en">
 3 <head>
 4     <meta charset="UTF-8">
 5     <title>Title</title>
 6 </head>
 7 <body>
 8     <a href="/add_user/">添加</a>
 9     <ul>
10         {% for row in user_list %}
11             <li>{{ row.id }}-{{ row.username }}-{{ row.email }}<a href="/edit_user-{{ row.id }}/">编辑</a></li>
12         {% endfor %}
13     </ul>
14 </body>
15 </html>
users.html
 1 <!DOCTYPE html>
 2 <html lang="en">
 3 <head>
 4     <meta charset="UTF-8">
 5     <title>Title</title>
 6 </head>
 7 <body>
 8     <form action="/add_user/" method="post" novalidate>
 9         <p>{{ obj.username }}{{ obj.errors.username.0 }}</p>
10         <p>{{ obj.email }}{{ obj.errors.email.0 }}</p>
11         <input type="submit" value="提交">
12     </form>
13 </body>
14 </html>
add_user.html
 1 <!DOCTYPE html>
 2 <html lang="en">
 3 <head>
 4     <meta charset="UTF-8">
 5     <title>Title</title>
 6 </head>
 7 <body>
 8 <form action="/edit_user-{{ nid }}/" method="post" novalidate>
 9     <p>{{ obj.username }}{{ obj.errors.username.0 }}</p>
10     <p>{{ obj.email }}{{ obj.errors.email.0 }}</p>
11     <input type="submit" value="提交">
12 </form>
13 </body>
14 </html>
edit_user.html

 

Form类

创建Form类时,主要涉及到 【字段】 和 【插件】,字段用于对用户请求数据的验证,插件用于自动生成HTML;

1、Django内置字段如下:

  1 Field
  2     required=True,               是否允许为空
  3     widget=None,                 HTML插件
  4     label=None,                  用于生成Label标签或显示内容
  5     initial=None,                初始值
  6     help_text='',                帮助信息(在标签旁边显示)
  7     error_messages=None,         错误信息 {'required': '不能为空', 'invalid': '格式错误'}
  8     show_hidden_initial=False,   是否在当前插件后面再加一个隐藏的且具有默认值的插件(可用于检验两次输入是否一直)
  9     validators=[],               自定义验证规则
 10     localize=False,              是否支持本地化
 11     disabled=False,              是否可以编辑
 12     label_suffix=None            Label内容后缀
 13  
 14  
 15 CharField(Field)
 16     max_length=None,             最大长度
 17     min_length=None,             最小长度
 18     strip=True                   是否移除用户输入空白
 19  
 20 IntegerField(Field)
 21     max_value=None,              最大值
 22     min_value=None,              最小值
 23  
 24 FloatField(IntegerField)
 25     ...
 26  
 27 DecimalField(IntegerField)
 28     max_value=None,              最大值
 29     min_value=None,              最小值
 30     max_digits=None,             总长度
 31     decimal_places=None,         小数位长度
 32  
 33 BaseTemporalField(Field)
 34     input_formats=None          时间格式化   
 35  
 36 DateField(BaseTemporalField)    格式:2015-09-01
 37 TimeField(BaseTemporalField)    格式:11:12
 38 DateTimeField(BaseTemporalField)格式:2015-09-01 11:12
 39  
 40 DurationField(Field)            时间间隔:%d %H:%M:%S.%f
 41     ...
 42  
 43 RegexField(CharField)
 44     regex,                      自定制正则表达式
 45     max_length=None,            最大长度
 46     min_length=None,            最小长度
 47     error_message=None,         忽略,错误信息使用 error_messages={'invalid': '...'}
 48  
 49 EmailField(CharField)      
 50     ...
 51  
 52 FileField(Field)
 53     allow_empty_file=False     是否允许空文件
 54  
 55 ImageField(FileField)      
 56     ...
 57     注:需要PIL模块,pip3 install Pillow
 58     以上两个字典使用时,需要注意两点:
 59         - form表单中 enctype="multipart/form-data"
 60         - view函数中 obj = MyForm(request.POST, request.FILES)
 61  
 62 URLField(Field)
 63     ...
 64  
 65  
 66 BooleanField(Field)  
 67     ...
 68  
 69 NullBooleanField(BooleanField)
 70     ...
 71  
 72 ChoiceField(Field)
 73     ...
 74     choices=(),                选项,如:choices = ((0,'上海'),(1,'北京'),)
 75     required=True,             是否必填
 76     widget=None,               插件,默认select插件
 77     label=None,                Label内容
 78     initial=None,              初始值
 79     help_text='',              帮助提示
 80  
 81  
 82 ModelChoiceField(ChoiceField)
 83     ...                        django.forms.models.ModelChoiceField
 84     queryset,                  # 查询数据库中的数据
 85     empty_label="---------",   # 默认空显示内容
 86     to_field_name=None,        # HTML中value的值对应的字段
 87     limit_choices_to=None      # ModelForm中对queryset二次筛选
 88      
 89 ModelMultipleChoiceField(ModelChoiceField)
 90     ...                        django.forms.models.ModelMultipleChoiceField
 91  
 92  
 93      
 94 TypedChoiceField(ChoiceField)
 95     coerce = lambda val: val   对选中的值进行一次转换
 96     empty_value= ''            空值的默认值
 97  
 98 MultipleChoiceField(ChoiceField)
 99     ...
100  
101 TypedMultipleChoiceField(MultipleChoiceField)
102     coerce = lambda val: val   对选中的每一个值进行一次转换
103     empty_value= ''            空值的默认值
104  
105 ComboField(Field)
106     fields=()                  使用多个验证,如下:即验证最大长度20,又验证邮箱格式
107                                fields.ComboField(fields=[fields.CharField(max_length=20), fields.EmailField(),])
108  
109 MultiValueField(Field)
110     PS: 抽象类,子类中可以实现聚合多个字典去匹配一个值,要配合MultiWidget使用
111  
112 SplitDateTimeField(MultiValueField)
113     input_date_formats=None,   格式列表:['%Y--%m--%d', '%m%d/%Y', '%m/%d/%y']
114     input_time_formats=None    格式列表:['%H:%M:%S', '%H:%M:%S.%f', '%H:%M']
115  
116 FilePathField(ChoiceField)     文件选项,目录下文件显示在页面中
117     path,                      文件夹路径
118     match=None,                正则匹配
119     recursive=False,           递归下面的文件夹
120     allow_files=True,          允许文件
121     allow_folders=False,       允许文件夹
122     required=True,
123     widget=None,
124     label=None,
125     initial=None,
126     help_text=''
127  
128 GenericIPAddressField
129     protocol='both',           both,ipv4,ipv6支持的IP格式
130     unpack_ipv4=False          解析ipv4地址,如果是::ffff:192.0.2.1时候,可解析为192.0.2.1, PS:protocol必须为both才能启用
131  
132 SlugField(CharField)           数字,字母,下划线,减号(连字符)
133     ...
134  
135 UUIDField(CharField)           uuid类型
136     ...

注: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')
UUID

2、Django内置插件:

 1 TextInput(Input)
 2 NumberInput(TextInput)
 3 EmailInput(TextInput)
 4 URLInput(TextInput)
 5 PasswordInput(TextInput)
 6 HiddenInput(TextInput)
 7 Textarea(Widget)
 8 DateInput(DateTimeBaseInput)
 9 DateTimeInput(DateTimeBaseInput)
10 TimeInput(DateTimeBaseInput)
11 CheckboxInput
12 Select
13 NullBooleanSelect
14 SelectMultiple
15 RadioSelect
16 CheckboxSelectMultiple
17 FileInput
18 ClearableFileInput
19 MultipleHiddenInput
20 SplitDateTimeWidget
21 SplitHiddenDateTimeWidget
22 SelectDateWidget

常用选择插件

 1 # 单radio,值为字符串
 2 # user = fields.CharField(
 3 #     initial=2,
 4 #     widget=widgets.RadioSelect(choices=((1,'上海'),(2,'北京'),))
 5 # )
 6  
 7 # 单radio,值为字符串
 8 # user = fields.ChoiceField(
 9 #     choices=((1, '上海'), (2, '北京'),),
10 #     initial=2,
11 #     widget=widgets.RadioSelect
12 # )
13  
14 # 单select,值为字符串
15 # user = fields.CharField(
16 #     initial=2,
17 #     widget=widgets.Select(choices=((1,'上海'),(2,'北京'),))
18 # )
19  
20 # 单select,值为字符串
21 # user = fields.ChoiceField(
22 #     choices=((1, '上海'), (2, '北京'),),
23 #     initial=2,
24 #     widget=widgets.Select
25 # )
26  
27 # 多选select,值为列表
28 # user = fields.MultipleChoiceField(
29 #     choices=((1,'上海'),(2,'北京'),),
30 #     initial=[1,],
31 #     widget=widgets.SelectMultiple
32 # )
33  
34  
35 # 单checkbox
36 # user = fields.CharField(
37 #     widget=widgets.CheckboxInput()
38 # )
39  
40  
41 # 多选checkbox,值为列表
42 # user = fields.MultipleChoiceField(
43 #     initial=[2, ],
44 #     choices=((1, '上海'), (2, '北京'),),
45 #     widget=widgets.CheckboxSelectMultiple
46 # )

练习:

  1 from django.shortcuts import render,HttpResponse,redirect
  2 
  3 # Create your views here.
  4 
  5 from django import forms
  6 from django.forms import fields
  7 from django.forms import widgets
  8 from APP01 import models
  9 
 10 class TestForm(forms.Form):
 11     ##################### 常用字段 #####################
 12     user = fields.CharField(
 13         required = True,  #是否必填
 14         label = '用户名',  #用于生成Label标签或显示内容
 15         initial = '请输入用户名',  #初始值
 16         max_length = 12,  #最大长度
 17         min_length = 3,   #最小长度
 18         #widget = widgets.Select(), #自定制生成HTML插件
 19         widget = widgets.TextInput(attrs={'class':'c1'}), #可添加标签属性
 20         # help_text = 'aaa',
 21         show_hidden_initial = False, #生成一个具有默认值的隐藏插件(可用于检验两次输入是否一致),默认是False
 22         # validators = []   #自定义验证规则
 23         # localize = False,    #是否支持本地化
 24         # disabled = True,     #是否可以编辑
 25         label_suffix = ':',  #label内容后缀
 26         error_messages = {   #自定义错误提示
 27             'required':'不可为空',
 28             'max_length':'太长了',
 29             'min_length':'太短了'
 30         },
 31 
 32     )
 33     age = fields.IntegerField(
 34         required = True,
 35         label = '年纪',
 36         error_messages = {
 37             'required':'不可为空',
 38             'min_value':'太小了',
 39             'max_value':'太大了'
 40         },
 41         initial = 0,
 42         max_value = 12,  #最大值
 43         min_value = 0    #最小值
 44     )
 45     email = fields.EmailField(
 46         required = True,
 47         label = '邮箱',
 48         error_messages = {
 49             'required':'不可为空',
 50         },
 51         initial = '请输入邮箱',
 52         label_suffix = ':'
 53     )
 54     price = fields.DecimalField(
 55         required = True,
 56         label = '价格',
 57         initial = 0,
 58         max_value = 10,  #最大值
 59         min_value = 0,   #最小值
 60         max_digits = 3,  #总长度
 61         decimal_places = 2, #小数位长度
 62         error_messages = {
 63             'required':'不可为空',
 64             'max_value':'太多了',
 65             'min_value':'太少了',
 66             'max_digits':'整体长度太长了',
 67             'decimal_places':'小数长度太长了'
 68         }
 69     )
 70     time01 = fields.DateField(
 71         required = True,
 72         label = '日期01',
 73     )
 74     time02 = fields.DateTimeField(
 75         required = True,
 76         label = '日期02',
 77     )
 78     img = fields.FileField(
 79         allow_empty_file = True, #是否允许空文件
 80         label = '文件上传',
 81     )
 82     city01 = fields.ChoiceField(
 83         choices = [(1,'上海'),(2,'北京'),(3,'深圳')],
 84         required = True,
 85         widget = None,  #插件,默认为select插件
 86         label = 'city01',
 87         # initial = 2,  #初始值
 88         help_text = '',
 89     )
 90     hobby01 = fields.TypedChoiceField(   #对提交的数据进行类型转换
 91         coerce = lambda x: int(x), #'city': 3,  提交的city对应的值从字符串变成了数字
 92         choices = [(1,'唱歌'),(2,'游泳'),(3,'踢球'),(4,'看书'),(5,'打麻将')],
 93         initial = 2,
 94         label = 'hobby01'
 95     )
 96     city02 = fields.MultipleChoiceField(
 97         choices = [(1,'上海'),(2,'北京'),(3,'深圳')],
 98         required = True,
 99         label = 'city02',
100         initial = [1,2]
101     )
102     hobby02 = fields.TypedMultipleChoiceField(
103         coerce = lambda x: int(x),  #对选中的每一个值进行一次转换
104         choices = [(1, '唱歌'), (2, '游泳'), (3, '踢球'), (4, '看书'), (5, '打麻将')],
105         initial = [1,2,3],
106         label = 'hobby02'
107     )
108     path = fields.FilePathField(
109         path = 'APP01'
110     )
111     ##################### 常用选择插件 #####################
112     # 单select,值为字符串
113     note01 = fields.CharField(
114         label = 'note01',
115         initial = 2,
116         widget = widgets.Select(choices=[(1,'唱歌'),(2,'游泳'),(3,'踢球'),(4,'看书'),(5,'打麻将')])
117     )
118     # 单select,值为数字
119     note02 = fields.IntegerField(
120         label='note02',
121         initial = 3,
122         widget=widgets.Select(choices=[(1, '唱歌'), (2, '游泳'), (3, '踢球'), (4, '看书'), (5, '打麻将')])
123     )
124     # 单select,值为字符串
125     note03 = fields.ChoiceField(
126         label = 'note03',
127         choices = [(1,'唱歌'),(2,'游泳'),(3,'踢球'),(4,'看书'),(5,'打麻将')],
128         initial = 3,
129         widget = widgets.Select(), #插件,默认为select插件
130     )
131     # 多选select,值为列表
132     note04 = fields.MultipleChoiceField(
133         label = 'note04',
134         initial = [1,2,3],
135         choices = [(1, '唱歌'), (2, '游泳'), (3, '踢球'), (4, '看书'), (5, '打麻将')],
136         widget = widgets.SelectMultiple(attrs={'class':'c1'})  #默认插件
137     )
138     # 多选select,值为字符串
139     note05 = fields.CharField(
140         label = 'note05',
141         initial = [1,2,3],
142         widget = widgets.SelectMultiple(choices=[(1, '唱歌'), (2, '游泳'), (3, '踢球'), (4, '看书'), (5, '打麻将')])
143     )
144     # 单选radio,值为字符串
145     note06 = fields.CharField(
146         label = 'note06',
147         initial = 2,
148         widget = widgets.RadioSelect(choices=[(1, '唱歌'), (2, '游泳'), (3, '踢球'), (4, '看书'), (5, '打麻将')])
149     )
150     # 单选radio,值为字符串
151     note07 = fields.ChoiceField(
152         label = 'note07',
153         choices = [(1, '唱歌'), (2, '游泳'), (3, '踢球'), (4, '看书'), (5, '打麻将')],
154         initial = 3,
155         widget =  widgets.RadioSelect()
156     )
157     # 单选checkbox
158     # note08 = fields.ChoiceField(
159     #     label = 'note08',
160     #     initial = 3,
161     #     choices = [(1, '唱歌'), (2, '游泳'), (3, '踢球'), (4, '看书'), (5, '打麻将')],
162     #     widget = widgets.CheckboxInput()
163     # )
164     # 多选checkbox
165     note09 = fields.MultipleChoiceField(
166         label = 'note09',
167         initial = [2,3],
168         choices = [(1, '唱歌'), (2, '游泳'), (3, '踢球'), (4, '看书'), (5, '打麻将')],
169         widget = widgets.CheckboxSelectMultiple()
170     )
171 def test01(request):
172     if request.method == "GET":
173         # obj = TestForm({'city01':3})  #传入默认选择的参数
174         obj = TestForm()
175         return render(request,"test01.html",{'obj':obj})
176     else:
177         obj = TestForm(request.POST)
178         obj.is_valid()
179         print(obj.cleaned_data)
180         # print(obj.cleaned_data['note01'])
181         # print(type(obj.cleaned_data['note01'])) #<class 'str'>
182         # print(obj.cleaned_data['note02'])
183         # print(type(obj.cleaned_data['note02'])) #<class 'int'>
184         # print(obj.cleaned_data['note03'])
185         # print(type(obj.cleaned_data['note03']))  # <class 'str'>
186         # print(obj.cleaned_data['note04'])
187         # print(type(obj.cleaned_data['note04']))  # <class 'list'>
188         # print(obj.cleaned_data['note05'])
189         # print(type(obj.cleaned_data['note05']))  # <class 'str'>
190         # print(obj.cleaned_data['note06'])
191         # print(type(obj.cleaned_data['note06']))  # <class 'str'>
192         # print(obj.cleaned_data['note07'])
193         # print(type(obj.cleaned_data['note07']))  # <class 'str'>
194         # print(obj.cleaned_data['note07'])
195         # print(type(obj.cleaned_data['note07']))  # <class 'str'>
196         # print(obj.cleaned_data['note09'])
197         # print(type(obj.cleaned_data['note09']))  # <class 'list'>
198         return render(request,"test01.html",{'obj':obj})
字段和插件
 1 <!DOCTYPE html>
 2 <html lang="en">
 3 <head>
 4     <meta charset="UTF-8">
 5     <title>Title</title>
 6 </head>
 7 <body>
 8 <form action="/test01/" method="post" novalidate>
 9     <p>{{ obj.user.label }}{{ obj.user }}{{ obj.errors.user.0 }}</p>
10     <p>{{ obj.age.label }}{{ obj.age }}{{ obj.errors.age.0 }}</p>
11     <p>{{ obj.email.label }}{{ obj.email }}{{ obj.errors.email.0 }}</p>
12     <p>{{ obj.price.label }}{{ obj.price }}{{ obj.errors.price.0 }}</p>
13     <p>{{ obj.time01.label }}{{ obj.time01 }}{{ obj.errors.time01.0 }}</p>
14     <p>{{ obj.time02.label }}{{ obj.time02 }}{{ obj.errors.time02.0 }}</p>
15     <p>{{ obj.img.label }}{{ obj.img }}{{ obj.errors.img.0 }}</p>
16     <p>{{ obj.city01.label }}{{ obj.city01 }}{{ obj.errors.city01.0 }}</p>
17     <p>{{ obj.hobby01.label }}{{ obj.hobby01 }}{{ obj.errors.hobby01.0 }}</p>
18     <p>{{ obj.city02.label }}{{ obj.city02 }}{{ obj.errors.city02.0 }}</p>
19     <p>{{ obj.hobby02.label }}{{ obj.hobby02 }}{{ obj.errors.hobby02.0 }}</p>
20     <p>{{ obj.path.label }}{{ obj.path }}{{ obj.errors.path.0 }}</p>
21     <p>{{ obj.note01.label }}{{ obj.note01 }}{{ obj.errors.note01.0 }}</p>
22     <p>{{ obj.note02.label }}{{ obj.note02 }}{{ obj.errors.note02.0 }}</p>
23     <p>{{ obj.note03.label }}{{ obj.note03 }}{{ obj.errors.note03.0 }}</p>
24     <p>{{ obj.note04.label }}{{ obj.note04 }}{{ obj.errors.note04.0 }}</p>
25     <p>{{ obj.note05.label }}{{ obj.note05 }}{{ obj.errors.note05.0 }}</p>
26     <p>{{ obj.note06.label }}{{ obj.note06 }}{{ obj.errors.note06.0 }}</p>
27     <p>{{ obj.note07.label }}{{ obj.note07 }}{{ obj.errors.note07.0 }}</p>
28 {#    <p>{{ obj.note08.label }}{{ obj.note08 }}{{ obj.errors.note08.0 }}</p>#}
29     <p>{{ obj.note09.label }}{{ obj.note09 }}{{ obj.errors.note09.0 }}</p>
30     <input type="submit" value="提交">
31 </form>
32 </body>
33 </html>
相关html代码

问题:在使用选择标签时,需要注意choices的选项可以从数据库中获取,但是由于是静态字段 ***获取的值无法实时更新***,那么需要自定义构造方法从而达到此目的

 

 1 - 特殊的单选或多选时,数据源是否能实时更新
 2         def love(request):
 3             return render(request,'love.html')
 4             
 5         class LoveForm(forms.Form):
 6             price = fields.IntegerField()
 7             user_id = fields.IntegerField(
 8             # 写死了
 9             #widget = widgets.Select(choices=[(1,'唱歌'),(2,'游泳'),(3,'踢球'),(4,'看书'),(5,'打麻将')]),
10             # 数据库
11             widget = widgets.Select(choices=models.UserInfo.objects.values_list('id','username'))
12             )
13             
14         #上面的问题在于当程序运行时,数据库数据更新,刷新页面数据源并不会更新,拿到的数据源还是第一次执行时拿到的数据,放在内存当中

两种方案:

 1 class LoveForm(forms.Form):
 2     price = fields.IntegerField()
 3     user_id = fields.IntegerField(
 4         # 写死了
 5         #widget = widgets.Select(choices=[(1,'唱歌'),(2,'游泳'),(3,'踢球'),(4,'看书'),(5,'打麻将')]),
 6         # 数据库
 7         widget = widgets.Select()
 8     )
 9     # 第二种:更新数据源,不推荐
10     user_id2 = ModelChoiceField(
11         queryset = models.UserInfo.objects.all(),
12         to_field_name = 'id'  # HTML中value的值对应的字段
13     )
14 
15     # 第一种:更新数据源,推荐
16     #  增加__init__目的是 当页面刷新加载时,会执行__init__,这样每次更新完数据,拿到的数据源都是最新的
17 
18     def __init__(self,*args,**kwargs):
19         # 拷贝所有的静态字段,复制给self.fields,这样self.fields里面才有值可取
20         super(LoveForm,self).__init__(*args,**kwargs)
21         self.fields['user_id'].widget.choices = models.UserInfo.objects.values_list('id','username')
22 
23 ############# models.py #################
24 # 第二种:更新数据源,不推荐
25 # 原因:注意:依赖models中的str方法
26 class UserInfo(models.Model):
27     username = models.CharField(max_length=32)
28     email = models.EmailField(max_length=32)
29 
30     #问题:耦合性太强,只能显示username,其他不能显示
31     def __str__(self):
32         return self.username

 

 1 ######################  数据源更新 ######################
 2 from django.forms.models import ModelChoiceField
 3 from django.core.validators import RegexValidator
 4 class LoveForm(forms.Form):
 5     price = fields.IntegerField()
 6     user_id = fields.IntegerField(
 7         #写死了
 8         # widget = widgets.Select(choices=[(1,'唱歌'),(2,'游泳'),(3,'踢球'),(4,'看书'),(5,'打麻将')])
 9         widget = widgets.Select()
10         # widget = widgets.Select(choices=models.UserInfo.objects.values_list("id","username"))
11     )
12     # 第二种:更新数据源,不推荐
13     user_id2 = ModelChoiceField(
14         queryset = models.UserInfo.objects.all(),
15         to_field_name = 'id'  # HTML中的value的值对应的字段
16     )
17     def __init__(self,*args,**kwargs):
18     # 第一种:更新数据源,推荐
19     # 拷贝所有的静态字段,复制给self.fields,这样self.fields里面才有值可取
20         super(LoveForm,self).__init__(*args,**kwargs)
21         self.fields['user_id'].widget.choices = models.UserInfo.objects.values_list("id","username")
22 
23 def test02(request):
24     obj = LoveForm()
25     return render(request,"test02.html",{'obj':obj})
数据源更新
 1 <!DOCTYPE html>
 2 <html lang="en">
 3 <head>
 4     <meta charset="UTF-8">
 5     <title>Title</title>
 6 </head>
 7 <body>
 8     <h1>大型人口</h1>
 9     <p>价格:{{ obj.price }}</p>
10     <p>姓名1:{{ obj.user_id }}</p>
11     <p>姓名2:{{ obj.user_id2 }}</p>
12 </body>
13 </html>
相关html代码

widget = widgets.TextInput(attrs={'class':'c1'})  #可添加自定义属性

1 #后端  -> 将HTML提前渲染,可以在前端直接显示
2 txt = "<input type='text'>"
3 from django.utils.safestring import mark_safe
4 txt = mark_safe(txt)
5 
6 #前端  -> 加上|safe 可以进行渲染
7 obj.user|safe

对用户请求的验证

-Form表单(验证;无需保留上一次内容) ====== 需要Form组件实现保留数据

-Ajax(验证;保留上一次内容) =========Ajax提交,页面不会跳转刷新,所以直接保留上一次内容

Ajax提交验证 + Form组件扩展

1.错误类型

 1 if obj.is_valid():
 2     print(obj.cleaned_data)
 3     if ...:
 4         obj.errors['username'] = ['用户名已经存在','不能为空']
 5         obj.errors['email'] = ['用户名已经存在','不能为空']
 6         
 7 #字段错误信息格式
 8 # {
 9 #     'username':['错误1','错误2']
10 # }
11 #整体错误信息格式
12 # {
13 #     __all__:['整体错误信息']
14 # }

 源码分析:

  1   2 def is_valid(self):
  3     """
  4     Returns True if the form has no errors. Otherwise, False. If errors are
  5     being ignored, returns False.
  6     """
  7     return self.is_bound and not self.errors
  8   9 @property
 10 def errors(self):
 11     "Returns an ErrorDict for the data provided for the form"
 12     if self._errors is None:
 13         self.full_clean() #执行这一步
 14     return self._errors   #此时错误为空
 15  16 def full_clean(self):
 17     """
 18     Cleans all of self.data and populates self._errors and
 19     self.cleaned_data.
 20     """
 21     self._errors = ErrorDict()  #错误为空
 22     if not self.is_bound:  # Stop further processing.
 23         return
 24     self.cleaned_data = {}
 25     # If the form is permitted to be empty, and none of the form data has
 26     # changed from the initial data, short circuit any validation.
 27     if self.empty_permitted and not self.has_changed():
 28         return
 29 
 30     self._clean_fields()   #字段验证
 31     self._clean_form()     #整体验证
 32     self._post_clean()     #自定义
 33  34 def _clean_fields(self):
 35     for name, field in self.fields.items():
 36         # value_from_datadict() gets the data from the data dictionaries.
 37         # Each widget type knows how to retrieve its own data, because some
 38         # widgets split data over several HTML fields.
 39         if field.disabled:
 40             value = self.get_initial_for_field(field, name)
 41         else:
 42             value = field.widget.value_from_datadict(self.data, self.files, self.add_prefix(name))
 43         try:
 44             if isinstance(field, FileField):
 45                 initial = self.get_initial_for_field(field, name)
 46                 value = field.clean(value, initial)
 47             else:
 48                 value = field.clean(value)
 49             self.cleaned_data[name] = value
 50             if hasattr(self, 'clean_%s' % name):
 51                 value = getattr(self, 'clean_%s' % name)()
 52                 self.cleaned_data[name] = value
 53         except ValidationError as e:
 54             self.add_error(name, e)   #增加错误信息
 55 def _clean_form(self):
 56     try:
 57         cleaned_data = self.clean()
 58     except ValidationError as e:
 59         self.add_error(None, e)   #增加错误信息
 60     else:
 61         if cleaned_data is not None:
 62             self.cleaned_data = cleaned_data
 63  64 def add_error(self, field, error):
 65     if not isinstance(error, ValidationError):
 66         # Normalize to ValidationError and let its constructor
 67         # do the hard work of making sense of the input.
 68         error = ValidationError(error)
 69 
 70     if hasattr(error, 'error_dict'):
 71         if field is not None:
 72             raise TypeError(
 73                 "The argument `field` must be `None` when the `error` "
 74                 "argument contains errors for multiple fields."
 75             )
 76         else:
 77             error = error.error_dict
 78     else:
 79         error = {field or NON_FIELD_ERRORS: error.error_list}
 80 
 81     for field, error_list in error.items():
 82         if field not in self.errors:
 83             if field != NON_FIELD_ERRORS and field not in self.fields:
 84                 raise ValueError(
 85                     "'%s' has no field named '%s'." % (self.__class__.__name__, field))
 86             if field == NON_FIELD_ERRORS:
 87                 self._errors[field] = self.error_class(error_class='nonfield')
 88             else:
 89                 self._errors[field] = self.error_class()
 90         self._errors[field].extend(error_list)
 91         if field in self.cleaned_data:
 92             del self.cleaned_data[field]
 93  94 NON_FIELD_ERRORS = '__all__'  #全部信息共有的错误
 95 
 96 class ValidationError(Exception):
 97     def __init__(self, message, code=None, params=None):
 98         # PY2 can't pickle naive exception: http://bugs.python.org/issue1692335.
 99         super(ValidationError, self).__init__(message, code, params)
100 
101         if isinstance(message, ValidationError):
102             if hasattr(message, 'error_dict'):
103                 message = message.error_dict
104             # PY2 has a `message` property which is always there so we can't
105             # duck-type on it. It was introduced in Python 2.5 and already
106             # deprecated in Python 2.6.
107             elif not hasattr(message, 'message' if six.PY3 else 'code'):
108                 message = message.error_list
109             else:
110                 message, code, params = message.message, message.code, message.params
111 
112         if isinstance(message, dict):
113             self.error_dict = {}
114             for field, messages in message.items():
115                 if not isinstance(messages, ValidationError):
116                     messages = ValidationError(messages)
117                 self.error_dict[field] = messages.error_list
118 
119         elif isinstance(message, list):
120             self.error_list = []
121             for message in message:
122                 # Normalize plain strings to instances of ValidationError.
123                 if not isinstance(message, ValidationError):
124                     message = ValidationError(message)
125                 if hasattr(message, 'error_dict'):
126                     self.error_list.extend(sum(message.error_dict.values(), []))
127                 else:
128                     self.error_list.extend(message.error_list)
129 
130         else:
131             self.message = message
132             self.code = code
133             self.params = params
134             self.error_list = [self]
错误源码

2.验证扩展

 第一种:

源码:

 1 执行顺序:is_valid -> self.errors -> self.full_clean() -> self._clean_fields()
 2 # 源码
 3 def _clean_fields(self):
 4     for name, field in self.fields.items():             # name -> price user_id     # field -> fields.IntegerField()
 5         # value_from_datadict() gets the data from the data dictionaries.
 6         # Each widget type knows how to retrieve its own data, because some
 7         # widgets split data over several HTML fields.
 8         if field.disabled:
 9             value = self.get_initial_for_field(field, name)
10         else:
11             value = field.widget.value_from_datadict(self.data, self.files, self.add_prefix(name))
12         try:
13             if isinstance(field, FileField):
14                 initial = self.get_initial_for_field(field, name)
15                 value = field.clean(value, initial)
16             else:
17                 value = field.clean(value)             ###########上面的都是进行正则表达式判断
18             self.cleaned_data[name] = value          ####不管正则匹配是否正确,value直接赋值
19             if hasattr(self, 'clean_%s' % name):     #  先判断是否存在‘clean_%s’方法
20                 value = getattr(self, 'clean_%s' % name)()   # 若存在,则将经过‘clean_%s’方法处理,得到新的value
21                 self.cleaned_data[name] = value     #再重新进行赋值
22         except ValidationError as e:
23             self.add_error(name, e)

使用:

 1 扩展:单个字段错误验证
 2     from django.core.exceptions import NON_FIELD_ERRORS, ValidationError
 3     class AjaxForm(forms.Form):
 4         username = fields.CharField()
 5         user_id = fields.IntegerField(
 6             widget = widgets.Select(choices=[(1,'唱歌'),(2,'游泳'),(3,'踢球'),(4,'看书'),(5,'打麻将')]),
 7         )
 8         # 源码更改,自定义方法clean_字段名
 9         # 必须返回值 self.cleaned_data['username']
10         # 如果出错,raise ValidationError('用户名已存在')
11         # 先执行正则匹配,然后根据字段依次执行下面的函数,得到处理完后的value值
12         def clean_username(self):
13             v = self.cleaned_data['username']  #用户提交的数据
14             #进行数据库匹配是否存在
15             if models.UserInfo.objects.filter(username=v).count():
16                 # 整体错误
17                 # 自己详细的错误信息
18                 raise ValidationError('用户名已存在')
19             return v
20         def clean_user_id(self):
21             return self.cleaned_data['user_id']

第二种:

源码:

 1 执行顺序:is_valid -> self.errors -> self.full_clean() -> self._clean_form()
 2 ##########################   适用于整体信息进行验证
 3 def _clean_form(self):
 4     try:
 5         cleaned_data = self.clean()  #执行clean方法
 6     except ValidationError as e:
 7         self.add_error(None, e)
 8     else:
 9         if cleaned_data is not None:
10             self.cleaned_data = cleaned_data
11 
12 def clean(self):
13     """
14     Hook for doing any extra form-wide cleaning after Field.clean() has been
15     called on every field. Any ValidationError raised by this method will
16     not be associated with a particular field; it will have a special-case
17     association with the field named '__all__'.
18     """
19     return self.cleaned_data

使用:

1 # 自定义扩展clean方法
2 def clean(self):
3     value_dict = self.cleaned_data
4     v1 = value_dict.get('username')
5     v2 = value_dict.get('user_id')
6     if v1 == 'root' and v2 == 1:
7         raise ValidationError('整体错误信息')
8     return self.cleaned_data

第三种:

 1 扩展3:
 2     is_valid -> self.errors -> self.full_clean() -> self._post_clean()
 3     
 4         def _post_clean(self):
 5             """
 6             An internal hook for performing additional cleaning after form cleaning
 7             is complete. Used for model validation in model forms.
 8             """
 9             pass
10     先不举例说明此方法的自定义

Ajax提交+Form扩展练习:

 1 #################Form组件之Ajax提交
 2 from django.core.exceptions import NON_FIELD_ERRORS, ValidationError
 3 class AjaxForm(forms.Form):
 4     username = fields.CharField()
 5     user_id = fields.IntegerField(
 6         widget = widgets.Select(choices=[(1,'唱歌'),(2,'游泳'),(3,'踢球'),(4,'看书'),(5,'打麻将')]),
 7     )
 8     # 源码更改,自定义方法clean_字段名
 9     # 必须返回值 self.cleaned_data['username']
10     # 如果出错,raise ValidationError('用户名已存在')
11     # 先执行正则匹配,然后根据字段依次执行下面的函数,得到处理完后的value值
12     def clean_username(self):
13         v = self.cleaned_data['username']  #用户提交的数据
14         #进行数据库匹配是否存在
15         if models.UserInfo.objects.filter(username=v).count():
16             # 整体错误
17             # 自己详细的错误信息
18             raise ValidationError('用户名已存在')
19         return v
20     def clean_user_id(self):
21         return self.cleaned_data['user_id']
22     # 自定义扩展clean方法:适用于整体信息进行验证
23     def clean(self):
24         value_dict = self.cleaned_data
25         v1 = value_dict.get('username')
26         v2 = value_dict.get('user_id')
27         if v1 == 'root' and v2 == 1:
28             raise ValidationError('整体错误信息')
29         return self.cleaned_data
30 
31 def ajax(request):
32     if request.method == "GET":
33         obj = AjaxForm()
34         return render(request,"ajax.html",{'obj':obj})
35     else:
36         ret = {'status':'no money','message':None}
37         import json
38         obj = AjaxForm(request.POST)
39         if obj.is_valid():
40             print(obj.cleaned_data)
41             # if ...:
42             #     obj.errors['username'] = ['用户名已经存在','不能为空']
43             #     obj.errors['email'] = ['用户名已经存在','不能为空']
44             ret['status'] = 'money'
45             # return redirect('http://www.baidu.com')   -> 不会进行刷新跳转
46             return HttpResponse(json.dumps(ret))   # -> 将信息发送到前端,前端进行刷新跳转
47         else:
48             print(obj.errors)
49             print(type(obj.errors))  #错误信息类型:<class 'django.forms.utils.ErrorDict'>
50             from django.forms.utils import ErrorDict   # ErrorDict继承dict
51             print(obj.errors.as_ul())  #默认返回ul类型
52             print(obj.errors.as_json())
53             print(obj.errors.as_data())
54             #字段错误信息格式
55             # {
56             #     'username':['错误1','错误2']
57             # }
58             #整体错误信息格式
59             # {
60             #     __all__:['整体错误信息']
61             # }
62             ret['message'] = obj.errors
63             return HttpResponse(json.dumps(ret))
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     <form id="fm" action="/ajax/" method="post">
 9         {{ obj.as_p }}
10         <input type="button" value="Ajax提交" id="btn">
11     </form>
12     <script src="/static/jquery-3.3.1.min.js"></script>
13     <script>
14         $(function () {
15             $('#btn').click(function () {
16                 $.ajax({
17                     url: '/ajax/',
18                     type: 'POST',
19                     data: $('#fm').serialize(),
20                     dataType: 'JSON',
21                     success:function (arg) {
22                         // arg: 状态,错误信息
23                         if (arg.status == "money"){
24                             window.location.href = 'http://www.baidu.com';  {# 进行跳转 #}
25                         }
26                         console.log(arg);
27                     }
28                 })
29             })
30         })
31     </script>
32 </body>
33 </html>
ajax.html

其他:

 1 利用Form组件自带的正则扩展
 2     a.方式一
 3         from django import forms
 4         from django.forms import fields
 5         from django.forms import widgets
 6         from django.core.validators import RegexValidator
 7         
 8         class MyForm(Form):
 9             user = fields.CharField(
10                 error_messages={'invalid':'....'},
11                 validators=[RegexValidator(r'^[0-9]+$','请输入数字'),RegexValidator(r'^159[0-9]+$','数字必须以159开头')],
12             )
13     b.方式二
14         from django import forms
15         from django.forms import fields
16         from django.forms import widgets
17         from django.core.validators import RegexValidator
18         
19         class MyForm(Form):
20             user = fields.RegexField(r'^[0-9]+$',error_messages={'invalid':'....'})

 初始化数据

在Web应用程序中开发编写功能时,时常用到获取数据库中的数据并将值初始化在HTML中的标签上

 1 from django.forms import Form
 2 from django.forms import widgets
 3 from django.forms import fields
 4 from django.core.validators import RegexValidator
 5  
 6  
 7 class MyForm(Form):
 8     user = fields.CharField()
 9  
10     city = fields.ChoiceField(
11         choices=((1, '上海'), (2, '北京'),),
12         widget=widgets.Select
13     )
form.py
 1 from django.shortcuts import render, redirect
 2 from .forms import MyForm
 3  
 4  
 5 def index(request):
 6     if request.method == "GET":
 7         values = {'user': 'root', 'city': 2}
 8         obj = MyForm(values)
 9  
10         return render(request, 'index.html', {'form': obj})
11     elif request.method == "POST":
12         return redirect('http://www.google.com')
13     else:
14         return redirect('http://www.google.com')
views.py
1 <form method="POST" enctype="multipart/form-data">
2     {% csrf_token %}
3     <p>{{ form.user }} {{ form.user.errors }}</p>
4     <p>{{ form.city }} {{ form.city.errors }}</p>
5  
6     <input type="submit"/>
7 </form>
index.html