Django视图

1.HttpRequest

  • 位置参数和关键字参数
  • 查询字符串
  • 请求体:表单数据,JSON数据
  • 请求头

2.HttpResponse

  • HttpResponse
  • JsonResponse
  • redirect

3.类视图

  • 类视图的定义和使用

视图介绍和项目准备

视图介绍

  • 视图就是应用views.py文件中的函数

  • 视图的第一个参数必须为

    1
    HttpRequest对象

    ,还可能包含下参数如

    • 通过正则表达式组获取的位置参数
    • 通过正则表达式组获得的关键字参数
  • 视图必须返回一个

    1
    HttpResponse对象

    1
    子对象

    作为响应

    • 子对象: JsonResponse HttpResponseRedirect
  • 视图负责接受Web请求

    1
    HttpRequest

    ,进行逻辑处理,返回Web响应

    1
    HttpResponse

    给请求者

    • 响应内容可以是HTML内容404错误重定向json数据
  • 视图处理过程如下图:

使用视图时需要进行两步操作,两步操作不分先后

  1. 配置URLconf
  2. 应用/views.py中定义视图

项目准备

创建项目+创建应用+安装应用+配置模板路径+本地化+mysql数据库+URLconf+视图

mysql数据库使用之前的book

1
2
3
4
5
6
7
8
9
10
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.mysql',
'HOST': '127.0.0.1', # 数据库主机
'PORT': 3306, # 数据库端口
'USER': 'root', # 数据库用户名
'PASSWORD': 'mysql', # 数据库用户密码
'NAME': 'book' # 数据库名字
}
}

URLconf

  • settings.py中:指定url配置

    1
    ROOT_URLCONF = 'bookmanager.urls'
  • 项目中urls.py:只要不是admin/就匹配成功,包含到应用中的urls.py

    1
    2
    3
    4
    5
    6
    7
    from django.contrib import admin
    from django.urls import path,include

    urlpatterns = [
    path('admin/', admin.site.urls),
    path('',include('book.urls'))
    ]

应用中urls.py:匹配testproject/成功就调用views中的testproject函数,测试项目逻辑

1
2
3
4
5
6
from django.urls import path
from book.views import index

urlpatterns = [
path('index/',index)
]

视图:测试项目逻辑

1
2
3
4
5
from django.http import HttpResponse

def index(request):

return HttpResponse("OK")

在models.py 文件中定义模型类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
from django.db import models

# Create your models here.
# 准备书籍列表信息的模型类
class BookInfo(models.Model):
# 创建字段,字段类型...
name = models.CharField(max_length=20, verbose_name='名称')
pub_date = models.DateField(verbose_name='发布日期',null=True)
readcount = models.IntegerField(default=0, verbose_name='阅读量')
commentcount = models.IntegerField(default=0, verbose_name='评论量')
is_delete = models.BooleanField(default=False, verbose_name='逻辑删除')

class Meta:
db_table = 'bookinfo' # 指明数据库表名
verbose_name = '图书' # 在admin站点中显示的名称

def __str__(self):
"""定义每个数据对象的显示信息"""
return self.name

# 准备人物列表信息的模型类
class PeopleInfo(models.Model):
GENDER_CHOICES = (
(0, 'male'),
(1, 'female')
)
name = models.CharField(max_length=20, verbose_name='名称')
gender = models.SmallIntegerField(choices=GENDER_CHOICES, default=0, verbose_name='性别')
description = models.CharField(max_length=200, null=True, verbose_name='描述信息')
book = models.ForeignKey(BookInfo, on_delete=models.CASCADE, verbose_name='图书') # 外键
is_delete = models.BooleanField(default=False, verbose_name='逻辑删除')

class Meta:
db_table = 'peopleinfo'
verbose_name = '人物信息'

def __str__(self):
return self.name

生成迁移文件

1
python manage.py makemigrations

同步到数据库中

1
python manage.py migrate

