1. 视图介绍
- 视图就是应用中的 views.py 文件中的函数
- 视图的第一个参数必须为HttpRequest对象,还可能包含以下参数:
- 通过正则表达式组获取的参数位置
- 通过正则表达式组获得的关键字参数
- 视图必须返回一个HttpResponse对象或其子对象作为响应
- 常用子对象 JsonResponse、HttpResponseRedirect
- 视图负责接收Web请求(HttpRequest),进行逻辑处理,返回Web响应(HttpResponse)
- 响应内容可以是HTML内容(render渲染)、404错误、重定向、json数据…
- 视图的处理过程如下图:
在使用视图时需要进行两部操作,这两部操作不分先后
- 配置URLConf
- 在应用/views.py 中定义视图
2. URLCOnf
- 浏览者通过在浏览器的地址栏中输入网址请求网站
- 对于Django开发的网站,由哪一个视图进行处理请求,是由url匹配找到的
配置URLConf
1) 在settings.py 中
- 配置 ROOT_URLCONF,指定url配置位置
ROOT_URLCONF = '项目.urls'
**2) 在项目中urls.py **
- 配置匹配规则,如匹配成功后包含应用的urls.py
url(正则, include('应用.urls'))
3) 在具体应用中urls.py
- 匹配url-view匹配规则,匹配成功后调用 views.py对应的函数
url(正则, views.函数名)
4) 注意事项
- 正则部分推荐使用 r,表示字符串不转义,这样在正则表达式中使用 \ 只写一个就可以
- 不能在开始加反斜杠,推荐在结束加反斜杠
正确:path/
正确:path
错误:/path
错误:/path/- 需导入具体子应用的正则,注意不要加正则结束符号$
- 请求的url被看做是一个普通的python字符串,进行匹配时不包括域名、get或post参数
如请求地址如下:
http://127.0.0.1:8000/18/?a=10
去掉域名和参数部分后,只剩下如下部分与正则匹配
18/
说明:
虽然路由结尾带/能带来上述好处,但是却违背了HTTP中URL表示资源位置路径的设计理念。
是否结尾带/ 视具体情况而定。
3. 路由命名及reverse()反解析
3.1 路由命名
在定义路由的时候,可以为路由命名,方便查找特定视图的具体路径信息,或重定向时反解析路由。
- 1)在使用include函数定义路由时,可以定义路由的命名空间namespace.
- 现在有的版本需要在指定namespace时,同时指定app_name
- app_name的指定可以在url(正则,include((urls,app_name), name_space))中指定,也可以在相应的视图函数中定义一个app_name的属性
# BMS/urls.py
urlpatterns = [
# path('admin/', admin.site.urls),
url(r'^admin/', admin.site.urls),
# 导入子应用视图 此时访问 book/xxx 的url都会去找 book.urls 下定义的路由
# 注意,如果指定了namespace,则需要在include中指定应用名app_name(或在对应的urls.py文件中定义此属性)。即传递include((urlconf_model,app_name), name_space)
url(r'^', include(('book.urls', 'book'), namespace='book'))
]
# book/views.py
app_name = 'book' # 也可以在views中指定应用名app_name
命名空间表示,凡是book.urls中定义的路由,均属于namespace指明的book名下。
命名空间的作用:避免不同应用中的路由使用了相同的名字发生冲突,使用命名空间区别开。
- 2)在定义普通路由时,可以使用name参数指明路由的名字
# book/urls.py
urlpatterns = [
url(r'^test/$', views.test, name='test'),
]
3.2 reverse反解析
使用reverse函数,可以根据路由名称,返回具体的路径,如:
# book/views.py
def to_test(request):
# reverse('book:test') # 反解析 命名空间 book 下的名字为test的路由
return redirect(to=reverse('book:test'))
- 对于未指明namespace的,reverse(路由name)
- 对于指明namespace的,reverse(命名空间namespace:路由name)
4. HttpRequest对象
利用HTTP协议向服务器传参有几种途径:
- 提取URL的特定部分,如/weather/beijing/2018,可以在服务器端的路由中用正则表达式截取;
- 查询字符串(query string),形如key1=value1&key2=value2;
- 请求体(body)中发送的数据,比如表单数据、json、xml;
- 在http报文的头(header)中。
4.1 URL路径参数
- 如果想从URL中获取值,需要在正则表达式中使用 分组
- 获取值分为两种方式
- 位置参数: 参数位置不能错
- 关键字参数: 参数位置可变,但关键字必须一致
- 注意:两种参数方式不要混合使用,在一个正则表达式中只能使用一种参数方式
例:分别使用以上两种获取URL值的方式获取 2021 12
http://localhost:8000/2021/12
1) 位置参数
- 应用中urls.py
# book/urls.py
urlpatterns = [
url(r'^(\d+)/(\d+)/$', views.index, name='index')
]
- 视图函数中,参数位置不能出错
# book/views.py
def index(request, v1, v2):
return render(request, 'book/index.html', context={'p1:': v1, 'p2:':v2})
2) 关键字参数
- 应用urls.py
# book/urls.py
urlpatterns = [
url(r'^(?P<p1>\d+)/(?P<p2>\d+)/$', views.index, name='index')
]
- 视图函数中,参数名称和url中保持一致
# book/views.py
def index(request, p2, p1):
return render(request, 'book/index.html', context={'p1:': p1, 'p2:':p2})
4.2 查询字符串Query String
1) Django中的QueryDict对象
HttpRequest对象的GET、POST属性都是QueryDict类型的对象
与Python字典不同,QueryDict类型的对象可用来处理同一个键带有多个值的情况
- 方法get(‘键’, 默认值) : 根据键获取值,如果一个键有多个值将返回最后一个,如果键不存在则返回默认值(没设置就返回None)
- 方法getlist(‘键’, 默认值) : 根据键获取值,值已列表方式返回,如果不存在返回默认值(没设置就返回[])
2) 查询字符串Query String的获取
获取请求路径中的查询字符串参数(形如?k1=v1&k2=v2),可以通过request.GET属性获取,返回QueryDict对象。
# /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获取请求中的查询字符串数据。
4.3 请求体参数获取
请求体数据格式不固定,可以是表单类型字符串,可以是JSON字符串,可以是XML字符串,应区别对待。
可以发送请求体数据的请求方式有POST、PUT、PATCH、DELETE。
Django默认开启了CSRF防护,会对上述请求方式进行CSRF防护验证,在测试时可以关闭CSRF防护机制,方法为在settings.py文件中注释掉CSRF中间件,如:
1) 表单类型 Form Data 参数获取
前端发送的表单类型的请求体数据,可以通过request.POST属性获取,返回QueryDict对象。
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')
2) 非表单类型 Non-Form Data 参数获取
非表单类型的请求体数据,Django无法自动解析,可以通过request.body属性获取最原始的请求体数据,自己按照请求体格式(JSON、XML等)进行解析。request.body返回bytes类型。
例如要获取请求体中的如下JSON数据
{“a”: 1, “b”: 2}
def post_json(request):
json_str = request.body
json_str = json_str.decode() # python3.6 无需执行此步
req_data = json.loads(json_str)
4.4 请求头
可以通过request.META属性获取请求头headers中的数据,request.META为字典类型。
常见的请求头:
或见官方文档
def get_headers(request):
print(request.META['CONTENT_TYPE'])
return HttpResponse('OK')
4.5 其他request对象
详情见官方文档
-
method:一个字符串,表示请求使用的HTTP方法,常用值包括:‘GET’、‘POST’。
-
user:请求的用户对象。
-
path:一个字符串,表示请求的页面的完整路径,不包含域名和参数部分。
-
encoding:一个字符串,表示提交的数据的编码方式。
- 如果为None则表示使用浏览器的默认设置,一般为utf-8。
- 这个属性是可写的,可以通过修改它来修改访问表单数据使用的编码,接下来对属性的任何访问将使用新的encoding值。
-
FILES:一个类似于字典的对象,包含所有的上传文件。
5. HttpResponse对象
视图在接收请求并处理后,必须返回HttpResponse对象或子对象。HttpRequest对象由Django创建,HttpResponse对象由开发人员创建。
1) HttpResponse
可以使用django.http.HttpResponse来构造响应对象。
HttpResponse(content=响应体, content_type=响应体数据类型, status=状态码)
也可通过HttpResponse对象属性来设置响应体、响应体数据类型、状态码:
- content:表示返回的内容。
- status_code:返回的HTTP响应状态码。
响应头可以直接将HttpResponse对象当做字典进行响应头键值对的设置:
response = HttpResponse()
response['python'] = 'Django' # 自定义响应头python, 值为Django
示例:
from django.http import HttpResponse
def response(request):
# return HttpResponse('Test httpResponse', status=400)
# 或者
response = HttpResponse('Test httpResponse')
response.status_code = 400
response['python'] = 'Django'
return response
2) HttpResponse子类
Django提供了一系列HttpResponse的子类,可以快速设置状态码
- HttpResponseRedirect 301
- HttpResponsePermanentRedirect 302
- HttpResponseNotModified 304
- HttpResponseBadRequest 400
- HttpResponseNotFound 404
- HttpResponseForbidden 403
- HttpResponseNotAllowed 405
- HttpResponseGone 410
- HttpResponseServerError 500
3) JsonResponse
若要返回json数据,可以使用JsonResponse来构造响应对象,作用:
- 帮助我们将数据转换为json字符串
- 设置响应头Content-Type为application/json
def response(request):
return JsonResponse({'city': 'beijing', 'subject': 'python'})
4) redirect重定向
from django.shortcuts import redirect
def response(request):
return redirect('/get_header')
6. cookie
1) Cookie的特点
- Cookie以键值对的格式进行信息的存储。
- Cookie基于域名安全,不同域名的Cookie是不能互相访问的,如访问taobao.com时向浏览器中写了Cookie信息,使用同一浏览器访问baidu.com时,无法访问到taobao.com写的Cookie信息。
- 当浏览器请求某网站时,会将浏览器存储的跟网站相关的所有Cookie信息提交给网站服务器。
2) 设置cookie
可以通过HttpResponse对象中的set_cookie方法来设置cookie。
# max_age单位为秒,默认为None 。如果是临时cookie,可将max_age设置为None。
HttpResponse.set_cookie(cookie名, value=cookie值, max_age=cookie有效期)
- max_age单位为秒,默认为 None 。如果是临时cookie,可将max_age设置为None。
示例
def cookie(request):
response = HttpResponse('ok')
response.set_cookie('cookie1', 'python1') # 临时cookie
response.set_cookie('cookie2', 'python2', max_age=3600) # 有效期一小时
return response
3) 读取cookie
可以通过HttpRequest对象的COOKIES属性来读取本次请求携带的cookie值。request.COOKIES为字典类型。
示例:
def cookie(request):
cookie2 = request.COOKIES.get('cookie2')
print(cookie2)
return HttpResponse('OK')
3) 删除cookie
可以通过HttpResponse对象中的delete_cookie方法来删除。
示例:
def del_cookie(request):
response = HttpResponse('ok')
response.delete_cookie('cookie2')
return response
7. session
7.1 启用session
Django项目默认启用Session。
可以在settings.py文件中查看,如图所示
如需禁用session,将上图中的session中间件注释掉即可。
7.2 session 存储方式
- 1) 数据库存储
存储在数据库中,如下设置可以写,也可以不写,这是默认存储方式。
# django/conf/global_sessings.py
SESSION_ENGINE = 'django.contrib.sessions.backends.db'
如果存储在数据库中,需要在项INSTALLED_APPS中安装Session应用。
数据表为django_session
表django_session的表结构为:
由表结构可知,操作Session包括三个数据:键,值,过期时间。
- 2) 本地缓存
存储在本机内存中,如果丢失则不能找回,比数据库的方式读写更快。
需在settings.py中配置
# 项目/settings.py
SESSION_ENGINE = 'django.contrib.sessions.backends.cache'
- 3) 混合存储
优先从本机内存中存取,如果没有则从数据库中存取。
需在settings.py中配置
# 项目/settings.py
SESSION_ENGINE='django.contrib.sessions.backends.cached_db'
- 4) Redis
在redis中保存session,需要引入第三方扩展,我们可以使用django-redis来解决。
需要:
安装扩展
pip install django-redis
在settings.py中配置
# 项目/settings.py
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 不能连接,需要自己去配置一下redis/redis.conf 的ip地址绑定
7.3 Session相关操作
1) 以键值对的格式写session。
httpRequest.session['key'] = value
2)根据键读取值。
httpRequest.get_session('key', 'defalut_value')
3)清除所有session,在存储中删除值部分。
httpRequest.session.clear()
4)清除session数据,在存储中删除session的整条数据。
httpRequest.session.flush()
5)删除session中的指定键及值,并清除存储中这条session值的部分。
del httpRequest.session['key']
6)设置session的有效期
request.session.set_expiry(value)
- 如果value是一个整数,session将在value秒没有活动后过期。
- 如果value为0,那么用户session的Cookie将在用户的浏览器关闭时过期。
- 如果value为None,那么session有效期将采用系统默认值, 默认为两周,可以通过在settings.py中设置SESSION_COOKIE_AGE来设置全局默认值。
8. 类视图
8.1 类视图使用
在Django中也可以使用类来定义一个视图,称为类视图。
使用类视图可以将视图对应的不同请求方式以类中的不同方法来区别定义。如下所示
# 类视图的定义
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()**方法来添加。
# 配置路由时,使用类视图的as_view()方法来添加。
urlpatterns = [
# 视图函数:注册
# url(r'^register/$', views.register, name='register'),
# 类视图:注册
url(r'^register/$', views.RegisterView.as_view(), name='register'),
]
8.2 类视图的原理
# 类视图的原理 : django.views.generic.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)
一般可以使用python的多继承,重新dispatch方法来实现一些如登录验证等操作。
如:
class LoginRequireMixin:
def dispatch(self, request, *args, **kwargs):
u = request.COOKIES.get('username', None)
if u is None:
return redirect('/lg/to_login/?next=%s' % request.path)
return super().dispatch(request, *args, **kwargs)
class CenterView(LoginRequireMixin,View):
def get(self,request):
return HttpResponse("OK")
def post(self,request):
return HttpResponse("OK")
9. 中间件
Django中的中间件是一个轻量级、底层的插件系统,可以介入Django的请求和响应处理过程,修改Django的输入或输出。中间件的设计为开发者提供了一种无侵入式的开发方式,增强了Django框架的健壮性。
我们可以使用中间件,在Django处理视图的不同阶段对输入或输出进行干预。
9.1 中间件的定义方法
定义一个中间件工厂函数,然后返回一个可以被调用的中间件。
中间件工厂函数需要接收一个可以调用的get_response对象。
返回的中间件也是一个可以被调用的对象,并且像视图一样需要接收一个request对象参数,返回一个response对象。
# book/middleware.py
# 原理:这就是一个装饰器
def simple_middleware(get_response):
# 此处编写的代码仅在Django第一次配置和初始化的时候执行一次。
def middleware(request):
# 此处编写的代码会在每个请求处理视图前被调用。
response = get_response(request)
# 此处编写的代码会在每个请求处理视图之后被调用。
return response
return middleware
# 类装饰器
class SimpleMiddleware:
def __init__(self, get_response):
self.get_response = get_response
# One-time configuration and initialization.
def __call__(self, request):
# Code to be executed for each request before
# the view (and later middleware) are called.
response = self.get_response(request)
# Code to be executed for each request/response after
# the view is called.
return response
9.2 中间件注册
定义好中间件后,需要在settings.py 文件中添加注册中间件
MIDDLEWARE = [
...
'book.middleware.SimpleMiddleware', # 添加中间件
]
9.3 中间件调用顺序
- 在请求视图被处理前,中间件由上至下依次执行
- 在请求视图被处理后,中间件由下至上依次执行