Django - 请求与响应、表单、中间件、上下文处理器

时间:2024-06-07 23:07:02

请求与响应篇

一、HttpRequest对象

服务器接收到http协议的请求后,会根据报文创建HttpRequest对象。视图函数的第一个参数(request)是HttpRequest对象在django.http模块中定义了HttpRequest对象的API

所谓的API,在django中就是属性、方法。

(一).HttpRequest对象的属性及方法

属性:
path:一个字符串,表示请求的页面的完整路径,不包含域名
method:一个字符串,表示请求使用的HTTP方法,常用值包括:'GET'、'POST'
encoding:一个字符串,表示提交的数据的编码方式
如果为None则表示使用浏览器的默认设置,一般为utf-8
这个属性是可写的,可以通过修改它来修改访问表单数据使用的编码,接下来对属性的任何访问将使用新的encoding值
GET:一个类似于字典的对象,包含get请求方式的所有参数
POST:一个类似于字典的对象,包含post请求方式的所有参数
FILES:一个类似于字典的对象,包含所有的上传文件
COOKIES:一个标准的Python字典,包含所有的cookie,键和值都为字符串
session:一个既可读又可写的类似于字典的对象,表示当前的会话,只有当Django 启用会话的支持时才可用,详细内容见“状态保持” 方法:
is_ajax():如果请求是通过XMLHttpRequest发起的,则返回True

(二).QueryDict对象

request对象的GET、POST属性,都是QueryDict类型的对象。它不是python的字典!QueryDict类型的对象用来处理同一个键带有多个值的情况。

(1).get()

根据键获取值,只能获取键的一个值。如果一个键同时拥有多个值,获取最后一个值

(2).getlist()

根据键获取值,将键的值以列表返回,可以获取一个键的多个值。

(3).例

dict.get('键',default)  # 或简写为 dict['键']
dict.getlist('键',default)

(三).GET属性

(1).QueryDict类型的对象

(2).包含get请求方式的所有参数

(3).与url请求地址中的参数对应,在url的?后面进行拼接

(4).参数的格式是键值对,如key1=value1

(5).多个参数之间,使用&连接,如key1=value1&key2=value2

(四).POST属性

(1).QueryDict类型的对象

(2).包含post请求方式的所有参数

(3).与form表单中的控件对应

(4).表单中控件要有name属性,则name属性的值为键,value属性的值为键,构成键值对提交

(5).对于checkbox控件,name属性一样为一组,当控件被选中后会被提交,存在一键多值的情况

二、HttpResponse对象

(一).属性及方法

属性:
content:表示返回的内容,字符串类型
charset:表示response采用的编码字符集,字符串类型
status_code:响应的HTTP响应状态码 方法:
init:使用页内容实例化HttpResponse对象
write(content):以文件的方式写
flush():以文件的方式输出缓存区
set_cookie(key, value='', max_age=None, expires=None):设置Cookie
key、value都是字符串类型
max_age是一个整数,表示在指定秒数后过期
expires是一个datetime或timedelta对象,会话将在这个指定的日期/时间过期,注意datetime和timedelta值只有在使用PickleSerializer时才可序列化
max_age与expires二选一
如果不指定过期时间,则关闭浏览器就失效.
delete_cookie(key):删除指定的key的Cookie,如果key不存在则什么也不发生

(二).cookie的例子

def cookie_test(request):
response = HttpResponse()
response.set_cookie("a", "") # 设置一个cookie
cookie = response.COOKIES
a = cookie.get("a", None) # 获取cookie
response.write(a)
return response

cookie是以明文的方式保存在客户端的,如果有敏感的信息则不安全。

(三).HttpResponse的子类:JsonResponse

(1).返回json数据

(2).帮助用户创建JSON编码的响应

(3).参数data是字典对象

(4).JsonResponse的默认Content-Type为application/json

(5).例

from django.http import JsonResponse

def jpTest(request):
return JsonResponse({'ss':''})

三、状态保持

(1).http协议是无状态的:每次请求都是一次新的请求,不会记得之前通信的状态

(2).客户端与服务器端的一次通信,就是一次会话实现状态保持的方式:在客户端或服务器端存储与会话有关的数据