添加测试数据

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
insert into bookinfo(name, pub_date, readcount,commentcount, is_delete) values
('射雕英雄传', '1980-5-1', 12, 34, 0),
('天龙八部', '1986-7-24', 36, 40, 0),
('笑傲江湖', '1995-12-24', 20, 80, 0),
('雪山飞狐', '1987-11-11', 58, 24, 0);
insert into peopleinfo(name, gender, book_id, description, is_delete) values
('郭靖', 1, 1, '降龙十八掌', 0),
('黄蓉', 0, 1, '打狗棍法', 0),
('黄药师', 1, 1, '弹指神通', 0),
('欧阳锋', 1, 1, '蛤蟆功', 0),
('梅超风', 0, 1, '九阴白骨爪', 0),
('乔峰', 1, 2, '降龙十八掌', 0),
('段誉', 1, 2, '六脉神剑', 0),
('虚竹', 1, 2, '天山六阳掌', 0),
('王语嫣', 0, 2, '神仙姐姐', 0),
('令狐冲', 1, 3, '独孤九剑', 0),
('任盈盈', 0, 3, '弹琴', 0),
('岳不群', 1, 3, '华山剑法', 0),
('东方不败', 0, 3, '葵花宝典', 0),
('胡斐', 1, 4, '胡家刀法', 0),
('苗若兰', 0, 4, '黄衣', 0),
('程灵素', 0, 4, '医术', 0),
('袁紫衣', 0, 4, '六合拳', 0);

使用 PostMan 对请求进行测试

PostMan 是一款功能强大的网页调试与发送网页 HTTP 请求的 Chrome 插件,可以直接去对我们写出来的路由和视图函数进行调试,作为后端程序员是必须要知道的一个工具。

  • 使用 PostMan,打开之后,会弹出注册页面,选择下方的Skip this,go straight to the app进行程序

注意,在进行测试post请求时,我们现阶段暂时把settings.py设置下的'django.middleware.csrf.CsrfViewMiddleware',关掉。

HttpRequest对象

回想一下,利用HTTP协议向服务器传参有几种途径?

  • 提取URL的特定部分,如/weather/beijing/2018,可以在服务器端的路由中用正则表达式截取;
  • 查询字符串(query string),形如key1=value1&key2=value2;
  • 请求体(body)中发送的数据,比如表单数据、json、xml;
  • 在http报文的头(header)中。

URL路径参数

  • 如果想从URL中获取值http://127.0.0.1:8000/18/188/

  • 应用中urls.py

    1
    2
    3
    4
    5
    from django.urls import path
    from book.views import goods
    urlpatterns = [
    path('<cat_id>/<goods_id>/',goods)
    ]
  • 视图中函数: 参数的位置不能错

    1
    2
    3
    4
    from django.http import JsonResponse
    def goods(request,cat_id,id):

    return JsonResponse({'cat_id':cat_id,'id':id})

Django中的QueryDict对象

HttpRequest对象的属性GET、POST都是QueryDict类型的对象

与python字典不同,QueryDict类型的对象用来处理同一个键带有多个值的情况

  • 方法get():根据键获取值

    如果一个键同时拥有多个值将获取最后一个值

    如果键不存在则返回None值,可以设置默认值进行后续处理

    1
    get('键',默认值)
  • 方法getlist():根据键获取值,值以列表返回,可以获取指定键的所有值

    如果键不存在则返回空列表[],可以设置默认值进行后续处理

    1
    getlist('键',默认值)

查询字符串Query String

获取请求路径中的查询字符串参数(形如?k1=v1&k2=v2),可以通过request.GET属性获取,返回QueryDict对象。

1
2
3
4
5
6
7
8
9
10
# /get/?a=1&b=2&a=3

def get(request):
a = request.GET.get('a')
b = request.GET.get('b')
alist = request.GET.getlist('a')
print(a) # 3
print(b) # 2
print(alist) # ['1', '3']
return HttpResponse('OK')

重要:查询字符串不区分请求方式,即假使客户端进行POST方式的请求,依然可以通过request.GET获取请求中的查询字符串数据。

请求体

请求体数据格式不固定,可以是表单类型字符串,可以是JSON字符串,可以是XML字符串,应区别对待。

可以发送请求体数据的请求方式有POSTPUTPATCHDELETE

