HTML 表单是交互式网站的基本组成部分,用户提交信息、搜索内容、与后台数据交互都要用到表单。
1、从请求对象中获取数据
view视图函数的第一个参数都是 request,这个request就是请求获得的HttpRequest对象。里面包含中有一些关于当前所请求 URL 的信息,如下图
属性/方法 | 说明 | 示例 |
---|---|---|
request.path | 完整的路径,不含域名,但是包 含前导斜线 | “/hello/” |
request.get_host() | 主机名(即通常所说的“域名”) | “127.0.0.1:8000”或www.newweb.com |
request.get_full_path() | 包含查询字符串(如果有的话) 的路径 | “/hello/?print=true” |
request.is_secure() | 通过 HTTPS 访问时为 True,否 则为 False | True 或 False |
2、关于请求的其他信息
request.META 的值是一个 Python 字典,包含请求的所有 HTTP 首部,例如用户的 IP 地址和用户代理(user agent,通常是 Web 浏览器的名称和版本)。
注意,具体包含哪些首部取决于用户发送了什么首部,以及 Web 服务器返回了什么首部。
这个字典中常见的几个键有:
• HTTP_REFERER:入站前的 URL(可能没有)。(注意,要使用错误的拼写,即 REFERER。)
• HTTP_USER_AGENT:浏览器的用户代理(可能没有)。例如:"Mozilla/5.0 (X11; U; Linux i686; frFR; rv:1.8.1.17) Gecko/20080829 Firefox/2.0.0.17"。
• REMOTE_ADDR:客户端的 IP 地址,例如 "12.345.67.89"。(如果请求经由代理,这个首部的值可能是一 组 IP 地址,以逗号分隔,例如 "12.345.67.89,23.456.78.90"。)
注意,因为 request.META 是个普通的 Python 字典,所以尝试访问不存在的键时,抛出 KeyError 异常。 (HTTP 首部是外部数据,即由用户的浏览器提交,因此不能完全相信,当某个首部为空或不存在时,应该让应用程序优雅失败。)为了处理未定义的键,应该使用 try/except 子句,或者 get() 方法:
# 用try语句
def ua_display_good1(request):
try:
useragent = request.META['HTTP_USER_AGENT']
except KeyError:
useragent = 'unknown'
return HttpResponse("Your browser is %s" % useragent)
# 用get方法
def ua_display_good2(request):
useragent = request.META.get('HTTP_USER_AGENT', 'unknown')
return HttpResponse("Your browser is %s" % useragent)
也可以编写一个简单的视图,显示 request.META 中的所有信息,以便查阅。
def display_meta(request):
values = request.META.items()
values
html = []
for k, v in values:
html.append('<tr><td>%s</td><td>%s</td></tr>' % (k, v))
return HttpResponse('<table>%s</table>' % '\n'.join(html))
3、用表单实现查询功能
除了关于请求的基本元数据之外,HttpRequest 对象还有两个属性包含用户提交的信息:request.GET 和 request.POST。这两个属性的值都是类似字典的对象,分别用于获取 GET 和 POST 数据。POST 数据一般由 HTML 表单提交,而 GET 数据既可以来自表单,也可以来自页面 URL 中的查询字符串。
我们利用request.GET 和 request.POST就可以实现前后端之间的通信,让用户提交的信息传递给服务器,服务器进行响应。
我们前面的案例中说到可以用url传输信息给后端,但那不是个好办法,现在我们改用表单传输数据,实现查询功能。我们先在之前的 'notice/' url 对应的 class3.html 增加一个查询表单(纯属偷懒,表单应该放到页面合适的地方去)
<form action="/search/" method="get">
<h3>根据姓名查找同学</h3>
<input type="text" name="chaxun">
<input type="submit" value="查询">
</form>
这里 method = ‘get’表示请求的方法为 get
<h3>这一行是标题,告诉用户这个表单是干嘛的,应该填什么数据,可要可不要
第一个input 的 name='chaxun' 这是后面我们找数据是要用到的标签名称
第二个input 的 type = 'submit' 表示这是一个提交按钮,value='查询' 表示它在页面上显示为“查询”
看一下页面:
然后我们来写个视图函数 chaxun1
def chaxun1(request):
studentName = request.GET['chaxun']
s = Student.objects.get(name=studentName)
return HttpResponse('你要查询的{}同学信息如下:姓名:{},年龄{},性别{},成绩{},武力值{}'.format(s.name,s.name,s.age,s.sex,s.score,s.skill))
然后我们在上面的文本框中输入 “黄蓉”,点击查询按钮,得到如下结果:
4、改进表单
上面这个表单虽然实现了我们预期的功能,但是还不完善。比如如果用户提交的是个空表单怎么办,如果用户提交的信息数据库里查不到怎么办,我们不能总是报错啊,现在我们来着手改进这个表单:
if request.GET and request.GET['chaxun']: #确保GET方法获得了值,并且'chaxun'标签(就是那个提交查询内容的输入框)不为空
studentName = request.GET['chaxun']
try: #确保要查询的数据存在
s = Student.objects.get(name=studentName)
result = '你要查询的{}同学信息如下:姓名:{},年龄{},性别{},成绩{},武力值{}'.format(s.name,s.name,s.age,s.sex,s.score,s.skill)
context['result']=result
except:
context['result']='查无此人' #如果查不到,显示“查无此人”并返回同一个页面
return render(request,'class3.html',context=context,status=200)
else: #如果输入为空,曾刷新页面,重新填写
return render(request, 'class3.html')
这样改造后,我们再查询页面,就可以直接看到结果了,输入空值则会返回当前页面继续填写
5、Django的表单类
用户在填写表单的时候,谁也不知道用户会写些什么,为了数据能够按规范填写,就需要对用户的填写行为进行约束,也就是表单的验证。我们当然可以自己手动去写这些验证方法,不过Django已经贴心地为我们准备好了内置的表单库,django.forms,它能处理从显示 HTML 表单到验证的大多数问题,即开即用。
我们用一个例子来演示一下这个表单类怎么用。比如上面那个查询功能,我们现在用forms表单类把它集成实现。
首先新建一个forms.py文件(当然也可以直接写在views.py,不过一般Django风格都是给forms单独一个文件)我们定义一下我们需要的字段。这里的字段其实是显示在前端给用户看的,所以想要让用户输入什么字段,就在这里定义,比如我们这里打算让用户可以通过 姓名、年龄 和 成绩这三个条件来查询:
from django import forms #注意这里要从django引入forms库
class chaxunForm(forms.Form): #定义一个查询类
#这里label就是前端显示的输入框的提示文字,max_length是输入的最大长度,
#required=False是允许这个值为空,因为我们期望的是通过每个条件单独都可以查询
name = forms.CharField(label="名字",max_length=10,required=False)
#这里可以定义输入数值的大小,用max_value 和 min_value 参数来设定上下限
age = forms.IntegerField(label="年龄",required=False,max_value=100,min_value=10)
score = forms.IntegerField(label="成绩",required=False)
为了方便演示,我们另外写了个HTML页面文件 chaxun.html,反正用模板继承很方便
{% extends "base.html" %}
{% block title %}武侠三班的小窝{% endblock %}
{% block main %}
<h1>查询页面,在这里可以查找你想了解的同学</h1>
<form action="" method="post">{% csrf_token %}
<h3>根据条件查找同学</h3>
{{form}}
<input type="submit" value="查询">
</form>
<P>
{% for result in results %}
<div>{{result}}</div>
{% endfor %}
</P>
{% endblock %}
注意这里页面上的输入框不用我们自己手动写了,form类会自动把自定义的字段转成input输入框 然后我们去views.py调用这个类,
from classManage.forms import chaxunForm
........
#其他代码
........
def chaxun4(request):
context={}
form=chaxunForm(request.POST)
if request.method == 'POST': #这里要注意确认一下方法为POST
form = chaxunForm(request.POST) #实例化一个我们在forms.py里面定义的chaxunForm类
context['form']=form #把form实例赋值给字典的 'form',方便前端调用
results = []
if form.is_valid(): #确保form的数据有效,这个验证工作forms类帮我们做了
stuName = form.cleaned_data.get('name') #cleaned_data是经过清洗的数据
stuAge = form.cleaned_data.get('age')
print(stuAge)
if stuName:
#名字一般是唯一的,但是为了上下统一用一个函数比较方便,我们统统用filter,而不是get,然后把数据放到一个列表中去遍历
stus=Student.objects.filter(name=stuName)
elif stuAge:
stus=Student.objects.filter(age=stuAge)
elif stuScore:
stus=Student.objects.filter(score=stuScore)
else:
pass
for s in stus:
result = '你要查询的{}同学信息如下:姓名:{},年龄{},性别{},成绩{},武力值{}'.format(s.name,s.name,s.age,s.sex,s.score,s.skill)
results.append(result)
print('postresult:',results)
context['results']=results
return render(request,'class3.html',context=context,status=200)
else:
form = chaxunForm()
context['form']=form #把form对象整个赋值给字典的 'form'方便前端调用
return render(request,'chaxun.html',context=context,status=200)
我们去访问页面 http://127.0.0.1:8000/cha/ 看看效果
可以看到出现了 名字、年龄、成绩 三个查询框,我们任意填其中一个,实现查询功能
我们测试一下forms的验证效果,我们在年龄框里面输入汉字,发现根本不给输入,然后我们在年龄这里输入数字5,点击查询后发现它提示我数字必须大于10:
当然这里这个提示不太美观,可以自定义来定制验证信息。总之,forms类可以帮我们进行数据审查,比如如果我们定义了一个forms类的email字段,它会自动审查输入是否为一个email格式,这真的大大简化了我们的工作啊。
所以我们一般做登录、注册表单或者信息提交表单的时候,用这个forms类真的很方便。