python2.0_day18_django_form

python

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 forms

2 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 python

2 # -*- 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 url

2 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">&lt;alex li&gt;</option>

8 <option value="2">&lt;nanhai old&gt;</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">&lt;北大出版社&gt;</option>

14 <option value="2">&lt;上海出版社&gt;</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

回到顶部