Django默认开启了CSRF防护,会对上述请求方式进行CSRF防护验证,在测试时可以关闭CSRF防护机制,方法为在settings.py文件中注释掉CSRF中间件,如:

表单类型 Form Data

前端发送的表单类型的请求体数据,可以通过request.POST属性获取,返回QueryDict对象。

1
2
3
4
5
6
7
8
def post(request):
a = request.POST.get('a')
b = request.POST.get('b')
alist = request.POST.getlist('a')
print(a)
print(b)
print(alist)
return HttpResponse('OK')

非表单类型 Non-Form Data

非表单类型的请求体数据,Django无法自动解析,可以通过request.body属性获取最原始的请求体数据,自己按照请求体格式(JSON、XML等)进行解析。request.body返回bytes类型。

例如要获取请求体中的如下JSON数据

1
{"a": 1, "b": 2}

可以进行如下方法操作:

1
2
3
4
5
6
7
8
9
import json

def post_json(request):
json_str = request.body
json_str = json_str.decode() # python3.6 无需执行此步
req_data = json.loads(json_str)
print(req_data['a'])
print(req_data['b'])
return HttpResponse('OK')

验证path中路径参数

系统为我们提供了一些路由转换器位置在django.urls.converters.py

1
2
3
4
5
6
7
DEFAULT_CONVERTERS = {
'int': IntConverter(), # 匹配正整数,包含0
'path': PathConverter(), # 匹配任何非空字符串,包含了路径分隔符
'slug': SlugConverter(), # 匹配字母、数字以及横杠、下划线组成的字符串
'str': StringConverter(), # 匹配除了路径分隔符(/)之外的非空字符串,这是默认的形式
'uuid': UUIDConverter(), # 匹配格式化的uuid,如 075194d3-6885-417e-a8a8-6c931e272f00
}

我们可以通过以下形式来验证数据的类型

1
path('<int:cat_id>/<int:id>/',goods),

自定义转换器

http://127.0.0.1:8000/18500001111/默认的路由转换器中,没有专门用来匹配手机号的路由转换器。所以在使用path()实现需求时,就无法直接使用默认的路由转换器。如果默认的路由转换器无法满足需求时,我们就需要自定义路由转换器。在任意可以被导入的python文件中,都可以自定义路由转换器。

  • 比如:在工程根目录下,新建converters.py文件,用于自定义路由转换器

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    class MobileConverter:
    """自定义路由转换器:匹配手机号"""
    # 匹配手机号码的正则
    regex = '1[3-9]\d{9}'

    def to_python(self, value):
    # 将匹配结果传递到视图内部时使用
    return int(value)

    def to_url(self, value):
    # 将匹配结果用于反向解析传值时使用
    return str(value)
  • 注册自定义路由转换器

    • 在总路由中,注册自定义路由转换器

      1
      2
      3
      4
      5
      6
      from django.urls import register_converter
      # 注册自定义路由转换器
      # register_converter(自定义路由转换器, '别名')
      register_converter(MobileConverter, 'mobile')

      urlpatterns = []
  • 使用自定义路由转换器

    1
    2
    # 测试path()中自定义路由转换器提取路径参数:手机号 http://127.0.0.1:8000/18500001111/
    path('<mobile:phone>/',register)

请求头

可以通过request.META属性获取请求头headers中的数据,request.META为字典类型

常见的请求头如:

  • CONTENT_LENGTH– The length of the request body (as a string).
  • CONTENT_TYPE– The MIME type of the request body.
  • HTTP_ACCEPT– Acceptable content types for the response.
  • HTTP_ACCEPT_ENCODING– Acceptable encodings for the response.
  • HTTP_ACCEPT_LANGUAGE– Acceptable languages for the response.
  • HTTP_HOST– The HTTP Host header sent by the client.
  • HTTP_REFERER– The referring page, if any.
  • HTTP_USER_AGENT– The client’s user-agent string.
  • QUERY_STRING– The query string, as a single (unparsed) string.
  • REMOTE_ADDR– The IP address of the client.
  • REMOTE_HOST– The hostname of the client.
  • REMOTE_USER– The user authenticated by the Web server, if any.
  • REQUEST_METHOD– A string such as"GET"or"POST".
  • SERVER_NAME– The hostname of the server.
  • SERVER_PORT– The port of the server (as a string).