(3).存储方式包括cookie、session,会话一般指session对象

(4).使用cookie,所有数据存储在客户端,注意不要存储敏感信息

(5).推荐使用sesison方式,所有数据存储在服务器端,在客户端cookie中存储session_id

(6).状态保持的目的是在一段时间内跟踪请求者的状态,可以实现跨页面访问当前请求者的数据

(7).注意事项:不同的请求者之间不会共享这个数据,与请求者一一对应

四、会话(session)

session是保存在服务端的,使用sessionid对应服务端中的session,sessionid则保存在cookie中。

cookie对应sessionid,sessionid对应服务端的session

(一).启用session

(1).检查settings.py文件

检查settings.py文件是否有下面这些属性

# 没有的话,就添加

# INSTALLED_APPS 列表中:
'django.contrib.sessions', # MIDDLEWARE_CLASSES 列表中:
'django.contrib.sessions.middleware.SessionMiddleware',

(二).使用session

(1).启用会话后,每个HttpRequest对象将具有一个session属性,它是一个类字典对象

(2).get(key, default=None):根据键获取会话的值

(3).clear():清除所有会话

(4).flush():删除当前的会话数据并删除会话的Cookie

(5).del request.session['member_id']:删除会话

(三).session保持用户登录的例子

(1).视图函数

from django.shortcuts import render, HttpResponse, redirect, reverse

# Create your views here.

def session_index(request):
"""
首页
:param request:
:return:
"""
login_status = request.session.get("username", "你未登录,请登录!")
return render(
request,
"ts11/session_index.html",
context={
"login_status": login_status,
},
) def session_login(request):
"""
登录页面
用session实现登录
:param request:
:return:
"""
if request.method == "GET":
return render(request, "ts11/session_login.html")
elif request.method == "POST":
request.session["username"] = request.POST.get("username")
return redirect(reverse("index"))
else:
return HttpResponse("无效的请求") def session_login_out(request):
"""
注销
清空session
:param request:
:return: 清除session后,直接重定向,回到首页
"""
request.session.flush()
return redirect(reverse("index"))

(2).url配置

from django.conf.urls import url
from . import views urlpatterns = [
url(r"^session_index/$", views.session_index, name="index"),
url(r"^session_login/$", views.session_login, name="login_in"),
url(r"^session_login_out/$", views.session_login_out, name="login_out"),
]

(3).模板

# session_index.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>session_index</title>
</head>
<body>
你好!{{ login_status }}<br>
<a href="{% url "login_in" %}">登录</a>
<br><br>
<a href="{% url "login_out" %}">注销</a>
</body>
</html> # session_login.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>session_login</title>
</head>
<body>
<form action="" method="post">
{% csrf_token %}
用户名:<input type="text" name="username"><br>
<input type="submit" value="提交">
</form>
</body>
</html>

(四).会话过期时间

(1).set_expiry(value):设置会话的超时时间

(2).如果没有指定,则14天后过期

(3).如果value是一个整数,会话将在values秒没有活动后过期

(4).若果value是一个imedelta对象,会话将在当前时间加上这个指定的日期/时间过期

(5).如果value为0,那么用户会话的Cookie将在用户的浏览器关闭时过期

(6).如果value为None,那么会话永不过期

(7).在视图函数中设置过期的示例

#------------view.py--------------
def login(request):
if request.method == 'GET':
return render(request,'login.html')
elif request.method == 'POST':
username = request.POST.get('username')
request.session['username']=username
request.session.set_expiry(0) # 关闭浏览器就过期
return redirect(reverse('ts11_home'))

(8).settings.py中的配置

# 是否关闭浏览器使得Session过期,默认是False
SESSION_EXPIRE_AT_BROWSER_CLOSE = False #是否每次请求都保存Session,默认修改之后才保存
SESSION_SAVE_EVERY_REQUEST = False # Session的cookie失效日期,默认是2周
SESSION_COOKIE_AGE = 1209600

(五).如何查看cookie和session

我是chrome的忠实粉,所以只看了Google Chrome的设置。

