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 ]
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)
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()
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})
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>
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>
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>
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')
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>
问题:在使用选择标签时,需要注意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>
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))
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>
其他:
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 )
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')
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>