具体使用如:

1
2
3
def get_headers(request):
print(request.META['CONTENT_TYPE'])
return HttpResponse('OK')

其他常用HttpRequest对象属性

  • method:一个字符串,表示请求使用的HTTP方法,常用值包括:’GET’、’POST’。
  • user:请求的用户对象。
  • path:一个字符串,表示请求的页面的完整路径,不包含域名和参数部分。
  • encoding:一个字符串,表示提交的数据的编码方式。
    • 如果为None则表示使用浏览器的默认设置,一般为utf-8。
    • 这个属性是可写的,可以通过修改它来修改访问表单数据使用的编码,接下来对属性的任何访问将使用新的encoding值。
  • FILES:一个类似于字典的对象,包含所有的上传文件。

HttpResponse对象

视图在接收请求并处理后,必须返回HttpResponse对象或子对象。HttpRequest对象由Django创建,HttpResponse对象由开发人员创建。

HttpResponse

可以使用django.http.HttpResponse来构造响应对象。

1
HttpResponse(content=响应体, content_type=响应体数据类型, status=状态码)

也可通过HttpResponse对象属性来设置响应体、响应体数据类型、状态码:

  • content:表示返回的内容。
  • status_code:返回的HTTP响应状态码。

响应头可以直接将HttpResponse对象当做字典进行响应头键值对的设置:

1
2
response = HttpResponse()
response['itcast'] = 'Python' # 自定义响应头Itcast, 值为Python

示例:

1
2
3
4
5
6
7
8
9
from django.http import HttpResponse

def response(request):
return HttpResponse('itcast python', status=400)
或者
response = HttpResponse('itcast python')
response.status_code = 400
response['itcast'] = 'Python'
return response

HttpResponse子类

Django提供了一系列HttpResponse的子类,可以快速设置状态码

  • HttpResponseRedirect 301
  • HttpResponsePermanentRedirect 302
  • HttpResponseNotModified 304
  • HttpResponseBadRequest 400
  • HttpResponseNotFound 404
  • HttpResponseForbidden 403
  • HttpResponseNotAllowed 405
  • HttpResponseGone 410
  • HttpResponseServerError 500

JsonResponse

若要返回json数据,可以使用JsonResponse来构造响应对象,作用:

  • 帮助我们将数据转换为json字符串
  • 设置响应头Content-Typeapplication/json
1
2
3
4
from django.http import JsonResponse

def response(request):
return JsonResponse({'city': 'beijing', 'subject': 'python'})

redirect重定向

1
2
3
4
from django.shortcuts import redirect

def response(request):
return redirect('/get_header')

状态保持

  • 浏览器请求服务器是无状态的。
  • 无状态:指一次用户请求时,浏览器、服务器无法知道之前这个用户做过什么,每次请求都是一次新的请求。
  • 无状态原因:浏览器与服务器是使用Socket套接字进行通信的,服务器将请求结果返回给浏览器之后,会关闭当前的Socket连接,而且服务器也会在处理页面完毕之后销毁页面对象。
  • 有时需要保持下来用户浏览的状态,比如用户是否登录过,浏览过哪些商品等
  • 实现状态保持主要有两种方式:
    • 在客户端存储信息使用Cookie
    • 在服务器端存储信息使用Session

Cookie,有时也用其复数形式Cookies,指某些网站为了辨别用户身份、进行session跟踪而储存在用户本地终端上的数据(通常经过加密)。Cookie最早是网景公司的前雇员Lou Montulli在1993年3月的发明。Cookie是由服务器端生成,发送给User-Agent(一般是浏览器),浏览器会将Cookie的key/value保存到某个目录下的文本文件内,下次请求同一网站时就发送该Cookie给服务器(前提是浏览器设置为启用cookie)。Cookie名称和值可以由服务器端开发自己定义,这样服务器可以知道该用户是否是合法用户以及是否需要重新登录等。服务器可以利用Cookies包含信息的任意性来筛选并经常性维护这些信息,以判断在HTTP传输中的状态。Cookies最典型记住用户名。