步骤:设置 - 高级 - 内容设置 - Cookie - 查看所有Cookie和网站数据 - 找到你自己的IP或域名


表单篇

django中的表单不是html中的那个<form>表单。而是app中的forms.py这个文件所生成的(这个文件名不像templates那样严格,可以写form.py也可以写forms.py,但基本是写forms)

django的表单通常是用来验证数据的合法性。很少用来生成HTML代码(django生成的表单较难调整样式)

一、使用表单

(一).表单常用的属性、方法

(1).创建一个forms.py的文件,放在app当中,然后在里面写表单

(2).表单是通过类实现的,继承自forms.Form,然后在里面定义要验证的字段

(3).在表单中,创建字段跟模型是一模一样的,但是没有null=True或者blank=True等这几种参数了,有的参数是required=True/False

(4).使用is_valid()方法可以验证用户提交的数据是否合法,而且HTML表单元素的name必须和django中的表单的name保持一致,否则匹配不到

(5).is_bound属性:用来表示form是否绑定了数据,如果绑定了,则返回True,否则返回False

(6).cleaned_data:这个是在is_valid()返回True的时候,保存用户提交上来的数据.

(7).示例

# forms.py

from django import forms

class RegisterForm(forms.Form):
"""
验证注册页面的form
"""
username = forms.CharField(
max_length=100, min_length=6,
error_messages={
"min_length": "用户名不能少于6位",
}
)
password = forms.CharField(
max_length=100, min_length=6,
widget=forms.PasswordInput(),
error_messages={
"min_length": "密码不能少于6位",
}
)
password_repeat = forms.CharField(
max_length=100, min_length=6,
widget=forms.PasswordInput(),
error_messages={
"min_length": "密码不能少于6位",
}
)
email = forms.EmailField() class RegisterLogin(forms.Form):
"""
验证登录页面的form
"""
username = forms.CharField(
max_length=100, min_length=6,
error_messages={
"min_length": "用户名不能少于6位",
}
)
password = forms.CharField(
max_length=100, min_length=6,
widget=forms.PasswordInput(),
error_messages={
"min_length": "密码不能少于6位",
}
)

(二).字段类型中的一些参数

这些参数会对页面的输入做一些限制条件

max_length  最大长度
min_length 最小长度
widget 负责渲染网页上HTML 表单的输入元素和提取提交的原始数据
attrs 包含渲染后的Widget 将要设置的HTML 属性
error_messages 报错信息

二、表单小案例

需求:实现注册、登录。

要求:使用djang的form进行表单验证。

(一).写模型

from django.db import models

# Create your models here.

class Register(models.Model):
"""
注册页面的模型
"""
username = models.CharField(max_length=100, unique=True)
password = models.CharField(max_length=100)
email = models.EmailField() def __str__(self):
return "Register <username:{},password:{},email:{}>".format(
self.username, self.password, self.email
)

然后进行makemigrations和migrate

(二).写forms.py

#!/usr/bin/env python3
# -*- coding: utf-8 -*-
# __author__ = "Jack" from django import forms class RegisterForm(forms.Form):
"""
验证注册页面的form
"""
username = forms.CharField(
max_length=100, min_length=6,
error_messages={
"min_length": "用户名不能少于6位",
}
)
password = forms.CharField(
max_length=100, min_length=6,
widget=forms.PasswordInput(),
error_messages={
"min_length": "密码不能少于6位",
}
)
password_repeat = forms.CharField(
max_length=100, min_length=6,
widget=forms.PasswordInput(),
error_messages={
"min_length": "密码不能少于6位",
}
)
email = forms.EmailField() class RegisterLogin(forms.Form):
"""
验证登录页面的form
"""
username = forms.CharField(
max_length=100, min_length=6,
error_messages={
"min_length": "用户名不能少于6位",
}
)
password = forms.CharField(
max_length=100, min_length=6,
widget=forms.PasswordInput(),
error_messages={
"min_length": "密码不能少于6位",
}
)

(三).写视图函数views.py

(1).导包

from django.shortcuts import render, HttpResponse, redirect, reverse

from .forms import *
from .models import *

(2).index()

