Django框架全面讲解二
七、中间件(MiddleWare)
django 中的中间件(middleware),在django中,中间件其实就是一个类,在请求到来和结束后,django会根据自己的规则在合适的时机执行中间件中相应的方法。
在django项目的settings模块中,有一个 MIDDLEWARE_CLASSES 变量,其中每一个元素就是一个中间件,如下图
中间件中可以定义五个方法,分别是:
- process_request(self,request)
- process_view(self, request, callback, callback_args, callback_kwargs)
- process_template_response(self,request,response)
- process_exception(self, request, exception)
- process_response(self, request, response
分析源码得知前二个方法是从前往后执行的,后三个方法是从后往前执行的
所以前两个方法是请求进来的,后三个方法是请求出去的
一张图告诉你中间件的运行流程
自定义中间件
1、创建中间件类
class Middle_Test(object):
def process_request(self,request):
pass
def process_view(self, request, callback, callback_args, callback_kwargs):
1
pass
def process_exception(self, request, exception):
pass
def process_response(self, request, response):
return response
2、注册中间件
MIDDLEWARE_CLASSES = (
'django.contrib.sessions.middleware.SessionMiddleware',
'django.middleware.common.CommonMiddleware',
'django.middleware.csrf.CsrfViewMiddleware',
'django.contrib.auth.middleware.AuthenticationMiddleware',
'django.contrib.auth.middleware.SessionAuthenticationMiddleware',
'django.contrib.messages.middleware.MessageMiddleware',
'django.middleware.clickjacking.XFrameOptionsMiddleware',
'sparks.middleware.auth.Middle_Test',
)
八、 Form
1.使用 forms 模块生成表单,主要处理GET请求
1.forms模块的作用
通过forms模块,允许将表单与class结合(表单与实体类结合),允许通过class生成表单
2.使用forms模块
1.在应用中创建 forms.py 文件
2.导入forms模块
from django import forms
3.创建class,一个class对应生成一个表单
class ClassName(forms.Form):
pass
4.创建属性
一个属性对应到表单中生成一个控件
3.在模板中解析 form 对象
1.注意
在模板中,需要:
1.自定义 <form></form>
2.自定义 提交按钮
2.处理方法
1.在 views 中创建 form 的对象并发送到模板上
form = RemarkForm()
return render(request,'xxx.html',locals())
2.在模板中进行解析
1.手动解析
{% for field in form %}
{{field}} :
表示的就是form对象中的一个独立属性
表示的也就是一个独立的控件
{{field.label}}:
表示的是控件中的label的值
{% endfor %}
2.自动解析
1.{{form.as_p}}
将form对象中的每个属性使用p标记包裹起来再显示
2.{{form.as_ul}}
将form对象中的每个属性使用li标记包裹起来,再显示
注意:必须手动提供<ol> 或 <ul>
3.{{form.as_table}}
将form对象中的每个属性用tr标记包裹起来,再显示
注意:必须手动提供<table>
2.通过 forms.Form 获取表单数据 - POST
1.通过 forms.Form 的构造函数来接收post数据
form = RemarkForm(request.POST)
2.必须使form通过验证后再获取数据
form.is_valid()
返回True:提交的数据以及表单已通过所有的验证,允许取值
返回False:未通过验证,则不能正常取值
3.获取表单中的数据
通过 form.cleaned_data 来获取提交的数据
from django import forms
#声明ChoiceField要用到的数据
TOPIC_CHOICE = (
('1', '好评'),
('2', '中评'),
('3', '差评'),
)
class RemarkForm(forms.Form):
#评论标题
# forms.CharField() - 文本框
# label : 控件前的文本标签
subject = forms.CharField(label='标题')
#电子邮箱
# forms.EmailField() - Email框
# label : 控件前的文本标签
email = forms.EmailField(label='邮箱')
#品论内容
# widget=Textarea : 将当前的单行文本框变为多行文本域
message = forms.CharField(label='内容', widget=forms.Textarea)
#品论级别
# forms.ChoiceField() - 下拉列表框
# choices : 表示当前下拉列表框中的数据,取值为元组或列表
topic = forms.ChoiceField(label='级别', choices=TOPIC_CHOICE)
#是否保存-复选框
isSaved = forms.BooleanField(label='是否保存')
def form_register(request):
if request.method == 'GET':
form = RegisterForm()
return render(request,'07-form-register.html',locals())
else:
# 将request.POST中的数据提交给RegisterForm()
form = RegisterForm(request.POST)
# 将数据通过验证
if form.is_valid():
# 验证过后获取数据并保存进数据库
au = Author(**form.cleaned_data)
au.save()
return HttpResponse("Register OK")
html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<style>
#id_subject{
border:none;
border-bottom:1px solid #000;
outline:none;
}
</style>
</head>
<body>
{% comment '手动解析' %}
<form action="/06-form/" method="post">
{% csrf_token %}
{% for field in form %}
<p>
{{ field.label }}:{{ field }}
</p>
{% endfor %}
<p>
<input type="submit">
</p>
</form>
{% endcomment %}
{% comment '自动解析- {{ form.as_p }}' %}
<form action="/06-form/" method="post">
{{ form.as_p }}
<p>
<input type="submit">
</p>
</form>
{% endcomment %}
</body>
1.使用 forms 模块
2.forms模块的高级处理
将 Models 和 Forms 结合到一起使用
1.在 forms.py 中创建 class ,继承自 forms.ModelForm
2.创建内部类Meta,去关联 Model
1.model : 指定要关联的实体类
2.fields : 指定从Model中取哪些字段生成控件
1.取值 "__all__",表示全部属性都要生成控件
2.取值 列表,声明允许生成控件的属性名
3.labels : 指定每个属性所关联的label,取值为字典
labels = {
'属性名':'label文本',
'属性名':'label文本',
}
3.内置小部件 - widget
1.什么是小部件
小部件,表示的就是生成到网页上的控件类型以及其他的html属性
2.常用小部件类型
1.TextInput : type='text'
2.PasswordInput : type='password'
3.NumberInput : type='number'
4.EmailInput : type='email'
5.URLInput : type='url'
6.HiddenInput : type='hidden'
7.CheckboxInput : type='checkbox'
8.Textarea : <textarea></textarea>
9.Select : <select></select>
3.小部件的使用
1.继承自 forms.Form 类
1.基本版
只指定控件的类型
class RemarkForm(forms.Form):
属性 = forms.CharField(
label='文本',
widget=forms.小部件类型
)
2.高级版
指定控件类型的基础上还允许设置一些相关的HTML属性到控件上
属性 = forms.CharField(
label = '文本',
widget = forms.小部件类型(
attrs = {
'html属性名':'属性值',
'html属性名':'属性值',
}
)
)
2.继承自 forms.ModelForm 类
class XXXForm(forms.ModelForm):
class Meta:
model = xxxx
fields = "__all__" 或 []
labels = {
'属性1':'标签1',
}
#指定小部件
widgets = {
"属性1":forms.小部件类型(
attrs = {
'属性':'值',
}
),
}
class WidgetForm2(forms.ModelForm):
class Meta:
#指定关联的实体
model = Author
#指定要显示的字段
fields = ['name','age','email']
#指定字段对应的标签
labels = {
'name':'用户姓名',
'age':'用户年龄',
'email':'用户邮箱',
}
#指定字段对应的小部件
widgets = {
'age':forms.NumberInput(
attrs = {
'placeholder':'请输入年龄',
'class':'form-control',
}
),
'email':forms.EmailInput(
attrs = {
'placeholder':'请输入您的电子邮箱',
'class':'form-control',
}
)
}
def widget2_views(request):
if request.method == 'GET':
form = WidgetForm2()
return render(request,'08-widget1.html',locals())
else:
# 将request.POST中的数据提交给RegisterForm()
form = RegisterForm(request.POST)
# 将数据通过验证
if form.is_valid():
# 验证过后获取数据并保存进数据库
au = Author(**form.cleaned_data)
au.save()
return HttpResponse("Register OK")
html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<style>
input.form-control{
border:1px solid #000;
width:300px;
height:30px;
box-sizing:border-box;
padding:3px 10px;
outline:none;
}
</style>
</head>
<body>
<form action="/09-widget2">
{{ form.as_p }}
</form>
</body>
</html>
九、 认证系统(auth)
auth模块是Django提供的标准权限管理系统,可以提供用户身份认证, 用户组管理,并且可以和admin模块配合使用.
在INSTALLED_APPS中添加’django.contrib.auth’使用该APP, auth模块默认启用.
新建用户
from django.contrib.auth.models import User
user = User.objects.create_user(username, email, password)
user.save()
# 不存储用户密码明文而是存储一个Hash值
认证用户
from django.contrib.auth import authenticate
user = authenticate(username=username, password=password)
# 认证用户的密码是否有效, 若有效则返回代表该用户的user对象, 若无效则返回None.
# 该方法不检查is_active标志位.
修改密码:
user.set_password(new_password)
# 以下实例为先认证通过后才可以修改密码
user = auth.authenticate(username=username, password=old_password)
if user is not None:
user.set_password(new_password)
user.save()
登录
from django.contrib.auth import login
# login向session中添加SESSION_KEY, 便于对用户进行跟踪:
'login(request, user)'
# login不进行认证,也不检查is_active标志位
# 实例
user = authenticate(username=username, password=password)
if user is not None:
if user.is_active:
login(request, user)
退出登录
# logout会移除request中的user信息, 并刷新session
from django.contrib.auth import logout
def logout_view(request):
logout(request)
只允许登录的用户访问
@login_required装饰器装饰的view函数会先通过session key检查是否登录, 已登录用户可以正常的执行操作, 未登录用户将被重定向到login_url指定的位置,若未指定login_url参数, 则重定向到settings.LOGIN_URL
from django.contrib.auth.decorators import login_required
@login_required(login_url='/accounts/login/')
def userinfo(request):
...
# settings 配置
LOGIN_URL = '/index/'
# views
@login_required
def userinfo(request):
...
十、 跨站请求伪造(csrf)
django为用户实现防止跨站请求伪造的功能,通过中间件 django.middleware.csrf.CsrfViewMiddleware 来完成。而对于django中设置防跨站请求伪造功能有分为全局和局部。
全局:
中间件 django.middleware.csrf.CsrfViewMiddleware
局部:
- @csrf_protect,为当前函数强制设置防跨站请求伪造功能,即便settings中没有设置全局中间件。
- @csrf_exempt,取消当前函数防跨站请求伪造功能,即便settings中设置了全局中间件。
注:from django.views.decorators.csrf import csrf_exempt,csrf_protec
应用
1、普通表单
# veiw中设置返回值:
return render(request, 'xxx.html', data)
# html中设置Token:
{% csrf_token %}
十一、2.cookies 和 session
2.cookies 和 session
1.cookies
1.django 中使用 cookies
1.设置cookies的值(将数据保存到客户端)
语法:
响应对象.set_cookie(key,value,expires)
key:cookie的名字
value:cookie的值
expires:保存时间,以s为单位
1.不使用模板
resp = HttpResponse('响应给客户端的一句话')
resp.set_cookie(key,value,expires)
return resp
2.使用模板
resp = render(request,'xxx.html',locals())
resp.set_cookie(key,value,expires)
return resp
3.使用重定向
resp = redirect('/地址/')
resp.set_cookie(key,value,expires)
return resp
2.获取cookies的值
伴随着请求对象到达服务器之后再获取cookie的值
request.COOKIES:封装了当前访问站点下的所有的cookie的信息
3.删除cookie的值
通过响应对象通知客户端删除数据
resp.delete_cookie(key)
2.session
1.设置 session 的值
request.session['key'] = 值
2.获取 session 的值
value = request.session['key']
value = request.session.get('key')
3.删除 session 的值
del request.session['key']
4.有关 session 的配置
在 settings.py 中,有关session的设置
1.SESSION_COOKIE_AGE
作用:设置sessionID在cookies中的存活时间
ex:
SESSION_COOKIE_AGE=60*30
2.SESSION_EXPIRE_AT_BROWSER_CLOSE
作用:设置浏览器关闭时则清除服务器上对应的session空间
ex:
SESSION_EXPIRE_AT_BROWSER_CLOSE = True
十二、Session
Django中默认支持Session,其内部提供了5种类型的Session供开发者使用:
- 数据库(默认)
- 缓存
- 文件
- 缓存+数据库
- 加密cookie
1.数据库Session
Django默认支持Session,并且默认是将Session数据存储在数据库中,即:django_session 表中。
a. 配置 settings.py
SESSION_ENGINE = 'django.contrib.sessions.backends.db' # 引擎(默认)
SESSION_COOKIE_NAME = "sessionid" # Session的cookie保存在浏览器上 时的key,即:sessionid=随机字符串(默认)
SESSION_COOKIE_PATH = "/" # Session的cookie保存的路径(默认)
SESSION_COOKIE_DOMAIN = None # Session的cookie保存的域名(默认)
SESSION_COOKIE_SECURE = False # 是否Https传输cookie(默认)
SESSION_COOKIE_HTTPONLY = True # 是否Session的cookie只支持http传输(默认)
SESSION_COOKIE_AGE = 1209600 # Session的cookie失效日期(2周)(默认)
SESSION_EXPIRE_AT_BROWSER_CLOSE = False # 是否关闭浏览器使得Session过期(默认)
SESSION_SAVE_EVERY_REQUEST = False # 是否每次请求都保存Session,默认修改之后才保存(默认)
b. 使用
def index(request):
# 获取、设置、删除Session中数据
request.session['k1']
request.session.get('k1',None)
request.session['k1'] = 123
request.session.setdefault('k1',123) # 存在则不设置
del request.session['k1']
# 所有 键、值、键值对
request.session.keys()
request.session.values()
request.session.items()
request.session.iterkeys()
request.session.itervalues()
request.session.iteritems()
# 用户session的随机字符串
request.session.session_key
# 将所有Session失效日期小于当前日期的数据删除
request.session.clear_expired()
# 检查 用户session的随机字符串 在数据库中是否
request.session.exists("session_key")
# 删除当前用户的所有Session数据
request.session.delete("session_key")
...
2、缓存Session
a. 配置 settings.py
SESSION_ENGINE = 'django.contrib.sessions.backends.cache' # 引擎
SESSION_CACHE_ALIAS = 'default' # 使用的缓存别名(默认内存缓存,也可以是memcache),此处别名依赖缓存的设置
SESSION_COOKIE_NAME = "sessionid" # Session的cookie保存在浏览器上时的key,即:sessionid=随机字符串
SESSION_COOKIE_PATH = "/" # Session的cookie保存的路径
SESSION_COOKIE_DOMAIN = None # Session的cookie保存的域名
SESSION_COOKIE_SECURE = False # 是否Https传输cookie
SESSION_COOKIE_HTTPONLY = True # 是否Session的cookie只支持http传输
SESSION_COOKIE_AGE = 1209600 # Session的cookie失效日期(2周)
SESSION_EXPIRE_AT_BROWSER_CLOSE = False # 是否关闭浏览器使得Session过期
SESSION_SAVE_EVERY_REQUEST = False # 是否每次请求都保存Session,默认修改之后才保存
b. 使用
同上
3、文件Session
a. 配置 settings.py
SESSION_ENGINE = 'django.contrib.sessions.backends.file' # 引擎
SESSION_FILE_PATH = None # 缓存文件路径,如果为None,则使用tempfile模块获取一个临时地址tempfile.gettempdir() # 如:/var/folders/d3/j9tj0gz93dg06bmwxmhh6_xm0000gn/T
SESSION_COOKIE_NAME = "sessionid" # Session的cookie保存在浏览器上时的key,即:sessionid=随机字符串
SESSION_COOKIE_PATH = "/" # Session的cookie保存的路径
SESSION_COOKIE_DOMAIN = None # Session的cookie保存的域名
SESSION_COOKIE_SECURE = False # 是否Https传输cookie
SESSION_COOKIE_HTTPONLY = True # 是否Session的cookie只支持http传输
SESSION_COOKIE_AGE = 1209600 # Session的cookie失效日期(2周)
SESSION_EXPIRE_AT_BROWSER_CLOSE = False # 是否关闭浏览器使得Session过期
SESSION_SAVE_EVERY_REQUEST = False # 是否每次请求都保存Session,默认修改之后才保存
b. 使用
同上
4、缓存 + 数据库 Session
数据库用于做持久化,缓存用于提高效率
a. 配置 settings.py
SESSION_ENGINE = 'django.contrib.sessions.backends.cached_db' # 引擎
b. 使用
同上
5、加密cookie Session
a. 配置 settings.py
SESSION_ENGINE = 'django.contrib.sessions.backends.signed_cookies' # 引擎
b. 使用
同上
十三、缓存
由于Django是动态网站,所有每次请求均会去数据进行相应的操作,当程序访问量大时,耗时必然会更加明显,最简单解决方式是使用:缓存,缓存将一个某个views的返回值保存至内存或者memcache中,5分钟内再有人来访问时,则不再去执行view中的操作,而是直接从内存或者Redis中之前缓存的内容拿到,并返回。
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, # 缓存到达最大个数之后,剔除缓存个数的比例,即:1/CULL_FREQUENCY(默认3)
},
'KEY_PREFIX': '', # 缓存key的前缀(默认空)
'VERSION': 1, # 缓存key的版本(默认1)
'KEY_FUNCTION' 函数名 # 生成key的函数(默认函数会生成为:【前缀:版本:key】)
}
}
# 自定义key
def default_key_func(key, key_prefix, version):
"""
Default function to generate keys.
Constructs the key used by all other methods. By default it prepends
the `key_prefix'. KEY_FUNCTION can be used to specify an alternate
function with custom key making behavior.
"""
return '%s:%s:%s' % (key_prefix, version, key)
def get_key_func(key_func):
"""
Function to decide which key function to use.
Defaults to ``default_key_func``.
"""
if key_func is not None:
if callable(key_func):
return key_func
else:
return import_string(key_func)
return default_key_func
b、内存
# 此缓存将内容保存至内存的变量中
# 配置:
CACHES = {
'default': {
'BACKEND': 'django.core.cache.backends.locmem.LocMemCache',
'LOCATION': 'unique-snowflake',
}
}
# 注:其他配置同开发调试版本
c、文件
# 此缓存将内容保存至文件
# 配置:
CACHES = {
'default': {
'BACKEND': 'django.core.cache.backends.filebased.FileBasedCache',
'LOCATION': '/var/tmp/django_cache',
}
}
# 注:其他配置同开发调试版本
d、数据库
# 此缓存将内容保存至数据库
# 配置:
CACHES = {
'default': {
'BACKEND': 'django.core.cache.backends.db.DatabaseCache',
'LOCATION': 'my_cache_table', # 数据库表
}
}
# 注:执行创建表命令 python manage.py createcachetable
e、Memcache缓存(python-memcached模块)
# 此缓存使用python-memcached模块连接memcache
CACHES = {
'default': {
'BACKEND': 'django.core.cache.backends.memcached.MemcachedCache',
'LOCATION': '127.0.0.1:11211',
}
}
CACHES = {
'default': {
'BACKEND': 'django.core.cache.backends.memcached.MemcachedCache',
'LOCATION': 'unix:/tmp/memcached.sock',
}
}
CACHES = {
'default': {
'BACKEND': 'django.core.cache.backends.memcached.MemcachedCache',
'LOCATION': [
'172.19.26.240:11211',
'172.19.26.242:11211',
]
}
}
f、Memcache缓存(pylibmc模块)
# 此缓存使用pylibmc模块连接memcache
CACHES = {
'default': {
'BACKEND': 'django.core.cache.backends.memcached.PyLibMCCache',
'LOCATION': '127.0.0.1:11211',
}
}
CACHES = {
'default': {
'BACKEND': 'django.core.cache.backends.memcached.PyLibMCCache',
'LOCATION': '/tmp/memcached.sock',
}
}
CACHES = {
'default': {
'BACKEND': 'django.core.cache.backends.memcached.PyLibMCCache',
'LOCATION': [
'172.19.26.240:11211',
'172.19.26.242:11211',
]
}
}
2、应用
a. 全站使用
使用中间件,经过一系列的认证等操作,如果内容在缓存中存在,则使用FetchFromCacheMiddleware获取内容并返回给用户,当返回给用户之前,判断缓存中是否已经存在,如果不存在则UpdateCacheMiddleware会将缓存保存至缓存,从而实现全站缓存
MIDDLEWARE = [
'django.middleware.cache.UpdateCacheMiddleware',
# 其他中间件...
'django.middleware.cache.FetchFromCacheMiddleware',
]
CACHE_MIDDLEWARE_ALIAS = ""
CACHE_MIDDLEWARE_SECONDS = ""
CACHE_MIDDLEWARE_KEY_PREFIX = ""
b. 单独视图缓存
方式一:
from django.views.decorators.cache import cache_page
@cache_page(60 * 15)
def my_view(request):
...
方式二:
from django.views.decorators.cache import cache_page
urlpatterns = [
url(r'^foo/([0-9]{1,2})/$', cache_page(60 * 15)(my_view)),
]
c、局部视图使用
a. 引入TemplateTag
{% load cache %}
b. 使用缓存
{% cache 5000 缓存key %}
缓存内容
{% endcache %}
注:如果出现多个url匹配同一个view函数的情况,缓存机制会根据每一个不同的url做单独的缓存