Cookie是存储在浏览器中的一段纯文本信息,建议不要存储敏感信息如密码,因为电脑上的浏览器可能被其它人使用。

Cookie的特点

  • Cookie以键值对的格式进行信息的存储。
  • Cookie基于域名安全,不同域名的Cookie是不能互相访问的,如访问itcast.cn时向浏览器中写了Cookie信息,使用同一浏览器访问baidu.com时,无法访问到itcast.cn写的Cookie信息。
  • 当浏览器请求某网站时,会将浏览器存储的跟网站相关的所有Cookie信息提交给网站服务器。

设置Cookie

可以通过HttpResponse对象中的set_cookie方法来设置cookie。

1
HttpResponse.set_cookie(cookie名, value=cookie值, max_age=cookie有效期)
  • max_age单位为秒,默认为None 。如果是临时cookie,可将max_age设置为None。

示例:

1
2
3
4
5
def cookie(request):
response = HttpResponse('ok')
response.set_cookie('itcast1', 'python1') # 临时cookie
response.set_cookie('itcast2', 'python2', max_age=3600) # 有效期一小时
return response

读取Cookie

可以通过HttpResponse对象的COOKIES属性来读取本次请求携带的cookie值。request.COOKIES为字典类型

1
2
3
4
def cookie(request):
cookie1 = request.COOKIES.get('itcast1')
print(cookie1)
return HttpResponse('OK')

删除Cookie

可以通过HttpResponse对象中的delete_cookie方法来删除。

1
response.delete_cookie('itcast2')

Session

启用Session

Django项目默认启用Session。

可以在settings.py文件中查看,如图所示

如需禁用session,将上图中的session中间件注释掉即可。

存储方式

在settings.py文件中,可以设置session数据的存储方式,可以保存在数据库、本地缓存等。

数据库

存储在数据库中,如下设置可以写,也可以不写,这是默认存储方式

1
SESSION_ENGINE='django.contrib.sessions.backends.db'

如果存储在数据库中,需要在项INSTALLED_APPS中安装Session应用。

数据库中的表如图所示

表结构如下

由表结构可知,操作Session包括三个数据:键,值,过期时间。

本地缓存

存储在本机内存中,如果丢失则不能找回,比数据库的方式读写更快。

1
SESSION_ENGINE='django.contrib.sessions.backends.cache'
混合存储

优先从本机内存中存取,如果没有则从数据库中存取。

1
SESSION_ENGINE='django.contrib.sessions.backends.cached_db'
Redis

在redis中保存session,需要引入第三方扩展,我们可以使用django-redis来解决。

文档

1) 安装扩展

1
pip install django-redis

2)配置

在settings.py文件中做如下设置

1
2
3
4
5
6
7
8
9
10
11
CACHES = {
'default': {
'BACKEND': 'django_redis.cache.RedisCache',
'LOCATION': 'redis://127.0.0.1:6379/1',
'OPTIONS': {
'CLIENT_CLASS': 'django_redis.client.DefaultClient',
}
}
}
SESSION_ENGINE = 'django.contrib.sessions.backends.cache'
SESSION_CACHE_ALIAS = 'default'
注意

如果redis的ip地址不是本地回环127.0.0.1,而是其他地址,访问Django时,可能出现Redis连接错误,如下:

解决方法:

修改redis的配置文件,添加特定ip地址。

打开redis的配置文件

1
sudo vim /etc/redis/redis.conf

在如下配置项进行修改(如要添加10.211.55.5地址)

重新启动redis服务

1
sudo service redis-server restart

Session操作

通过HttpRequest对象的session属性进行会话的读写操作。

1) 以键值对的格式写session。

1
request.session['键']=值

2)根据键读取值。

1
request.session.get('键',默认值)

3)清除所有session,在存储中删除值部分。

1
request.session.clear()

4)清除session数据,在存储中删除session的整条数据。

1
request.session.flush()

