python2.0_day18_django_form
Django form
Django admin
为什么要讲form,Django里的form能做什么.
前面day16节 简单学习了Django admin,我们知道当我们的models在admin.py文件里注册后,就可以通过admin后台管理注册的数据表了.
可以通过页面上对表进行记录的添加.
需要注意的是默认在admin后台页面上对某一个表,比如Book类,进行添加记录的时候.我们在input标签里必须都输入内容.否则就提示报错.
这个是怎样实现的呢?我们首先想到的是通过编辑模版文件,限制表单不为空.那问题又来了,如果有些字段可以为空呢.你就说在html中不做js的验证.
对能实现.假如总共就10个字段,也就是在10个input标签进行设置.没觉得麻烦.那如果是100个字段,你还要手动设置岂不是很麻烦.且不说每一个input的属性要求不一样了.
就算一样全部要求需要做验证 ,js不是要写很多标签的验证(因为每一个标签提示的报错内容都不一样).那么就会想到,能不能做程公共的插件,然后在用的时候调用.
可以bootstrap validate 就是这么一个插件.当然你也可以自己写,但是自己写的不够漂亮.不管怎样,是能实现的,这里的实现是 --- 前端验证
能够使用js在前端进行验证,也可以在后端验证,前端不做任何验证,form表单最终以post或者get形式提交.我们就通过判断get和post的值来做判断.
而Django就可以很方便的实现在后端验证,它就在后台里提供Form模块,使用Form模块创建自己form类.然后再在form类中像定义数据库字段一样定义每一个input标签的属性.这样,当视图views.py里的函数收到request.post或者get请求后,可以讲请求的内容传入我们创建的类,然后调用验证方法完成验证.
你以为这就完成了,Django的form类远远不只有这个功能.我们看在form类中创建的字段是用来做验证的,那后端怎么保证接收到的post请求数据就能和我创建的form类中进行一一对应呢?
当然是在创建前端html模版的时候就要定义好字段的名称,以便和后台字段一一对应.那么怎样做呢?我们知道靠程序员来根据自己之前定义的类,小心谨慎的做一一对应,是可以,但是这效率不高.这时就引出form类的另一大功能:
form类根据定义的字段属性,自动生成html前端页面代码.这样views在return时,把这个类作为参数传到前端,那么传过去的既有html代码也有数据。
总结
django中的Form一般有两种功能:1.输入html 2.验证用户输入
Django form 使用实例:
1.创建一个新的URL,我们应该按照一个请求的顺序.比如Django框架中,先经过urls.py文件
所以我们在app01/urls.py文件,添加一个URL
1 urlpatterns = [2 ...
3 url(r'^book_form/$',views.book_form),
4 ]
2.然后通过URL后,就会调用views.py文件下的book_form,
所以我们在views.py文件里添加一个book_form视图,我们知道一个URL都有可能有get和post请求.一般默认get请求都是读数据.所以一般返回的都是页面.
那么页面返回就得通过render 将 模版文件返回.
所以按照常规,创建一个views视图和创建一个模版应该属于同一个级别的.其次再是对视图和模版进行加工的工作
2.1 创建模版文件templates/app01/book_form.html
2.2 创建视图
1 def book_form(request):2 if request.method == 'POST':
3 pass
4 return render(request,'app01/book_form.html')
3.进入视图后,我们这里要使用到Django里的form模块进行创建form类,所以理论上应该在views.py文件里创建一个form类,但是我们在平时的开发中,有N个页面都需要用到form表单提交.每一个form表单的字段都不一样.所以理论上来说每一个需要提交的form表单就是一个单独的类.那么类多了,最好是单独写一个文件.于是我们在app01目录下创建一个forms.py文件.
最简单的一个form表单:
1 from django import forms2 class BookForm(forms.Form):
3 # 定义表单字段的时候要注意一定要和这个form表单将要提交到的ORM类保持一致
4 title = forms.CharField(max_length=10)
5 publisher_id = forms.IntegerField(widget=forms.Select)
6 # 这里models.py文件里的Book类是外键,外键对应的是一个类,而form类中无外键类型.所以这里不能用外键
7 # 同时models.py文件里虽然是外键,但实际写到数据库中的是外键的id,所以这里用publisher_id ,代表publisher.id
8 # widget=forms.Select 这个是数据类型的一个属性.主要是定义你在form表单代码内用什么样的标签类型.
9 publish_date = forms.DateField()
10 # 时间类型
11 # 当然这里少了一个many_to_many 的作者字段.咱们先不管,因为这个多对多关系并不在Book表中体现,而是在app01_book_authors
12 # 我们稍后在来处理这个多对多关系的填报方法
4. 我们在form表单中写了3个字段,就需要在前端显示3个字段.怎么返回给前端?就通过views视图,返回给前端,于是再次更改视图和模版文件.
总结Django中更改视图和模版经常会一起,所以当你更改时,要多关注对方
视图文件更改成:
1 def book_form(request):2 form = forms.BookForm()
3 if request.method == 'POST':
4 pass
5 return render(request,'app01/book_form.html',{'book_form':form}) #这里传给模版html
模版html更改成:
1 <body>2 <h1>222222</h1>
3 {{ book_form }}
4 </body>
这时候在访问
http://127.0.0.1:8000/payment/book_form/
返回结果如下图:
我们看到的结果是在form中我们写的字段,都拿出来做提示了:
title 显示了Title:
publisher_id 显示了Publisher id
publish_date 显示了Publish date
这也算是一个知识点把.
优化前端代码,添加 input的commit类型标签,实现提交功能
这时候我们看下前端页面中,form被传入到模版中到底以什么样的前端代码转换的?
1 <body>2 <h1>222222</h1>
3
4 <label for="id_title">Title:</label>
5 <input id="id_title" maxlength="10" name="title" type="text">
6 <label for="id_publisher_id">Publisher id:</label>
7 <select id="id_publisher_id" name="publisher_id">
8 </select>
9 <label for="id_publish_date">Publish date:</label>
10 <input id="id_publish_date" name="publish_date" type="text">
11
12 </body>
我们看到,虽然显示成输入标签,并且Django中也称之为form表单,但是实际中它并没有真正的做在form 标签中,而是转换成应该写在form标签内的标签代码.
所以我们应该在html模版中加上form标签,用来包含被转换的前端代码,于是更改模版html为:
1 <body>2 <form action="/payment/book_form" method="post"> {% csrf_token %}
3 {{ book_form }}
4 <input type="submit" value="创建图书">
5 </form>
6 </body>
这里post方式提交,我们前面总结了修改 模版html 和 视图views一般都是连动的,既然这里有post,我们就应该在视图里,做出对post的数据的处理.
所以视图更改如下:
1 def book_form(request):2 form = forms.BookForm()
3 if request.method == 'POST':
4 print(request.POST)
5 return render(request,'app01/book_form.html',{'book_form':form}) #这里传给模版html
到这里位置,我们已经把后台创建的form表单传到了前端.并且在前端打开页面也已经看到了
这时候只是看到Django form表单的功能一:1.输入html
那功能二:2.验证用户输入 如何实现呢?
更改views.py视图函数,实现form类验证功能
要想进行验证:
1.需要把前端页面输入的内容传入到form表单模块创建的类中生成实例.
2.调用form类的验证方法.
代码如下:
1 def book_form(request):2 form = forms.BookForm()
3 if request.method == 'POST':
4 print(request.POST)
5 form = forms.BookForm(request.POST) #将前端输入的数据,传入forms.BookForm类生成实例
6 if form.is_valid(): # 调用form 的验证方法
7 print("form is ok")
8 else:
9 print(form.errors)
10 return render(request,'app01/book_form.html',{'book_form':form}) #这里传给模版html
此时我们就可以在前端页面输入,查看验证效果:
紧接着,我们会发现publisher_id这个外键的select里没有任何内容.
我们怎么把这种外键关联的内容显示出来呢?
首先我们知道这种外键关联的字段,1.一般都是在数据库中存储外键关联对象的id值.2.在页面上展示的时候都是用这种选择框方式展现出来的.
3.并且选择框里的内容是动态的.
而我们通过form模块中的Form类创建的表单.实例化后,这个实例可就不会在更新了.
你即使在定义class BookForm(forms.Form): ... 里引入models模块,调用models.Publisher.objects.all()获得列表,也不行,因为一旦实例化后就不会在更新了.
所以使用forms.Form就无法实现这种动态显示后台数据的需求.
使用FormModel是可以实现的,这里先不说.
但如果我们在不使用FromModel方式情况下,如何实现这个需求呢.
可以把publisher_id中从form类中踢出来 .在视图中单独作为一个字典传入.
forms.py文件改成:
1 class BookForm(forms.Form):2 title = forms.CharField(max_length=10)
3 publish_date = forms.DateField()
views.py 文件改成
1 def book_form(request):2 form = forms.BookForm()
3 if request.method == 'POST':
4 print(request.POST)
5 form = forms.BookForm(request.POST) #将前端输入的数据,传入forms.BookForm类生成实例
6 if form.is_valid():
7 print("form is ok")
8 else:
9 print(form.errors)
10 publisher_list = models.Publisher.objects.all()
11 return render(request,'app01/book_form.html',{'book_form':form},{'publishers':publisher_list}) #这里单独传入publishers作为参数
模版的app01/book_form.html文件改成
1 <body>2
3 <form action="/payment/book_form/" method="post">{% csrf_token %}
4 {{ book_form }}
5 <select name="publisher_id">
6 {% for publisher in publishers %} //单独的添加publisher_id的选择框
7 <option value="{{ publisher.id }}"> {{ publisher.name }}</option>
8 {% endfor %}
9 </select>
10 <input type="submit" value="创建图书">
11 </form>
12 </body>
上面改动完成后,我们在浏览器中访问测试,结果如下
我们看到虽然是能够动态显示publisher_id的列表框了.但是却不能做验证了.对于这个需求根本的解决方案不是这个,这里只是提供一个解决暂时的解决方案.接下来会学习真正的解决方案
验证完数据,当然是把数据添加到后台数据库了.改如何添加呢?
这里有两个知识点:
1.form类提供两个功能: a.输入html. b.用户验证. 所以 form类应该包含html的部分和数据的部分.可以使用form.cleaned_date获得纯数据
2. 通过form.cleaned_date获得的纯数据.是字典的形式如:{'publish_date':datetime.date(2016,6,6),'name':'alex python'}
3. 前面我们把publisher_id提出form类了,所以这里需要单独获取并加到form.cleaned_date里
4. 使用book_obj= models.Book(**form.cleaned_date) ,book_obj.save()直接把前端post来的数据保存到数据库
按照上面的思路,后台保存数据库的视图应改成如下:
1 def book_form(request):2 form = forms.BookForm()
3 publisher_list = models.Publisher.objects.all()
4 if request.method == 'POST':
5 print(request.POST)
6 form = forms.BookForm(request.POST) #将前端输入的数据,传入forms.BookForm类生成实例
7 if form.is_valid():
8 print("form is ok")
9 form_data = form.cleaned_data
10 form_data['publisher_id'] = request.POST.get('publisher_id')
11 book_obj = models.Book(**form_data)
12 book_obj.save
13 else:
14 print(form.errors)
15 return render(request,'app01/book_form.html',{'book_form':form,'publishers':publisher_list}) #这里传给模版html
表单定制
上面我们定义了最简单的django form类表单.要知道每一个字段都是可以定义很多属性.下面是老师给的代码实例,不要求记住,只要求在用的时候能够根据这个实例里代码套用.
1 #!/usr/bin/env python2 # -*- coding:utf-8 -*-
3 import re
4 from django import forms
5 from django.core.exceptions import ValidationError
6
7 #定义一个验证的方法
8 def mobile_validate(value):
9 mobile_re = re.compile(r'^(13[0-9]|15[012356789]|17[678]|18[0-9]|14[57])[0-9]{8}$')
10 if not mobile_re.match(value):
11 raise ValidationError('手机号码格式错误')
12
13
14 class PublishForm(forms.Form):
15 # 定义一个供select标签作为option的元祖
16 user_type_choice = (
17 (0, u'普通用户'),
18 (1, u'高级用户'),
19 )
20 # 引入上面定义的choice方法,widget定义前端页面的一些属性. attrs={'class': "form-control"}指的是此字段转换的html应用这个样式.前提是在你的html模版中有这个样式
21 user_type = forms.IntegerField(widget=forms.widgets.Select(choices=user_type_choice,
22 attrs={'class': "form-control"}))
23 # error_messages属性定义提示语,以及前面条件限制的报警语言
24 # 在widget里'placeholder': u'标题5-20个字符'是前端提示符
25 title = forms.CharField(max_length=20,
26 min_length=5,
27 error_messages={'required': u'标题不能为空',
28 'min_length': u'标题最少为5个字符',
29 'max_length': u'标题最多为20个字符'},
30 widget=forms.TextInput(attrs={'class': "form-control",
31 'placeholder': u'标题5-20个字符'}))
32 # required=False 表示不是必填项.并且我们看能在标签里定义
33 memo = forms.CharField(required=False,
34 max_length=256,
35 widget=forms.widgets.Textarea(attrs={'class': "form-control no-radius", 'placeholder': u'详细描述', 'rows': 3}))
36 # validators=[mobile_validate, ] 这里使用了上面定义的验证手机号的函数,我们看这里是一个列表,所以可以引入多个验证函数
37 phone = forms.CharField(validators=[mobile_validate, ],
38 error_messages={'required': u'手机不能为空'},
39 widget=forms.TextInput(attrs={'class': "form-control",
40 'placeholder': u'手机号码'}))
41 # error_messages里设置当格式错误时提示的信息.
42 email = forms.EmailField(required=False,
43 error_messages={'required': u'邮箱不能为空','invalid': u'邮箱格式错误'},
44 widget=forms.TextInput(attrs={'class': "form-control", 'placeholder': u'邮箱'}))
45
46 '''
47 def __init__(self, *args, **kwargs):
48 super(SampleImportForm, self).__init__(*args, **kwargs)
49
50 self.fields['idc'].widget.choices = models.IDC.objects.all().order_by('id').values_list('id','display')
51 self.fields['business_unit'].widget.choices = models.BusinessUnit.objects.all().order_by('id').values_list('id','name')
52
53 Forms
54 '''
先写好一个form
1 def test_form_view(request):2 if request.method == 'POST':
3 request_form = PublishForm(request.POST)
4 if request_form.is_valid():
5 request_dict = request_form.clean()
6 print(request_dict)
7 return render(request,'test.html', {'pub_form':request_form})
8 else:
9 pub_form = PublishForm()
10 return render(request,'test.html',{'pub_form':pub_form})
写好视图
1 <div>2 <form method="post" action="{% url 'test_form' %}">{% csrf_token %}
3
4 <div>{{ pub_form.user_type }} {{ pub_form.errors.title }}</div>
5 <div>{{ pub_form.title }}</div>
6 <div>{{ pub_form.email }}</div>
7 <div>{{ pub_form.phone }}</div>
8 <div>{{ pub_form.memo }}</div>
9
10
11 {% if pub_form.errors %}
12 {{ pub_form.errors }}
13 {% endif %}
14 <input type="submit" value="提交">
15 </form>
16
17 </div>
模版文件
接下来我们来解决上面对于Django form表单中对于外键需要动态显示选择框的需求.
解决方案:不在使用forms.Form作为基类,而是使用forms.ModelForm作为基类.
PS:forms.ModelForm模块有很多功能是forms.Form所不能实现的.比如我们现在只是通过form类写入到数据库.能不能对之前的记录进行查看,然后在修改呢.
这就需要使用forms.ModelForm模块来实现了.
首先我们用这个模块实现一个新的URL
1.编辑app01/urls.py
1 from django.conf.urls import url2 from app01 import views
3 urlpatterns = [
4 url(r'^$', views.index), # 因为这里已经是二级匹配了,所以前面的url路径就不需要了.$匹配的根目录比如http://127.0.0.1/payment/
5 url(r'^book_modelform/$',views.book_modelform),
6 ]
2.1 编辑app01/views.py
1 def book_modelform(request):2 if request.method == 'POST':
3 pass
4 return render(request,'app01/book_modelform.html')
2.2 编辑templates/app01/book_modelform.py
1 <body>2 <h2>book_modelform_page</h2>
3 </body>
3.编辑app01/forms.py文件
1 class BookModelForm(forms.ModelForm):2 # 此字段和models.py里的字段一一对应
3 class Meta:
4 model = models.Book # 指定要对照的ORM类
5 # fields = ('title','authors','publisher','publication_date') # 指定关联哪些字段
6 exclude = () # 也可以指定不关联哪些字段.
7
8 widgets = {
9 'title':forms.TextInput(attrs={'class':'form-control'}),
10 }
11 # 之前在字段里定义的属性,这里就可以单独拿出来定义了
12 error_messages = {
13 'title':{'required': u'书名不能为空','invalid': u'书名书写错误'},
14 }
4.修改app01/views.py文件和templates/app01/book_modelform.html文件,引入新创建的modelform类
app01/views.py
1 def book_modelform(request):2 modelform = forms.BookModelForm()
3 if request.method == 'POST':
4 pass
5 return render(request,'app01/book_modelform.html',{'book_modelform':modelform,})
templates/app01/book_modelform.html
1 <body>2 <form action="/payment/book_modelform/" method="post"> {% csrf_token %}
3 {{book_modelform}}
4 <input type="submit" value="创建书本">
5 </form>
6 </body>
访问http://127.0.0.1:8000/payment/book_modelform/,结果如下图
5.修改views.py文件进行验证
1 def book_modelform(request):2 modelform = forms.BookModelForm()
3 if request.method == 'POST':
4 print(request.POST)
5 modelform = forms.BookModelForm(request.POST) #将前端输入的数据,传入forms.BookModelForm类生成实例
6 if modelform.is_valid(): # 调用验证方法
7 print("modelform is ok")
8 else:
9 print(modelform.errors)
10 return render(request,'app01/book_modelform.html',{'book_modelform':modelform,})
验证效果图片:
6.然后就是保存提交的内容到数据库,这时候我们的保存就不需要像forms.Form那样先使用form.cleaned_data,然后添加,最后保存了.
这里就可以直接保存了.modelform.save()即可
1 def book_modelform(request):2 modelform = forms.BookModelForm()
3 if request.method == 'POST':
4 print(request.POST)
5 modelform = forms.BookModelForm(request.POST) #将前端输入的数据,传入forms.BookModelForm类生成实例
6 if modelform.is_valid(): # 调用验证方法
7 print("modelform is ok")
8 modelform.save()
9 else:
10 print(modelform.errors)
11 return render(request,'app01/book_modelform.html',{'book_modelform':modelform,})
前端页面就可以填数据进行提交了.没报错,就一定保存到数据库了,进入数据库查看是否有提交的数据或者通过admin后台查询.
7.我们看验证效果图,发现这些input标签的排版很乱.原因是modelform转换程前端的代码不是块级标签,如下:
1 <body>2 <form action="/payment/book_modelform/" method="post"> <input type="hidden" name="csrfmiddlewaretoken" value="f9CRr2eHNzU3U43KJUV7D1YTHnhxhLyo">
3 <label for="id_title">Title:</label>
4 <input class="form-control" id="id_title" maxlength="100" name="title" type="text">
5 <label for="id_authors">Authors:</label>
6 <select multiple="multiple" id="id_authors" name="authors">
7 <option value="1"><alex li></option>
8 <option value="2"><nanhai old></option>
9 </select>
10 <label for="id_publisher">Publisher:</label>
11 <select id="id_publisher" name="publisher">
12 <option value="" selected="selected">---------</option>
13 <option value="1"><北大出版社></option>
14 <option value="2"><上海出版社></option>
15 </select>
16 <label for="id_publication_date">Publication date:</label>
17 <input id="id_publication_date" name="publication_date" type="text">
18 <input type="submit" value="创建书本">
19 </form>
20 </body>
想让这些标签排版好看,那我们就要在html模版文件中加上div标签,自己来写.
1 <body>2 <form action="/payment/book_modelform/" method="post"> {% csrf_token %}
3 <div class="form-confirm">
4 {% for ele in book_modelform %}
5 <div class="form-ele">{{ele.name}}:{{ele}} {{ele.errors}}</div>
6 #{{ele.name}}显示的名称
7 # 查出来后错误信息不会默认返回,需要后面加上{{ele.errors}},这样在出错的时候就可以返回错误提示了
8 {% endfor %}
9 </div>
10 <input type="submit" value="创建书本">
11 </form>
12 </body>
以上是 python2.0_day18_django_form 的全部内容, 来源链接: utcz.com/z/388974.html