def index(request):
"""
首页
:param request:
:return:
"""
login_status = request.session.get("username", "请登录")
return render(
request,
"ts22/index.html",
context={
"login_status": login_status,
},
)

(3).register()

def register(request):
"""
注册页面的视图函数
:param request:
:return:
"""
if request.method == "GET":
return render(request, "ts22/register.html")
elif request.method == "POST":
form = RegisterForm(request.POST) # 使用django的form进行验证
if form.is_valid():
username = request.POST["username"]
password = request.POST["password"]
password_repeat = request.POST["password_repeat"]
email = request.POST["email"]
if password == password_repeat:
Register.objects.create(username=username, password=password_repeat, email=email)
else:
return render(
request,
"ts22/register.html",
context={"error_message": "两次密码输入的不一致,请检查!", },
)
else:
return render(request, "ts22/register.html", context={"error_message": form.errors, }, )
return render(request, "ts22/register_success.html") # 直接渲染模板,进行跳转
else:
return render(request, "ts22/register.html")

(4).login()

def login(request):
"""
登录页面的视图函数
:param request:
:return:
"""
if request.method == "GET":
return render(request, "ts22/login.html")
elif request.method == "POST":
form = RegisterLogin(request.POST)
if form.is_valid():
form_data = form.cleaned_data
username = form_data["username"]
password = form_data["password"]
try:
Register.objects.get(username=username, password=password) # get()没有返回值是会报错的
except Register.DoesNotExist:
return HttpResponse("登录失败,请检查用户名或密码!") # 用异常捕获来检验是否登录成功
else:
request.session["username"] = username # 保存session
return redirect(reverse("index"))
else:
return render(request, "ts22/login.html", context={"error_messages": form.errors}, )
else:
return HttpResponse("无效的请求")

(5).logout()

def logout(request):
"""
注销页面的视图函数
:param request:
:return: 注销后直接回到首页
"""
request.session.flush()
return redirect(reverse("index"))

(四).写模板templates/ts22

(1).index.html

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>index</title>
</head>
<body>
你好,{{ login_status }}!
<br>
<a href="{% url "register" %}">注册</a>
<br>
<a href="{% url "login" %}">登录</a>
<br>
<a href="{% url "logout" %}">注销</a>
</body>
</html>

(2).login.html

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>login</title>
</head>
<body>
<form action="" method="post">
{% csrf_token %}
用户名:<input type="text" name="username">
<br>
密码:<input type="password" name="password">
<br>
<input type="submit" value="登录">
{{ error_messages }}
</form>
</body>
</html>

(3).register.html

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>register</title>
</head>
<body>
<form action="" method="post">
{% csrf_token %}
用户名:<input type="text" name="username" placeholder="请输入用户名">
<br>
密码:<input type="password" name="password" placeholder="请输入密码">
<br>
确认密码:<input type="password" name="password_repeat" placeholder="请确认密码">
<br>
邮箱:<input type="email" name="email" placeholder="请输入邮箱地址">
<br>
<input type="submit" value="立即注册">
{{ error_message }}
</form>
</body>
</html>