5)删除session中的指定键及值,在存储中只删除某个键及对应的值。

1
del request.session['键']

6)设置session的有效期

1
request.session.set_expiry(value)
  • 如果value是一个整数,session将在value秒没有活动后过期。
  • 如果value为0,那么用户session的Cookie将在用户的浏览器关闭时过期。
  • 如果value为None,那么session有效期将采用系统默认值, 默认为两周,可以通过在settings.py中设置SESSION_COOKIE_AGE来设置全局默认值。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
# session 是保存在服务器端 -- 数据相对安全
# session需要依赖于cookie

"""

第一次请求 http://127.0.0.1:8000/set_session/?username=itheima 。我们在服务器端设置sesison信息
服务器同时会生成一个sessionid的cookie信息。
浏览器接收到这个信息之后,会把cookie数据保存起来


第二次及其之后的请求 都会携带这个sessionid. 服务器会验证这个sessionid. 验证没有问题会读取相关数据。实现业务逻辑

"""

def set_session(request):

# 1. 模拟 获取用户信息
username=request.GET.get('username')

# 2. 设置session信息
# 假如 我们通过模型查询 查询到了 用户的信息
user_id=1

request.session['user_id']=user_id
request.session['username']=username


# clear 删除session里的数据,但是 key有保留
# request.session.clear()
# flush 是删除所有的数据,包括key
# request.session.flush()

request.session.set_expiry(3600)

return HttpResponse("set_session")


def get_session(request):

# user_id=request.session['user_id']
# username=request.session['username']

user_id=request.session.get('user_id')
username=request.session.get('username')

# '%s'%username
content = '{} ,{}'.format(user_id,username)

return HttpResponse(content)

类视图与中间件

类视图

思考:一个视图,是否可以处理两种逻辑?比如get和post请求逻辑。

如何在一个视图中处理get和post请求

注册视图处理get和post请求

以函数的方式定义的视图称为函数视图,函数视图便于理解。但是遇到一个视图对应的路径提供了多种不同HTTP请求方式的支持时,便需要在一个函数中编写不同的业务逻辑,代码可读性与复用性都不佳。

1
2
3
4
5
6
7
8
9
10
def register(request):
"""处理注册"""

# 获取请求方法,判断是GET/POST请求
if request.method == 'GET':
# 处理GET请求,返回注册页面
return render(request, 'register.html')
else:
# 处理POST请求,实现注册逻辑
return HttpResponse('这里实现注册逻辑')

类视图使用

在Django中也可以使用类来定义一个视图,称为类视图

使用类视图可以将视图对应的不同请求方式以类中的不同方法来区别定义。如下所示

1
2
3
4
5
6
7
8
9
10
11
12
from django.views.generic import View

class RegisterView(View):
"""类视图:处理注册"""

def get(self, request):
"""处理GET请求,返回注册页面"""
return render(request, 'register.html')

def post(self, request):
"""处理POST请求,实现注册逻辑"""
return HttpResponse('这里实现注册逻辑')

类视图的好处:

  • 代码可读性好
  • 类视图相对于函数视图有更高的复用性 , 如果其他地方需要用到某个类视图的某个特定逻辑,直接继承该类视图即可

定义类视图需要继承自Django提供的父类View,可使用from django.views.generic import View或者from django.views.generic.base import View导入,定义方式如上所示。

配置路由时,使用类视图的as_view()方法来添加

1
2
3
4
5
6
urlpatterns = [
# 视图函数:注册
# path('register/', views.register),
# 类视图:注册
path('register/', views.RegisterView.as_view()),
]

类视图原理(了解)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
@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)

类视图的多继承重写dispatch

1
2
3
4
5
6
7
class CenterView(View):

def get(self,request):
return HttpResponse("OK")

def post(self,request):
return HttpResponse("OK")

使用面向对象多继承的特性。

1
2
3
4
5
6
7
class CenterView(LoginRequireMixin,View):

def get(self,request):
return HttpResponse("OK")

def post(self,request):
return HttpResponse("OK")

注意:MRO的模式,就是注意先后顺序LoginRequireMixin到View,在LoginRequireMixin类里面的super().dispatch会将后面的View类的dispatch()调用。交换顺序则不行。

