当你考虑开发现代化、高效且可扩展的网站和Web应用时,Django是一个强大的选择。Django是一个流行的开源Python Web框架,它提供了一个坚实的基础,帮助开发者快速构建功能丰富且高度定制的Web应用
Django全套笔记地址: 请移步这里
共 10 章,31 子模块
请求与响应
学习目标
- 掌握request对象使用
- 掌握response对象使用
- 掌握Django中cookie的使用
- 掌握Django中session的使用
Session
一、Session介绍
-
session
与cookie
对比cookie
是在浏览器端保存键值对数据,而session
是在服务器端保存键值对数据- 重要敏感的数据(银行卡账号,验证码,余额等),建议存储在服务器端,不能通过cookie保存到浏览器
session
的使用依赖cookie
-
Django中
session
数据的保存- 生活例子
- session键值对数据保存
session
数据默认保存在django项目的一张数据库表中(表名为:django_session
),保存格式如下:
- 不同的用户使用各自不同的浏览器,可以认为:一个浏览器代表一个用户
- 【重要】表中一条记录,保存着一个浏览器(用户)所有的session键值对数据
- 【重要】
sessionid
是什么: 浏览器标识(用户标识),代表着一个用户,通过sessionid
可以找到该用户所有的session键值对数据
二、Session使用
- 开启session功能【默认已经开启】
django封装了session
模块,用来简化session
数据操作。请参见settings.py
配置文件中session配置:
INSTALLED_APPS = [
...
# 默认导入了django自带的session模块
'django.contrib.sessions',
]
MIDDLEWARE = [
...
# 开启session中间件
'django.contrib.sessions.middleware.SessionMiddleware',
]
-
生成django项目默认的数据库表
session
数据默认保存在django项目的一张数据库表中(表名为:django_session
)- 在保存session数据库前,需要先生成django项目默认的数据库表
- 如何生成: 打开终端,并进入到项目根目录下,再执行以下2个命令生成数据库表:
python manage.py makemigrations python manage.py migrate
-
session数据操作
request.session属性
:类型为 django.contrib.sessions.backends.db.SessionStore
- 保存session数据(键值对)
request.session['键']=值
- 读取session数据
request.session.get('键', 默认值)
- 删除命令
# 删除一个sessoin键值对(注意:键不存在会报错 `KeyError`)
del request.session['键']
# 清除当前访问用户所有的session数据
request.session.flush() # 删除一条表记录
request.session.clear() # 清空字段中的session键值对数据
- 设置session数据有效时间; 如果不设置,默认过期时间为两周
request.session.set_expiry(value)
* 如果value是一个整数,则 session数据 将在value秒没有活动后过期
* 如果value为0,则 session数据 将在用户 **关闭浏览器时过期**
* 如果value为None,则 session数据 将在 **2周后过期**
三、案例
-
需求:
- 使用session保存 登录用户名 和 验证码:
- 访问
http://127.0.0.1:8000/set_session
界面时,保存session数据 - 访问
http://127.0.0.1:8000/get_session
界面时,读取session数据
-
实现参考
# url配置
url(r'^set_session$', views.set_session),
url(r'^get_session$', views.get_session),
# 视图函数
def set_session(request):
"""保存session键值对数据"""
request.session['user_id'] = 10
request.session['user_name'] = 'admin'
return HttpResponse('保存session成功')
def get_session(request):
"""读取session键值对数据"""
user_id = request.session.get('user_id')
user_name = request.session.get('user_name')
text = 'user_id = %s, user_name = %s' % (user_id, user_name)
return HttpResponse(text)
测试:保存结果,需要作base64解码
四、保存session到redis中
使用第三方包 django-redis
实现**
- 在虚拟环境下安装包
pip install django-redis==4.8.0
- 修改
settings.py
文件,新增如下选项:
# django项目的缓存配置
CACHES = {
"default": {
"BACKEND": "django_redis.cache.RedisCache",
"LOCATION": "redis://127.0.0.1:6379/1",
"OPTIONS": {
"CLIENT_CLASS": "django_redis.client.DefaultClient",
"PASSWORD": ""
}
}
}
# session数据缓存到Redis中
SESSION_ENGINE = "django.contrib.sessions.backends.cache"
SESSION_CACHE_ALIAS = "default"
- 测试: 启动Redis服务器,登录一次,再查看session是否有保存到 redis 1号数据库中
类视图
学习目标
- 掌握Django中类视图的使用
- 掌握类视图中使用装饰器
- 能够使用类视图多继承和mixin扩展类
类视图
一、类视图
- 发帖功能
# url配置
urlpatterns = [
url(r'^post$', views.post), # 显示发帖界面
url(r'^do_post$', views.do_post), # 执行发帖操作
]
# 视图
def post(request):
"""get请求: 显示发帖界面"""
return render(request, 'post.html')
def do_post(request):
"""post请求: 执行发帖操作"""
title = request.POST.get('title')
content = request.POST.get('content')
return HttpResponse('发帖:title=%s, content=%s' % (title, content))
- 通过一个URL和视图同时实现登录功能的
GET
和POST
请求(注意:可能还有PUT
DELETE
等);
# url配置
urlpatterns = [
url(r'^post$', views.post), # 发帖功能
]
# 视图
def post(request):
"""发帖功能"""
if request.method == 'GET':
# get请求: 显示发帖界面
return render(request, 'post.html')
else:
# post请求: 执行发帖操作
title = request.POST.get('title')
content = request.POST.get('content')
return HttpResponse('发帖:title=%s, content=%s' % (title, content))
-
类视图 实现
-
以函数的方式定义的视图称为函数视图
-
在Django中还可以通过类来定义一个视图,称为类视图
-
类视图 的使用
- 定义一个类,继承Django提供的
View
类
from django.views.generic import View class PostView(View): def get(self, request): """get请求: 显示发帖界面""" return render(request, 'post2.html') def post(self, request): """post请求: 执行发帖操作""" # 代码简略 return HttpResponse('执行发帖操作')
- 定义一个类,继承Django提供的
-
2. 调用类视图的 as_view()
方法配置url
urlpatterns = [
...
# 类视图注册
url(r'^post2$', views.PostView.as_view()),
]
- 类视图优点:对于函数视图代码可读性和复用性更好
二、类视图原理
@classonlymethod
def as_view(cls, **initkwargs):
"""
Main entry point for a request-response process.
"""
...省略代码...
def view(request, *args, **kwargs):
self = cls(**initkwargs)
if hasattr(self, 'get') and not hasattr(self, 'head'):
self.head = self.get
self.request = request
self.args = args
self.kwargs = kwargs
# 调用dispatch方法,按照不同请求方式调用不同请求方法
return self.dispatch(request, *args, **kwargs)
...省略代码...
# 返回真正的函数视图
return view
def dispatch(self, request, *args, **kwargs):
# Try to dispatch to the right method; if a method doesn't exist,
# defer to the error handler. Also defer to the error handler if the
# request method isn't on the approved list.
if request.method.lower() in self.http_method_names:
handler = getattr(self, request.method.lower(), self.http_method_not_allowed)
else:
handler = self.http_method_not_allowed
return handler(request, *args, **kwargs)
三、类视图使用装饰器
1. 函数视图使用装饰器
需求: 实现禁止ip黑名单访问发帖界面。 解决: 可以通过在视图函数中使用装饰器实现,如下
- 为函数视图定义一个装饰器(在设计装饰器时,基本都以函数视图作为考虑的被装饰对象)
def check_ip(view_fun):
"""装饰器:禁止黑名单ip访问"""
def wrapper(request, *args, **kwargs):
# 在视图函数执行前做额外的操作:
# 禁止ip黑名单访问
IP = request.META.get('REMOTE_ADDR')
if IP in ['192.168.210.160']:
return HttpResponse('IP禁止访问')
return view_fun(request, *args, **kwargs)
return wrapper
- 给视图函数进行装饰
@check_ip
def post(request):
"""GET请求: 显示发帖界面"""
return render(request, 'post.html')
**或者:**也可以在URL中,通过方法调用的方式添加装饰器
urlpatterns = [
...
# 发帖功能
url(r'^post$', check_ip(views.post))
]
- 问题:代码可读性差,只看视图,不知道它添加了装饰器
2. 类视图中使用装饰器
方案一:在路由中添加
```python
urlpatterns = [
...
# 发帖功能
url(r'^post2$', check_ip(views.PostView.as_view()))
]
**方案二:在类视图中添加**
注意:**不能直接给类视图的方法添加装饰器**,需要使用**method_decorator**将其转换为适用于类视图方法的装饰器。
```python
# 方式二
# @method_decorator(check_ip, name='get') # 为特定的请求方法添加
# @method_decorator(check_ip, name='dispatch') # 为所有的请求方法添加
class PostView(View):
# 给所有的http方法都添加装饰器
# @method_decorator(check_ip)
def dispatch(self, request, *args, **kwargs):
return super().dispatch(request, *args, **kwargs)
# 方式一
@method_decorator(check_ip)
def get(self, request):
"""get请求:显示发帖界面"""
return render(request, 'post2.html')
def post(self, request):
"""post请求:执行发帖操作"""
# 代码简略
return HttpResponse('处理发帖操作')
**说明: 关于method_decorator装饰器作用:**为函数装饰器补充第一个self参数,以便让装饰器能应用到方法中。
3. 类视图多继承 & Mixin扩展类
使用面向对象多继承的特性,可以通过定义父类(作为扩展类),在父类中定义想要向类视图补充的方法,类视图继承这些扩展父类,便可实现代码复用。
定义的扩展父类名称通常以Mixin结尾。
举例如下:
class ListModelMixin(object):
"""
list扩展类
"""
def list(self, request, *args, **kwargs):
print('查询多条数据')
...
class CreateModelMixin(object):
"""
create扩展类
"""
def create(self, request, *args, **kwargs):
print('新增一条数据')
...
class DepartmentView(CreateModelMixin, ListModelMixin, View):
"""
同时继承两个扩展类,复用list和create方法
"""
def get(self, request):
self.list(request)
...
def post(self, request):
self.create(request)
...
class EmployeeView(CreateModelMixin, View):
"""
继承CreateModelMixin扩展类,复用create方法
"""
def post(self, request):
self.create(request)
...
中间件
学习目标
- 掌握Django中间件的使用
中间件
-
装饰器:不在改变原有函数的前提下,在函数调用之前或之后执行额外的操作
-
Django中间件:
- 一个轻量级、底层的插件系统,用于在视图函数调用之前或之后执行额外操作,在全局上修改Django的输入或输出;
官方文档–中间件
一、中间件使用
-
定义中间件类: 通过继承Django的
MiddlewareMixin
扩展类实现:-
__init__(self, get_response=None)
- 服务器启动,初始化中间件类时被调用,只执行一次
-
process_request(self, request)
:- 在视图执行之前调用,注意:该方法可以返回None或者response对象,如果返回response对象,则视图函数就不会再执行了
-
process_response(self, request, response)
:- 在视图执行之后调用,必须返回
response
对象
- 在视图执行之后调用,必须返回
-
-
在
setting.py
文件中的MIDDLEWARE
中注册
案例:
- 定义好中间件: 在项目中新建一个
middlewares.py
文件,然后在该文件中定义中间件类:
class MyMiddleware(MiddlewareMixin):
def __init__(self, get_response=None):
super().__init__(get_response)
print('init')
def process_request(self, request):
print('before 视图')
# 注意:可以返回None或者response对象,如果返回response对象,则视图函数就不会再执行了
def process_response(self, request, response):
print('after 视图')
return response
- 在settings.py 文件中添加注册中间件
MIDDLEWARE = [
'django.middleware.security.SecurityMiddleware',
...
'middlewares.MyMiddleware', # 注册中间件
]
- 定义一个视图进行测试
def index(request):
print('==index==')
return HttpResponse('hello django')
- 执行结果
init
before 视图
==index==
after 视图
注意:调试模式下 __init__
方法会执行两次
二、MiddlewareMixin
源码参考
class MiddlewareMixin(object):
def __init__(self, get_response=None):
self.get_response = get_response
super(MiddlewareMixin, self).__init__()
def __call__(self, request):
response = None
if hasattr(self, 'process_request'):
response = self.process_request(request)
if not response:
response = self.get_response(request)
if hasattr(self, 'process_response'):
response = self.process_response(request, response)
return response
三、多个中间件的执行顺序
示例
- 再定义一个中间件类
class MyMiddleware2(MiddlewareMixin):
def __init__(self, get_response=None):
super().__init__(get_response)
print('init 2')
def process_request(self, request):
print('before 视图 2')
def process_response(self, request, response):
print('after 视图 2')
return response
- 注册
MIDDLEWARE = [
'django.middleware.security.SecurityMiddleware',
...
'middlewares.MyMiddleware', # 注册中间件
'middlewares.MyMiddleware2',
]
- 执行结果
before 视图
before 视图 2
==index==
after 视图 2
after 视图
结论
- 对于视图之前执行的
process_request
方法,先 注册的中间件先执行 - 对于视图之后执行的
process_response
方法,后 注册的中间件先执行