HTML表单是网站交互性的经典方式,本章将记录如何用Django对用户提交的表单数据进行处理。
HTTP 请求
HTTP协议以"请求-回复"的方式工作。客户发送请求时,可以在请求中附加数据。服务器通过解析请求,就可以获得客户传来的数据,并根据URL来提供特定的服务。
-
一个请求的大概流程:
通过TCP三次握手进行连接,然后开始传数据,客户端发送请求给服务器,服务器作出相应的相应返回给客户端
表单
表单是HTML中用于提交数据给服务器的一个标签,所有的表单元素(input/textarea/button...)都要放在form标签当中.然后有以下参数:
-
form
的method
参数用于设置表单的提交方式,默认使用GET
. -
action
用于设置表单的提交url,如果不写或者保持空字符串,那么将使用当前的URL. -
action
尽量指定一个url,因为这块会有浏览器兼容问题,有的浏览器填空是不能获取到对应的action
请求类型
- GET: 请求指定的页面信息,并返回实体主体。它的参数在url中
- HEAD: 只请求页面的首部。它的参数在body中
- POST: 请求服务器接受所指定的文档作为对所标识的URI的新的从属实体。
- PUT: 从客户端向服务器传送的数据取代指定的文档的内容。
- DELETE: 请求服务器删除指定的页面。
- OPTIONS: 允许客户端查看服务器的性能。
- TRACE: 请求服务器在响应中的实体主体部分返回所得到的内容。
- PATCH: 实体中包含一个表,表中说明与该URI所表示的原内容的区别。
- MOVE: 请求服务器将指定的页面移至另一个网络地址。
- COPY: 请求服务器将指定的页面拷贝至另一个网络地址。
- LINK: 请求服务器建立链接关系。
- UNLINK: 断开链接关系。
- WRAPPED: 允许客户端发送经过封装的请求。
- Extension-mothed:在不改动协议的前提下,可增加另外的方法。
一般常用GET与POST类型
HttpRequest
- request.scheme:网络请求的协议,一般是http和https* request.body:http请求中传递的原始数据,是一个byte类型的string,如果要处理form表单上传上来的数据,应该使用request.POST,这个处理得比较好。
- request.path:当前请求的URL路径,但是不包括scheme和域名,比如有http://www.myblog.com/blog/1。那么返回的将是/blog/1。会去除掉http和www.myblog.com
- request.method:当前这个请求访问方法,一般为GET和POST,例如可以通过以下方式进行判断:
- request.GET:一个QueryDict的类字典类型的对象,可以通过访问字典的方式进行访问里面的值,里面的值是通过GET请求传递进来的参数。
- request.POST:同request.GET,但是是保存了通过POST请求传递上来的参数。注意,这个POST里面不包含上传的文件信息。
- request.FILES : 如果要访问上传的文件,请使用request.FILES。
- request.COOKIES:一个字典类型,包含了所有的cookie信息,key和value都是字符串。
- request.session:返回一个QueryDict的类字典类型的集合,这个属性要有效,必须添加SessionMiddleware这个中间件。
- request.META:存储所有请求的头部信息。
- request.is_secure():如果是HTTPS的,那么返回True,否则返回False
- request.is_ajax():这个请求是否是通过XMLHttpRequest进行访问的。
- 他的实现原理就是去request.META中查找HTTP_X_REQUESTED_WITH,如果是XMLHttpRequest,那么将返回True,否则返回False。**一般在第三方库如jQuery中使用ajax方法都设置了这个值,如果自己手动写XMLHttpRequest,那么需要自己添加这个header,is_ajax这个方法才能生效。
HttpResponse
HttpResponse.init(content=’’,content_type=None,status=200,reason=None,charset=None)构造方法:
content:代表要发送给客户端的内容。
content_type:代表发送给客户端的内容类型,默认是“text/html;charset=utf-8”。
charset:响应体的编码,如果没有指定,则会使用content_type中设置的charset。
set_cookie(key,value=’’,max_age=None,expires=None,path=’/’,domain=None,secure=None,httponly=False)
key:cookie的key。
value:cookie的value。
max_age:cookie最大的过期时间。
expires:cookie的过期时间和日期。
path:针对哪个目录下有效果。
domain:域名,如果你想跨域使用cookie,必须设置这个值,比如domain=”.lawrence.com”,那么www.lawrence.com和blogs.lawrence.com和calendars.lawrence.com都可以访问到这个cookie。
httponly:如果设置为True,客户端的js代码将不能访问到cookie。
delete_cookie(key,path=’/’,domain=None):删除cookie。
response子类
BadHeaderError
FileResponse
Http404
HttpResponse
HttpResponseBadRequest
HttpResponseForbidden
HttpResponseGone,
HttpResponseNotAllowed
HttpResponseNotFound
HttpResponseNotModified,
HttpResponsePermanentRedirect
HttpResponseRedirect,
HttpResponseServerError
JsonResponse
StreamingHttpResponse
其中将get,post,cookie,session简单介绍下。
get方法
用于接收用户的请求,调用的是View类中的get方法,其中传的数据在地址栏是可见的。
post方法
常用于和表单form一起用,数据是不可见的,当提交表单数据之后,会调用View类中的post方法。
cookie
在程序中,会话跟踪是很重要的事情。理论上,一个用户的所有请求操作都应该属于同一个会话,而另一个用户的所有请求操作则应该属于另一个会话,二者不能混淆。例如,用户A在超市购买的任何商品都应该放在A的购物车内,不论是用户A什么时间购买的,这都是属于同一个会话的,不能放入用户B或用户C的购物车内,这不属于同一个会话。而Web应用程序是使用HTTP协议传输数据的。HTTP协议是无状态的协议。一旦数据交换完毕,客户端与服务器端的连接就会关闭,再次交换数据需要建立新的连接。这就意味着服务器无法从连接上跟踪会话。即用户A购买了一件商品放入购物车内,当再次购买商品时服务器已经无法判断该购买行为是属于用户A的会话还是用户B的会话了。要跟踪该会话,必须引入一种机制。
Cookie就是这样的一种机制。它可以弥补HTTP协议无状态的不足。在Session出现之前,基本上所有的网站都采用Cookie来跟踪会话。
session
Session是另一种记录客户状态的机制,不同的是Cookie保存在客户端浏览器中,而Session保存在服务器上。客户端浏览器访问服务器的时候,服务器把客户端信息以某种形式记录在服务器上。这就是Session。客户端浏览器再次访问时只需要从该Session中查找该客户的状态就可以了。
如果说Cookie机制是通过检查客户身上的“通行证”来确定客户身份的话,那么Session机制就是通过检查服务器上的“客户明细表”来确认客户身份。Session相当于程序在服务器上建立的一份客户档案,客户来访的时候只需要查询客户档案表就可以了。
通过视图中对应的请求函数
class TestRequest(View):
# get方式请求
def get(self, request):
#通过request.GET获取GET数据
request.POST.get("param")
# post方式请求
def post(self, request):
request.POST.get("param")
...其它方式类似
使用session与cookie完成登录页面
1 建立一个用户Model, 完成一个注册
2 crsf_token: 防跨域html标签,这个是django自带的app,一但使用form表单则必须使用
<form>
{% crsf_token %}
</form>
添加一个登录视图
通过session+cookie保存登录状态
# 验证用户名密码
if user:
uuid_str = str(uuid.uuid4())
httpResponse = HttpResponse(u"登录成功")
httpResponse.set_cookie("mysession_id", uuid_str)
request.session[uuid_str] = user.username
return httpResponse
使用md5加密
import hashlib
password = "123123"
hashlib.md5(password).hexdigest()
uuid
以下这个函数会生成一个唯一字符串
import uuid
uuid.uuid4()
课后练习
- 配置对应路由URL
#项目urls.py
url(r'^formtest/', include('formApp.urls')
#app应用urls.py
urlpatterns = [
url(r'^register/$', views.Register.as_view(),name='register'),
url(r'^login/$', views.Login.as_view(),name = 'login'),
url(r'^index/$', views.Index.as_view(),name='index'),
url(r'^logout/$', views.Logout.as_view(),name='logout')
]
- 建立模型类
class User(models.Model):
username = models.CharField(u'用户名',max_length= 20)
password = models.CharField(max_length = 40,verbose_name = u'密码')
class Meta:
db_table = 'user'
managed = True
- 视图实现相关功能
- 导入模块
from __future__ import unicode_literals
from django.shortcuts import render,redirect,reverse
from django.views import View
from django.http import HttpResponse
import hashlib
from .models import *
import uuid
def md5_password(password): #使用md5加密
return hashlib.md5(password).hexdigest()
1 注册功能
# 通过通用视图的话,我们不需要判断请求method方法,执行实现对应method方法即可
class Register(View):
def get(self,request):
# request 其实就是一个HttpRequest,它是django对请求的一个封装
# view视图其实就是返回一个HttpResponse对象, render函数其实就是django方便我们渲染模版请求的一个封装
return render(request,'register.html')
def post(self,request):
# 在post里面也是可以获取get参数,但是get方法不能获取post参数
#param = request.GET.get("param")
# 使用post方法获取前端参数
user = request.POST.get('username')
passwd = request.POST.get('password')
# 密码这里一般不会明文直接存储到数据库,一般会进行加密
passwd = md5_password(passwd)
# 验证
# 比如用户名是否存在,密码是否符合规则等等
if User.objects.filter(username = user):
print(User.objects.filter(username = user))
return HttpResponse('该用户名已被使用,请选择其他账户,谢谢!')
User.objects.create(username = user,password = passwd)
return HttpResponse('注册成功 前往<a href = "/formtest/login">登录</a>')
对应页面代码:
注册页面
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>注册页面</title>
<style>
* {
margin: 0;
padding: 0;
}
</style>
</head>
<body>
<h2>注册界面</h2>
<form action="" method="post">
{% csrf_token %}
用户名:<input type="text" name="username" placeholder="请输入账号"/><br/>
密 码:<input type="password" name="password"/><br/>
<input type="submit" value="注册"/>
</form>
</body>
</html>
注册页面效果:2 登录
class Login(View): #登录模块
def get(self,request):
return render(request,'login.html')
def post(self,request):
loginusername = request.POST.get('username')
password = request.POST.get('password')
loginpassword = md5_password(password)
#filter方法是从数据库的取得匹配的结果,返回一个对象列表,如果记录不存在的话,它会返回[]。
users = User.objects.filter(username = loginusername,password = loginpassword)
if users: #如果有记录
unique_str = str(uuid.uuid4()) #生成唯一的字符串
rep = redirect(reverse('index'))
#每次用户第一次访问服务端, 把用户的唯一字符串 session_id加到cookie里面,发送给客户端;
# 设置cookies,当用户登录成功后,把{sessionID :随机字符串} 组织成键值对,加到cookie里发送给用户
cookies = rep.set_cookie('my_session_id',unique_str)
# 服务端session保存
request.session[unique_str] = loginusername #使用session保存用户名,这里key可以为对应值,当做字典处理而已;
return rep
else:
message = u'用户名或者密码错误'
return render(request,'login.html',locals()) #否则登录失败回到登录界面
class Index(View): #实现登录成功后功能
def get(self,request):
# my_session = request.COOKIES['my_session_id'] #获取客户端请求时带的cookies
my_sessionid = request.COOKIES.get('my_session_id')
if my_sessionid: #如果存在,则与服务器信息对比
username = request.session.get(unique_str) #获取客户端的 session信息
if username:
return render(request,'index.html',locals())
return redirect(reverse('login'))
页面代码:
登录界面
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>login登录页面</title>
<style>
*{
margin: 0;
padding: 0;
}
#topDiv,#bottomDiv{
background: black;
height: 40px;
width: 100%;
color: white;
text-align: center;
line-height: 40px;
}
#bottomDiv{
height: 90px;
line-height: 90px;
position: absolute;
bottom: 0;
}
#menu{
width: 100%;
height: 60px;
color: #666;
}
#menu ul li{
list-style: none;
float: left;
margin: 20px;
}
#loginbox {
width: 400px;
height: 200px;
border: 1px solid red;
margin: 90px auto;
text-align: center;
}
</style>
</head>
<body>
<div id="topDiv">
top顶部信息
</div>
<div id="bottomDiv">
bottom底部资料
</div>
<div id="menu">
<ul>
<li>首页</li>
<li>编程语言</li>
<li>项目实战</li>
<li>每日一学</li>
</ul>
</div>
<div id="loginbox">
<br/>
<h2>登录界面</h2>
<br/>
<form action="" method="post">
{% csrf_token %}
帐 号:<input type="text" name="username">
<br/>
<br/>
密 码:<input type="password" name="password">
<br/>
<br/>
<input type="submit" value="登录">
<input type="button" value="注册">
</form>
</div>
</body>
</html>
首页index.html代码
#首页
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>首页</title>
<style>
*{
margin: 0;
padding: 0;
}
</style>
</head>
<body>
<div style="margin-top:20px;color: black;font-size: 20px;line-height: 20px;text-align: center;";>
欢迎{{username}}登录成功!<a href="/formtest/logout">退出</a><br/>
</div>
</body>
</html>
登录成功效果:3 退出注销功能
class Logout(View): #退出注销功能
def get(self,request):
my_sessionid = request.COOKIES.get('my_session_id') #获取cookie
#判断浏览器访问时是否有session对象
if request.session.has_key(my_sessionid):
del request.session[my_sessionid] #删除对应session,利用删除键值对的方式;
return redirect(reverse('login'))