Django
一、中间件
django请求的生命周期
完整周期
可以看出中间件就是在实现请求之前,django或者我们自定义的对该请求的一些规则,而且是全局的,因为中间件的触发实在路由分发之前,比如某ip,某设为不能访问
django已经定义好的中间件
MIDDLEWARE = [
'django.middleware.security.SecurityMiddleware',
'django.contrib.sessions.middleware.SessionMiddleware',
'django.middleware.common.CommonMiddleware',
'django.middleware.csrf.CsrfViewMiddleware',
'django.contrib.auth.middleware.AuthenticationMiddleware',
'django.contrib.messages.middleware.MessageMiddleware',
'django.middleware.clickjacking.XFrameOptionsMiddleware',
]
如果我们看这些类会发现,他们都继承了一个类MiddlewareMixin
class MiddlewareMixin:
def __init__(self, get_response=None):
self.get_response = get_response
super().__init__()
def __call__(self, request):
response = None
if hasattr(self, 'process_request'): # 如果有process_request方法就执行
response = self.process_request(request)
response = response or self.get_response(request)
if hasattr(self, 'process_response'): # 如果有process_response方法就执行
response = self.process_response(request, response)
return response
- 所以,我们可以这样定义自己的类
from django.utils.deprecation import MiddlewareMixin
class MyMW(MiddlewareMixin):
def process_request(self, request):
print('1:', 'mymdwa-->process_request')
def process_response(self, request, response):
print('1:', 'mymdwa-->process_response')
return response # process_response是需要返回值的
class Mymw(MiddlewareMixin):
def process_request(self, request):
print('2:', 'mymdwa-->process_request')
def process_response(self, request, response):
print('2:', 'mymdwa-->process_response')
return response
这里需要注意的是:process_response是需要返回值的
- 然后我们将自定义的中间件加入到settings中
MIDDLEWARE = [
'django.middleware.security.SecurityMiddleware',
'django.contrib.sessions.middleware.SessionMiddleware',
'django.middleware.common.CommonMiddleware',
'django.middleware.csrf.CsrfViewMiddleware',
'django.contrib.auth.middleware.AuthenticationMiddleware',
'django.contrib.messages.middleware.MessageMiddleware',
'django.middleware.clickjacking.XFrameOptionsMiddleware',
'app01.md.mdware.MyMW', # 自定义中间件
'app01.md.mdware.Mymw', # 自定义中间件
]
- 接着做一次get请求,我们可以看到打印结果:
Views是我在views函数中加入的print(‘Views’)
这里反应的结果就是django先处理一个个request函数,如果一直通过,就会到达views函数,最后再将render的response逆序一层层地给中间件,最后传递到前端
可以发现这里的request和response其实就是请求头和相应结果
既然这个process_request(self, request)函数是用来处理请求头的,那如果该请求没有符合要求,我们就要给用户返回一个错误页面,或者请求结果
from django.utils.deprecation import MiddlewareMixin
from django.shortcuts import HttpResponse
class MyMW(MiddlewareMixin):
def process_request(self, request):
if 允许请求:
print('1:', 'mymdwa-->process_request')
else:
return HttpResponse('403')
def process_response(self, request, response):
print('1:', 'mymdwa-->process_response')
return response
class Mymw(MiddlewareMixin):
def process_request(self, request):
if 允许请求:
print('2:', 'mymdwa-->process_request')
else:
return HttpResponse('403')
def process_response(self, request, response):
print('2:', 'mymdwa-->process_response')
return response
在process_request()中加上未允许请求时的返回结果即可,1.7版本(目前最新2.11)之后,有一个中间件未通过,返回错误结果后,后面的中间件将不再执行。
- 其他
中间件的本质是,请求到达中间件时,先执行父类MiddlewareMixin的__call__方法,检测是否有process_request()和process_response()方法,再跟着上面讲述的顺序执行。
所以我们也可以自定义这个父类,方便我们写代码。
我们自己可以写一个类:
class MyMiddlewareMixin:
def __init__(self, get_response=None):
self.get_response = get_response
super().__init__()
def __call__(self, request):
response = None
if hasattr(self, 'my_process_request'):
response = self.process_request(request)
response = response or self.get_response(request)
if hasattr(self, 'my_process_response'):
response = self.process_response(request, response)
return response
这里修改了父类的名字和请求函数的名字,以后我们的中间件类就可以继承MyMiddlewareMixin,其中的函数名也可以改成my_process_request()和my_process_response(),当然这里只是举一个例子,类名和函数名都可以根据自己习惯定义。
- 中间件的另外几个函数
from django.utils.deprecation import MiddlewareMixin
from django.shortcuts import HttpResponse
class MyMW(MiddlewareMixin):
def process_request(self, request):
if 允许请求:
print('1:', 'mymdwa-->process_request')
else:
return HttpResponse('403')
def process_response(self, request, response):
print('1:', 'mymdwa-->process_response')
return response
def process_view(self, request, view_func, view_args, views_kwargs):
"""
在所有process_request函数执行完后,再从头依次执行view函数
request: 请求头,
view_func: views.py的对应处理函数,
view_args: view_func()的参数
view_kwargs: view_func()的参数
"""
print('1:', 'mymdwa-->process_view')
def process_exception(self, request, exception):
"""views.py的函数运行异常时逆序执行此函数"""
return HttpResponse('请求出错.') # 遇到return后又从最后一个中间件的process_response函数逆序执行,直到返回给前端
def process_template_response(self, request, response):
"""不常用"""
pass
二、CSRF
跨站请求伪造
在第一次请求的时候django服务器会给前端返回一个csrf-cookies,以后post请求时必须带上这个csrf才能通过这个csrfMiddleware,我们有几种方式来实现:
- Form请求:在form表单下添加{% csrf_token %}
- ajax请求:给ajax配置请求头:
// 预配置
$.ajaxSteup({
beforeSend:funtin(xhr, setting){
xhr.setRequestHeader('X-CSRFToken', $.cookie('csrf'));
}
});
// ajax请求
$('.submit-btn').click({
$.ajax({
url: '/url/',
type: 'POST',
data: {'username': 'wolf'},
success: function(arg){},
})
})
- ajax请求:在发送的data数据中加上csrf:
// ajax请求
$('.submit-btn').click({
$.ajax({
url: '/url/',
type: 'POST',
data: {'username': 'wolf', 'csrfmiddlewaretoken': '{{ csrf_token }}'},
success: function(arg){},
})
})
- 因为中间件是全局的,在views.py中可以设置装饰器,设置某些函数必须通过csrf验证,而全站不用
# 导入装饰器
from django.views.decorators.csrf import csrf_exempt, csrf_protect
# 添加装饰器,这个函数用,其他的不用
@csrf_protect
def login(request):
from django.conf import settings # django所有的配置
print(settings.CSRF_HEADER_NAME) #
return render(request, 'html')
@csrf_exempt # 添加排除装饰器,这个函数不用,其他的都用
def logout(request):
return render(request, ''html)
一般用全站,只是少部分不用,可以使用@csrf_exempt
三、缓存
如果每次请求都去访问数据库,耗时将会很明显,最好的解决方法就是用缓存:将某个views函数的返回值保存在内存(可以是其他机器)或者memcache中,设定时间内再有人来访问,就直接从内存或redis中获取,而不再去操作数据库。优势是快,劣势是不能实时更新,我们永远只能获取到大约5分钟以前的数据。
django的6种缓存方式
- 开发调试
- 内存
- 文件
- 数据库
- memcache缓存(python-memcached模块)
- memcache缓存(pylibmc模块)
一般访问量大,更新频率低的适合缓存
1、配置
a、开发调试
CACHES = {
'default': {
'BACKEND': 'django.core.cache.backends.dummy.DummyCache', # 引擎
'TIMEOUT': 300, # 缓存超时时间(默认300,None表示永久,0表示立即过期)
'OPTIONS': {
'MAX_ENTRIES': 300, # 最大缓存数(默认300)
'CULL_FREQUENCY': 3, # 缓存达到最大后,删除缓存的个数比例,默认为3,即删除1/3
}
'KEY_PREFIX': '', # 缓存key的前缀,默认为空
'VERSION': 1, # 缓存key的版本,默认为1
'KEY_FUNCTION':函数名 # 生成key的函数(默认函数会生成为:[前缀:版本:key])
}
}
# 自定义key
def my_key()
b、内存
CACHES = {
'default': {
'BACKEND': 'django.core.cache.backends.dummy.DummyCache', # 引擎
'LOCATION': 'unique-snowflake',
}
}
# 其他配置同开发调试
c、文件
CACHES = {
'default': {
'BACKEND': 'django.core.cache.backends.filebased.FileBasedCache', # 引擎
'LOCATION': '/var/temp/cache', # 文件路径
}
}
# 其他配置同开发调试
d、数据库
CACHES = {
'default': {
'BACKEND': 'django.core.cache.backends.filebased.FileBasedCache', # 引擎
'LOCATION': 'my_cache_table', # 数据库表明
}
}
# 其他配置同开发调试
e、Memcache缓存(python-memcached模块)
CACHES = {
'default': {
'BACKEND': 'django.core.cache.backends.filebased.FileBasedCache', # 引擎
'LOCATION': '127.0.0.1:10001', # 另外一台电脑的ip: port,socket访问
}
}
CACHES = {
'default': {
'BACKEND': 'django.core.cache.backends.filebased.FileBasedCache', # 引擎
'LOCATION': 'unix:/tmp/memcached.soc', # 本机文件
}
}
CACHES = {
'default': {
'BACKEND': 'django.core.cache.backends.filebased.FileBasedCache', # 引擎
'LOCATION': [
('172.168.1.1:9000',10) # 后面数字为权重,值越大,缓存到这台机器的概率越大
('172.168.1.2:9000',100)
] # 多台机器
}
}
# 其他配置同开发调试
f、Memcache缓存(pylibmc模块)
"""同上"""
2. 应用
先将上面的配置添加到settings.py文件中
a. 本页缓存
from django.views.decorators.cache import cache_page
import time
@cache_page(10) # 加入缓存装饰器,这个views函数对应的页面就会缓存,参数为缓存时间/s
def cac(request):
t = time.time()
return HttpResponse(t)
b. 局部缓存
{% load cache %}
{% cache 5000 key %} <!--缓存10秒,缓存文件为-->
缓存内容
{% cendcache %}
c. 全站缓存
访问进来时,为了不访问views函数直接返回缓存数据,我们就需要将检测是否返回缓存的函数写在我们前面所讲的process_view()中间件函数中,而对于是否对此次请求返回数据,需要经过中间件的审核,所以这个process_view()函数须要放在中间件的最后一步;
而对于缓存内容只是之前views.py函数所返回的response,我们是可能在中间件中对他进行了一些改变的,所以process_response()函数须要放在中间件的第一个位置。
这样的两个中间件django已经帮我们写好了。
MIDDLEWARE = [
'django.middleware.cache.UpdateCacheMiddleware',
# 其他中间件
'django.middleware.cache.FetchFromCacheMiddleware',
]
3. session使用缓存
在session配置中添加下面代码即可
SESSION_CACHE_ALIAS = 'sessions'
四、信号
Django预留的hook
让django在执行一些操作之前或者之后自动触发一些事件,比如记录数据库的增删改查操作,
- 内置信号
a. hook名称
Model signals
pre_init # django的modal执行其构造方法前,自动触发
post_init # django的modal执行其构造方法后,自动触发
pre_save # django的modal对象保存前,自动触发
post_save # django的modal对象保存后,自动触发
pre_delete # django的modal对象删除前,自动触发
post_delete # django的modal对象删除后,自动触发
m2m_changed # django的modal中使用m2m字段操作第三张表(add,remove,clear)前后,自动触发
class_prepared # 程序启动时,检测已注册的app中modal类,对于每一个类,自动触发
Management signals
pre_migrate # 执行migrate命令前,自动触发
post_migrate # 执行migrate命令后,自动触发
Request/response signals
request_started # 请求到来前,自动触发
request_finished # 请求结束后,自动触发
got_request_exception # 请求异常后,自动触发
Test signals
setting_changed # 使用test测试修改配置文件时,自动触发
template_rendered # 使用test测试渲染模板时,自动触发
Database Wrappers
connection_created # 创建数据库连接时,自动触发
b. 导入方法:
from django.core.signals import request_finished
from django.core.signals import request_started
from django.core.signals import got_request_exception
from django.db.models.signals import class_prepared
from django.db.models.signals import pre_init, post_init
from django.db.models.signals import pre_save, post_save
from django.db.models.signals import pre_delete, post_delete
from django.db.models.signals import m2m_changed
from django.db.models.signals import pre_migrate, post_migrate
from django.test.signals import setting_changed
from django.test.signals import template_rendered
from django.db.backends.signals import connection_created
# 注册信号
def my_func(sender,**kwargs):
"""sender: django给我们提供的一些信息"""
print('Do my func first')
reqyest_finished.connect(my_func) # 即在请求结束后执行my_func()方法
- 因为在django项目开启时就要注册这些信号,所以写在项目的__init__.py文件中,或者写在其他py文件中,在__init__.py中导入
- 一个hook可以添加多个函数
- 自定义信号
a. 定义信号
import django.dispatch
my_signal = django.dispatch.Signal(providing_args=['x', 'y']) # x,y参数自定义
b. 注册信号
def callback(sender, **kwargs):
print('my_signal')
my_signal.connect(callback)
c. 触发信号
在自己写的views等函数中触发
from path import my_signal
my_signal.send(sender='name', x='x', y='y')
其实信号就相当于自定义的模块,方便调用,当我们遇到可能需要灵活调用的功能时就可以选择信号
五、BootStrap(模板) - 响应式+模板
集成了css、js的一个文件夹
响应式
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>响应式</title>
<style>
/* 监听浏览器宽度,当宽度小于700px时,改变对应的样式
这里当浏览器宽度大于700px时,h1标签为黑色字体,宽度小于700px时,字体会变成红色 */
@media(max-width: 700px){
.response-style{
color: red;
}
}
</style>
</head>
<body>
<h1 class="response-style">响应式</h1>
</body>
</html>
先引入jQuery再引入boostrapjs
–> 企业官网(延迟加载+组合搜索)