HTML表单是网站交互的经典方式。流程:从html把数据提交到服务端,服务端接收数据后判断数据,然后做出对应的响应。
HTTP请求
HTTP协议以“请求—回复”的方式工作,客户发送请求时,可以在请求中附加数据,服务器通过解析请求,就可获得客户传来的数据,并根据URL来提供特定的服务。
form表单
表单在html中由<form>标签实现,一个完整的表单包括4部分:提交地址、请求方式、元素控件、提交按钮。
- action:提交地址,设置用户提交的数据由哪个url接收和处理。
- method:请求方式,get或post请求。
- input:元素控件,输入文本信息。
- submit:提交按钮,触发提交动作。
1. Get请求
HelloWorld/helloApp/templates/demo.html
|
HelloWorld/helloApp/views.py
也可写在任意文件,urls.py引入就行 |
HelloWorld/HelloWorld/urls.py
|
浏览器打开http://127.0.0.1:8000/demo
获取提交参数(request.GET):
html上的数据提交后,希望获取提交的数据,再对数据做处理,不同数据返回不同结果。
HelloWorld/helloApp/views.py
from django.shortcuts import render
from django.http import HttpResponse
def demo_qq(request):
return render(request,'demo.html')
def result_qq(request):
if request.method == 'GET':
r = request.GET['q'] # key就是输入框里的name属性对应值name='q'
res = ''
try:
if int(r) %2: # %操作符返回除法的余数,0为false,1为true
res = '大吉大利'
else:
res = '恭喜发财'
except:
res = '请输入正确的QQ号'
return HttpResponse("测试结果:%s" %res)
else:
# render(request,'demo.html')
return HttpResponse('请求方式不匹配!')
request.GET可以看成一个字典,用GET方法传递的值都会保存到其中,可以用request.GET[‘key_name’]来取值,但当key值不存在时会报错”MultiValueDictKeyError”。
为了规避key值不存在报错问题,可用另一种写法:request.GET.get(‘key_name’,None)
r = request.GET.get('q',None)
查询数据库返回:
通常查询涉及到查询数据库,与数据库会有数据交互,接着前面的Person库,通过输入用户名,查询对应的age:
HelloWorld/helloApp/templates/demo.html
|
HelloWorld/helloApp/views.py 也可写在任意文件,urls.py引入
|
HelloWorld/HelloWorld/urls.py
|
显示效果如下:
2. POST请求-注册
注册页面:需输入用户名(必填)、密码(必填)、邮箱(非必填),点击注册按钮。注册成功后,跳转到登录页面(逻辑先不写)。
HelloWorld/helloApp/templates/register.html
|
HelloWorld/helloApp/templates/register.html
|
POST请求form下面加个{% csrf_token %}
防止跨站点伪造请求。
视图与urls:
HelloWorld/helloApp/views.py 也可写在任意文件,urls.py引入就行
from django.shortcuts import render
from helloApp.models import User
def register(request):
res = ""
if request.method == "POST":
inName = request.POST.get('username')
inPwd = request.POST.get('password')
inMail = request.POST.get('email')
user_list = User.objects.filter(name=inName)
if user_list:
res = "%s用户已被注册!" % inName
return render(request,'register.html',{'rename':res})
else:
# 插入数据第一种写法 --推荐
user = User()
user.name = inName
user.pwd = make_password(inPwd) # 加密存储
user.mail = inMail
user.save()
# 插入数据第二种写法
user = User(name=inName,pwd=inPwd,mail=inMail)
user.save()
return render(request,'login.html',{'rename':res})
else:
return render(request,'register.html')
def login(request):
return render(request,'login.html')
HelloWorld/HelloWorld/urls.py
from django.conf.urls import url
from helloApp import views as view #引入文件名同名,可重命名
urlpatterns = [
url(r'^register/',view.register),
url(r'^login/',view.login),
]
csrf_token
CSRF:Cross Site Request Forgery,跨站点伪造请求。
例:某个恶意网站上有一个指向你的网站的链接,如果某个用户已经登录到你的网站了,则当这个用户点击这个恶意网站上那个链接时,就会向你的网站发来一个请求,你的网站会以为那个请求是用户发来的。
- django第一次响应来自某个客户端的请求时,会在服务端随机生成一个token,把这个token放在cookie里,然后每次POST请求都会带上这个token,这样就能避免被CSRF攻击。
- 在返回的HTTP响应的cookie里,django会为你添加一个csrftoken字段,其值为一个自动生成的token。
- 在所有的POST表单里,必须包含一个csrfmiddlewaretoken字段(只需在模板里添加一个tag,django就会自动帮你生成)。
- 在处理POST请求之前,django会验证这个请求的cookie里的csrftoken字段的值和提交的表单里的csrfmiddlewaretoken字段的值是否一样,如果一样则表明这是个合法请求,否则这个请求可能来自别人的csrf攻击,返回403 Forbidden。
- 在所有的ajax POST请求里,添加一个X-CSRFTOKEN header,其值为cookie里的csrftoken的值。
3. POST请求-登录
注册和登录都是post请求接口,注册是插入数据,登录是查询数据。
HelloWorld/helloApp/templates/login.html
|
HelloWorld/helloApp/views.py
|
E:/PyCharm/HelloWorld/HelloWorld/urls.py
from django.conf.urls import url
from helloApp import views as view #引入文件名同名,可重命名
urlpatterns = [
url(r'^register$',view.register),
url(r'^login$',view.login),
]
密码加密和解密:
密码明文存储到数据库不安全,一般会加密,django提供一套加密和解密的方法。需先导入。加密用 make_password()
解密用 check_password()
from django.contrib.auth.hashers import make_password,check_password
注册时,views.py里写入数据库时,加个make_password()方法转下就行
user = User()
user.name = inName
user.pwd = make_password(inPwd) # 加密存储
user.mail = inMail
user.save()
注意:密码加密的话,数据库里密码字段max_length就不能太小了,否则报错。
登录时,校验密码,先获取输入密码,再读取数据库用户对应密码,再通过check_password()函数校验密码,密码一致返回True,密码不一致返回False
def login(request):
if request.method == "GET":
return render(request,'login.html')
if request.method == "POST":
inUsername = request.POST.get('username')
inPassword = request.POST.get('password')
user_obj = User.objects.filter(name=inUsername).first()
if user_obj:
is_pwd_true = check_password(inPassword,user_obj.pwd)
if is_pwd_true:
return HttpResponse('登录成功!')
else:
return HttpResponse("用户名或密码错误!")
else:
return HttpResponse("用户名或密码错误!")
4. POST请求-修改密码
E:/PyCharm/HelloWorld/helloApp/templates/reset_pwd.html
E:/PyCharm/HelloWorld/HelloWorld/urls.py
|
E:/PyCharm/HelloWorld/helloApp/views.py
|
5. 视图
一个视图函数,简称视图。它接受web请求并返回web响应。响应可以是HTML页面、一个404错误页面、重定向页面、XML文档、或一张图片……
无论视图本身包含什么逻辑,都要返回响应。代码写在哪都可以,一般放在views.py文件中。
视图层中有2个重要对象:请求对象(request)与响应对象(HttpResponse)
5.1 请求对象:HttpRequest对象(简称request对象)
常用的request属性:GET、POST、body、path、method。
GET
数据类型是QueryDict,一个类似于字典的对象,包含HTTP GET的所有参数。有相同的键,就把所有的值放到对应的列表里。
取值格式:对象.方法 如:request.GET.get()
get():返回字符串,如果该键对应有多个值,取出该键的最后一个值。
def runoob(request):
name = request.GET.get("name")
return HttpResponse('姓名:{}'.format(name))
POST
数据类型是QueryDict,一个类似于字典的对象,包含HTTP POST是多有参数。常用于form表单,form表单里的标签name属性对应参数的键,value属性对应参数的值。
取值格式:对象.方法 如:request.POST.get()
method
获取当前请求的方式,数据类型是字符串,且结果为大写。request.method
def login(request):
if request.method == "GET":
return render(request,'login.html')
if request.method == "POST":
inUsername = request.POST.get('username')
inPassword = request.POST.get('password')
user_obj = User.objects.filter(name=inUsername).first()
if user_obj:
is_pwd_true = check_password(inPassword,user_obj.pwd)
if is_pwd_true:
return HttpResponse('登录成功!')
else:
return HttpResponse("用户名或密码错误!")
else:
return HttpResponse("用户名或密码错误!")
body
数据类型是二进制字节流,是原生请求体里的参数内容,在HTTP中用于POST,因为GET没有请求体。如登录页面改成这样打印body内容。request.body
def login(request):
body = request.body
return HttpResponse(body)
登录后页面展示为:csrfmiddlewaretoken=WWPyT5oWUTVDUUMyuxTuJVK7kXTwWxPJZcT0K2nXqN3vZDcEsR2AVOL0GRdgDtVM&username=aaa&password=aaa
path
获取URL中的路径部分,数据类型是字符串。request.path
|
5.2 响应对象:HttpResponse对象
响应对象主要有3种形式:HttpResponse()、render()、redirect()
HttpResponse():返回文本,参数为字符串,字符串中写文本内容。如果参数为字符串里含html标签,也可以渲染。
|
render():返回文本,第1个参数是request,第2个参数是字符串(页面名称),第3个参数是字典(可选参数,向页面传递的参数,键为页面参数名,值为views参数名)
def runoob(request):
name ="无敌大宝剑" # 可是字符串、字典、数组等
return render(request,"runoob.html",{"name":name})
redirect():重定向,跳转新页面。参数为字符串,字符串中填写页面页面路径。一般用于form表单提交后跳转至新页面。
def runoob(request):
return redirect("/index/")
render和redirect在HttpResponse的基础上进行了封装:
- render:底层返回的也是 HttpResponse 对象
- redirect:底层继承的是 HttpResponse 对象
6. Request对象 详解
每个视图函数的第一个参数是一个HttpRequest对象,
from django.http import HttpResponse
def runoob(request):
return HttpResponse("Hello world")
HttpRequest对象包含当前请求URL的一些信息:
属性 | 描述:request.属性 |
path | 请求页面的全路径,不包括域名。如”/hello/” |
method |
请求中使用HTTP方法的字符串表示,全大写表示。如: If request.method == ‘GET’: do_something() elif request.method == ‘POST’: do_something_else() |
GET | 包含所有HTTP GET参数的类字典对象 |
POST |
包含所有HTTP POST参数的类字典对象。服务器收到空的POST请求的情况也有可能,即表单form通过HTTP POST方法提交请求,但表单中可以没有数据。 因此,不能用if request.POST来判断是否使用HTTP POST方法,应使用if request.method == ‘POST’ 注:POST不包括file-upload信息。 |
REQUEST(建议不用) |
建议用GET和POST。为了方便,该属性是POST和GET属性的集合体,但有特殊性,先查找POST属性,再查找GET属性。例如:如果GET={“name”:”john”}和POST={”age”:34},则REQUEST[“name”]的值是”john”,REQUEST[“age”]的值是34。 |
COOKIES | 包含cookies的标准Python字典对象。Keys和values都是字符串。 |
FILES |
包含所有上传文件的类字典对象。 FILES中每个Key都是<input type=”file” name=”” /> 标签中name属性的值, FILES中每个value同时也是一个标准的Python字典对象,包含下面3个Keys:
只有请求方式为POST,且请求页面中<form>有enctype=”multipart/form-data”属性时FILES才有数据,否则,FILES是一个空字典。 |
META |
包含所有可用HTTP头部信息的字典,例如:
META中这些头加上前缀HTTP_为Key,冒号后面为Value,例如:
|
user | 是一个django.contrib.auth.modules.User对象,代表当前登录的用户。
若访问用户未登录,user将被初始化为django.contrib.auth.modules.AnonymousUser的实例。可通过user的is_authenticated()方法辨别用户是否登录:
只有激活Django中的AuthenticationMiddleware时属性才可用。 |
session | 唯一可读写的属性,代表当前会话的字典对象,只有激活Django中的session支持时该属性才可用。 |
raw_post_data | 原始HTTP POST数据,未解析过。高级处理时会有用处 |
Request对象也有一些有用的方法:
方法 | 描述 |
__getitem__(key) |
返回GET/POST的键值,先取POST后取GET。如果键值不存在抛出KeyError。这时我们可以使用字典语法访问HttpRequest对象。 如:request[“foo”]等同于先request.POST[“foo”]然后request.GET[“foo”] |
has_key() | 检查request.GET or request.POST中是否包含参数指定的Key |
get_full_path() | 返回包含查询字符串的请求路径。”/music/bands/the_beatles/?print=true” |
is_secure() | 如果请求是安全的,返回True。就是说发出的是https请求。 |
QueryDict对象
在HttpRequest对象中,GET和POST属性是django.http.QueryDict类的实例。
QueryDict类似字典的自定义类,用来处理单键对应多值的情况。QueryDict实现所有标准的字典方法,还包括一些特有的方法:
方法 | 描述 |
__getitem__ | 和标准字典不同的是:如果Key有多个Value,__getitem__()返回最后一个 |
__setitem__ | 设置参数指定key和value列表(一个Python list)。它只能在一个mutable QueryDict对象上被调用(就是通过copy()产生一个QueryDict对象的拷贝)。 |
get() | 如果key对应多个value,get()返回最后一个value。 |
update() |
参数可以是QueryDict,也可以是标准字典。和标准字典的update()方法不同,该方法添加字典items,而不是替换它们: >>> q = QueryDict(‘a=1’) >>> q = q.copy() # to make it mutable >>> q.update({‘a’: ‘2’}) >>> q.getlist(‘a’) [‘1’, ‘2’] >>> q[‘a’] # returns the last [‘2’] |
items() |
和标准字典的items()方法有点不同,该方法使用单值逻辑的__getitem__() >>> q = QueryDict(‘a=1&a=2&a=3’) >>> q.items() [(‘a’, ‘3’)] |
values() | 和标准字典的values()方法有点不同,该方法使用单值逻辑的__getitem__() |
此外,QueryDict也有一些方法,如下:
方法 | 描述 |
copy() | 返回对象的拷贝,内部实现是python标准库的copy.deepcopy()。该拷贝是mutable(可更改的)–即是说,可以更改该拷贝的值。 |
getlist(key) | 返回和参数key对应的所有值,作为一个python list返回。如果key不存在,则返回空list。 It’s guaranteed to return a list of some sort.. |
setlist(key,list) | 设置key的值为list_(unlike __setitem__()) |
appendlist(key,item) | 添加item到和key关联的内部list |
setlistdefault(key,list) | 和setdefault有一点不同,它接受list而不是单个value作为参数。 |
lists() |
和items()有一点不同,它会返回key的所有值,作为一个list。如: >>> q = QueryDict(‘a=1&a=2&a=3’) >>> q.lists() [(‘a’, [‘1’, ‘2’, ‘3’])] |
urlencode() | 返回一个以查询字符串格式进行格式化后的字符串。如:”a=2&b=3&b=5” |
原创文章,作者:ItWorker,如若转载,请注明出处:https://blog.ytso.com/279805.html