(4).register_success.html

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>注册成功</title>
<style>
* {
margin: 0;
padding: 0;
} .bigbox {
width: 400px;
height: 200px;
text-align: center;
border: 2px solid rgb(180, 9, 28);
border-radius: 7px;
position: absolute;
margin: auto;
top: 0;
right: 0;
bottom: 0;
left: 0;
box-shadow: lightgrey 3px 3px 3px;
} .cell {
position: relative;
top: 50%;
transform: translateY(-50%);
}
</style>
</head>
<body>
<div class="bigbox">
<div class="cell">
注册成功!感谢您注册成为我们的会员!
<br>
<span id="countdown_seconds">5</span>秒后,将会自动回到首页!
<br>
如果浏览器没有自动跳转,<a href="{% url "index" %}">点此回到主页</a>
</div>
</div>
<script>
window.onload = function () {
let time = document.getElementById("countdown_seconds").innerText;
let set = setInterval(function () {
time--;
if (time < 1) {
{#<1就不会出现0了#}
window.location = "{% url "index" %}";
} else {
document.getElementById("countdown_seconds").innerText = time;
}
}, 1000);
}
</script>
</body>
</html>

(五).注册路由

from django.conf.urls import url
from . import views urlpatterns = [
url(r"^index/$", views.index, name="index"),
url(r"^register/$", views.register, name="register"),
url(r"^login/$", views.login, name="login"),
url(r"^logout/$", views.logout, name="logout"),
]

(六).注意事项

(1).新的app不要忘记去总url中注册

(2).新的app不要忘记去settings.py中的INSTALLED_APPS中注册


中间件篇

中间件,顾名思义:中途作处理。可以介入Django的请求和响应处理过程,修改Django的输入或输出。它针对的是request、response

一、自定义中间件

(一).在settings.py同级目录下创建myself_middleware.py文件(中间件的命名随意,但要符合python变量命名规则)

(二).写入代码

例如:

from ts22.models import *

def is_my_user_login(request):
username = request.session.get("username", "")
user = Register.objects.filter(username=username).first() if user:
return {"login_or_not": user.username}
else:
return {"login_or_not": "请登录"}

返回的必须是一个字典!

(三).把这个函数注册进TEMPLATES中去

模型直接沿用了表单篇的小案例

#!/usr/bin/env python3
# -*- coding: utf-8 -*-
# __author__ = "Jack" from django.http import HttpResponse
from django.utils.deprecation import MiddlewareMixin from ts22.models import * class MyExceptionMiddleware(MiddlewareMixin):
def process_exception(self, request, exception):
return HttpResponse("程序出错了:" + str(exception)) class RegisterMiddleware(MiddlewareMixin):
def __init__(self, get_response):
# 接收一个请求对象,也就是request
self.get_response = get_response def __call__(self, request):
# request到达view之前执行的代码
username = request.session.get("username", "")
user = Register.objects.filter(username=username).first()
if user:
if not hasattr(request, "my_user"):
setattr(request, "my_user", user.username)
else:
setattr(request, "my_user", "请登录.this is from middleware") response = self.get_response(request) # 返回响应 """
这块代码是response对象,到达浏览器之前执行的代码
""" return response

(三).每个中间件组件是一个独立的Python类,可以在类中定义下面方法中的一个或多个

注意:它们的方法名都是固定的!

_init _:无需任何参数,服务器响应第一个请求的时候调用一次,用于确定是否启用当前中间件

process_request(request):执行视图之前被调用,在每个请求上调用,返回None或HttpResponse对象

process_view(request, view_func, view_args, view_kwargs):调用视图之前被调用,在每个请求上调用,返回None或HttpResponse对象

process_template_response(request, response):在视图刚好执行完毕之后被调用,在每个请求上调用,返回实现了render方法的响应对象

process_response(request, response):所有响应返回浏览器之前被调用,在每个请求上调用,返回HttpResponse对象

process_exception(request,response,exception):当视图抛出异常时调用,在每个请求上调用,返回一个HttpResponse对象

(四).注册

将(二)中的两个类,注册到settings.py中间件中的MISSLEWARE里面去

(五).在视图函数中使用中间件

def index(request):
"""
首页
:param request:
:return:
"""
login_status = request.my_user
return render(
request,
"ts22/index.html",
context={
"login_status": login_status,
},
)

(六).作用

后期需要加功能,直接加中间件处理了,不可能一个个视图去改了。

如果有好多视图需要同一个功能,中间件就很好用了。一处写,整个项目都能用了。


上下文处理器篇

实质上就是视图函数中的render(context={}),上下文处理器就是个模板变量,只针对模板。

一、自定义上下文处理器

(一).在settings.py同级目录下创建myself_contextprocessor.py文件(命名随意,但要符合python变量命名规则)

(二).写入代码

还是沿用了表单篇的models

from ts22.models import *

def is_my_user_login(request):
username = request.session.get("username", "")
user = Register.objects.filter(username=username).first() if user:
return {"login_or_not": user.username}
else:
return {"login_or_not": "请登录"}

返回的必须是一个字典!

(三).把这个函数注册进settings.py中的TEMPLATES去。

(四).使用它

直接去模板中,在你想要的地方,写上{{ login_or_not }}