中间件

Django中的中间件是一个轻量级、底层的插件系统,可以介入Django的请求和响应处理过程,修改Django的输入或输出。中间件的设计为开发者提供了一种无侵入式的开发方式,增强了Django框架的健壮性。

我们可以使用中间件,在Django处理视图的不同阶段对输入或输出进行干预。

中间件文档

中间件的定义方法

  • Django在中间件中预置了六个方法,这六个方法会在不同的阶段自动执行,对输入或输出进行干预。
初始化方法:
  • 启动Django程序,初始化中间件时,自动调用一次,用于确定是否启用当前中间件

    1
    2
    def __init__(self, get_response=None):
    pass
处理请求前的方法:(重要)
  • 在处理每个请求前,自动调用,返回None或HttpResponse对象

    1
    2
    def process_request(self, request):
    pass
处理视图前的方法:(重要)
  • 在处理每个视图前,自动调用,返回None或HttpResponse对象

    1
    2
    def process_view(self, request, view_func, view_args, view_kwargs):
    pass
处理模板响应前的方法:
  • 在处理每个模板响应前,自动调用,返回实现了render方法的响应对象

    1
    2
    def process_template_response(self, request, response):
    pass
处理响应后的方法:(重要)
  • 在每个响应返回给客户端之前,自动调用,返回HttpResponse对象

    1
    2
    def process_response(self, request, response):
    pass
异常处理:
  • 当视图抛出异常时,自动调用,返回一个HttpResponse对象

    1
    2
    def process_exception(self, request,exception):
    pass

定义中间件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
# 导入中间件的父类
from django.utils.deprecation import MiddlewareMixin


class TestMiddleware1(MiddlewareMixin):
"""自定义中间件"""
def process_request(self, request):
"""处理请求前自动调用"""
print('process_request1 被调用')

def process_view(self, request, view_func, view_args, view_kwargs):
# 处理视图前自动调用
print('process_view1 被调用')

def process_response(self, request, response):
"""在每个响应返回给客户端之前自动调用"""
print('process_response1 被调用')
return response

定义好中间件后,需要在settings.py 文件中添加注册中间件

1
2
3
4
5
6
7
8
9
10
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',
'book.middleware.TestMiddleware1', # 添加中间件
]

定义一个视图进行测试

1
2
3
def middleware(request):
print('view 视图被调用')
return HttpResponse('OK')

执行结果

1
2
3
process_request1 被调用
process_view1 被调用
process_response1 被调用

多个中间件的执行顺序

  • 在请求视图被处理,中间件由上至下依次执行
  • 在请求视图被处理,中间件由下至上依次执行

示例:

定义两个中间件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
from django.utils.deprecation import MiddlewareMixin


class TestMiddleware1(MiddlewareMixin):
"""自定义中间件"""
def process_request(self, request):
"""处理请求前自动调用"""
print('process_request1 被调用')

def process_view(self, request, view_func, view_args, view_kwargs):
# 处理视图前自动调用
print('process_view1 被调用')

def process_response(self, request, response):
"""在每个响应返回给客户端之前自动调用"""
print('process_response1 被调用')
return response


class TestMiddleware2(MiddlewareMixin):
"""自定义中间件"""
def process_request(self, request):
"""处理请求前自动调用"""
print('process_request2 被调用')

def process_view(self, request, view_func, view_args, view_kwargs):
# 处理视图前自动调用
print('process_view2 被调用')

def process_response(self, request, response):
"""在每个响应返回给客户端之前自动调用"""
print('process_response2 被调用')
return response

注册添加两个中间件

1
2
3
4
5
6
7
8
9
10
11
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',
'book.middleware.TestMiddleware1', # 添加
'book.middleware.TestMiddleware2', # 添加
]

执行结果

1
2
3
4
5
6
process_request1 被调用
process_request2 被调用
process_view1 被调用
process_view2 被调用
process_response2 被调用
process_response1 被调用

本博客所有文章除特别声明外,均采用 CC BY-SA 3.0协议 。转载请注明出处!