Django视图层
Django的View(视图)
一个视图函数(类),简称视图,是一个简单的Python 函数(类),它接受Web请求并且返回Web响应。
响应可以是一张网页的HTML内容,一个重定向,一个404错误,一个XML文档,或者一张图片。
无论视图本身包含什么逻辑,都要返回响应。代码写在哪里也无所谓,只要它在你当前项目目录下面。除此之外没有更多的要求了——可以说“没有什么神奇的地方”。为了将代码放在某处,大家约定成俗将视图放置在项目(project)或应用程序(app)目录中的名为views.py
的文件中
FBV与CBV
无论是FBV还是CBV路由层都是路由对应视图函数内存地址
FBV
基于函数的视图
>>>> urls.py
from django.conf.urls import url
from django.contrib import admin
from app01 import views
urlpatterns = [
url(r'^add_press/', views.add_press),
]
>>>> views.py
from django.shortcuts import render, redirect
from .models import Press, Book, Author
def add_press(request):
if request.method == 'POST':
name = request.POST.get('name')
Press.objects.create(name=name)
return redirect('/press_list/')
return render(request, 'add/add_press2.html')
CBV
基于类的视图
>>>> urls.py
from django.conf.urls import url
from django.contrib import admin
from app01 import views
urlpatterns = [
url(r'^add_press/', views.AddPress.as_view()),
]
>>>> views.py
from django.shortcuts import render, redirect
from django.views import View
from .models import Press, Book, Author
class AddPress(View):
def get(self, request):
return render(request, 'add/add_press2.html')
def post(self, request):
name = request.POST.get('name')
Press.objects.create(name=name)
return redirect('/press_list/')
CBV执行流程
1. 实例化自己写的类
AddPress.as_view() ---> 调用父类(View)的类绑定方法as_view并执行as_view下的嵌套函数view(主要作用是实例化对象和返回父类下的dispath方法执行的返回值)
class View(object):
@classonlymethod
def as_view(cls, **initkwargs):
def view(request, *args, **kwargs):
self = cls(**initkwargs)
self.request = request
self.args = args
self.kwargs = kwargs
return self.dispatch(request, *args, **kwargs)
return view
2. 执行dispatch方法
判断请求方式是否被允许
1. 允许的情况
handler = 通过反射获取 get post 方法
2. 不允许的情况
handler = 不允许的方法
3. handler(request, *args, **kwargs)
class View(object):
http_method_names = ['get', 'post', 'put', 'patch', 'delete', 'head', 'options', 'trace']
def dispatch(self, request, *args, **kwargs):
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)
3. 返回HttpResponse对象给到父类的as_view类绑定方法的嵌套函数view
4. view的返回值, 即响应结果由django处理返回给浏览器进行渲染
视图函数中使用装饰器
装饰器
import time
def timer(fn):
def inner(*args, **kwargs):
'''执行前的操作, 计算初始时间'''
start = time.time()
'''函数执行, 并将返回值由变量ret保存'''
ret = fn(*args, **kwargs)
'''函数执行后的操作'''
print('函数的执行时间是{}'.format(time.time() - start))
'''返回函数的返回值'''
return ret
return inner
FBV
from django.shortcuts import render, redirect
from django.views import View
from .models import Press, Book, Author
@timer
def add_press(request):
if request.method == 'POST':
name = request.POST.get('name')
Press.objects.create(name=name)
return redirect('/press_list/')
return render(request, 'add/add_press2.html')
CBV
from django.utils.decorators import method_decorator
第一种方法
在每个请求方法上分别加 @method_decorator(timer)
from django.shortcuts import render, redirect
from django.views import View
from .models import Press, Book, Author
import time
from django.utils.decorators import method_decorator
class AddPress(View):
@method_decorator(timer)
def get(self, request):
return render(request, 'add/add_press2.html')
@method_decorator(timer)
def post(self, request):
name = request.POST.get('name')
Press.objects.create(name=name)
return redirect('/press_list/')
第二种方法
想要将类中的所有方法都加上装饰器, 重写dispatch方法, 给dispatch方法加上装饰器
from django.shortcuts import render, redirect
from django.views import View
from .models import Press, Book, Author
import time
from django.utils.decorators import method_decorator
class AddPress(View):
@method_decorator(timer)
def dispatch(self, request, *args, **kwargs):
ret = super().dispatch(request, *args, **kwargs)
return ret
def get(self, request):
return render(request, 'add/add_press2.html')
def post(self, request):
name = request.POST.get('name')
Press.objects.create(name=name)
return redirect('/press_list/')
第三种方法
在类名上面加装饰器, 但是需要制定name属性
from django.shortcuts import render, redirect
from django.views import View
from .models import Press, Book, Author
import time
from django.utils.decorators import method_decorator
@method_decorator(timer, name='get')
@method_decorator(timer, name='post')
class AddPress(View):
def dispatch(self, request, *args, **kwargs):
ret = super().dispatch(request, *args, **kwargs)
return ret
def get(self, request):
return render(request, 'add/add_press2.html')
def post(self, request):
name = request.POST.get('name')
Press.objects.create(name=name)
return redirect('/press_list/')
Request对象和Response对象
request对象
当一个页面被请求时,Django就会创建一个包含本次请求原信息的HttpRequest对象。
Django会将这个对象自动传递给响应的视图函数,一般视图函数约定俗成地使用 request 参数承接这个对象。
请求地址格式
http://127.0.0.1/index
url:协议://IP:port/路径?get请求数据
请求相关的常用值
假如提交的url是 http://127.0.0.1:8000/test/?id=1
-
request.path_info 返回用户访问url,不包括域名
/test/
-
request.path 返回用户访问url,不包括域名
/test/
-
request.method 一个字符串, 表示请求使用的HTTP方法, 例如 "GET", "POST"
GET
-
request.GET 包含所有HTTP GET参数的类字典对象
<QueryDict: {}>
-
request.POST 包含所有HTTP POST参数的类字典对象
<QueryDict: {}>
-
request.body 请求内容,byte类型
业务场景主要是在处理非
HTTP
形式的报文时非常有用, 例如: 二进制图片,XML
,Json
等request.POST
的数据就是从body
里面提取到的, 如果要处理表单数据的时候, 推荐还是使用request.POST
注意 ! ! ! 请求内容从request.POST中取到后, 这里就取不到数据了
b''
-
request.FILES 一个类似字典的对象, 包含所有的上传文件信息
注意 ! ! ! FILES 只有在请求的方法为
POST
且提交的<form>
带有enctype="multipart/form-data"
的情况下才会包含数据, 否则, FILES 将为一个空的类似于字典的对象<MultiValueDict: {'myfile': [<InMemoryUploadedFile: avatar.jpg (image/jpeg)>]}>
-
request.encoding 一个字符串, 表示提交的数据的编码方式
如果为None, 则表示使用 DEFAULT_CHARSET 的设置, 默认为 'utf-8')
这个属性是可写的, 可以修改它来修改访问表单数据使用的编码
接下来对属性的任何访问(例如从 GET 或 POST 中读取数据)将使用新的 encoding值
如果你知道表单数据的编码不是 DEFAULT_CHARSET, 则使用它
-
request.META 一个标准的python字典, 包含所有的HTTP 首部. 具体的头部信息取决于客户端和服务器
键值 说明 CONTENT_LENGTH 请求的正文的长度(是一个字符串) CONTENT_TYPE 请求的正文的MIME类型 HTTP_ACCEPT 响应可接受的Content_Type HTTP_ACCEPT_ENCODING 响应可接收的编码 HTTP_ACCEPT_LANGUAGE 响应可接收的语言 HTTP_HOST 客户端发送HTTP Host头部 HTTP_REFERER Referring 页面 HTTP_USER_AGENT 客户端的user-agent字符串 QUERY_STRING 单个字符串形式的查询字符串(未解析过的形式) REMOTE_ADDR 客户端的IP 地址 REMOTE_HOST 客户端的主机名 REMOTE_USER 服务器认证后的用户 REQUEST_METHOD 一个字符串, 例如'GET' 或 'POST' SERVER_NAME 服务器的主机名 SERVER_PORT 服务器d额端口(是一个字符串) 从上面的表格可以看到, 除
CONTENT_LENGTH
和CONTENT_TYPE
之外, 请求中任何 HTTP 首部转换为 META时, 都会将所有字母大写并将连接符替换为下划线最后加上HTTP_
前缀所以, 一个叫做
X-Bender
的头部将转换成 META 中的HTTP_X_BENDER
键
-
request.user 用户认证组件下使用, 一个AUTH_USER_MODEL 类型的对象, 表示当前登录的用户
如果用户当前没有登录, user将设置为
django.contrib.auth.model.AnonymousUser
的一个实例, 可以通过is_authenticated
区分他们AnonymousUser ----> 用户没有登录时候的request.user的输出
-
request.COOKIES 一个标准的python字典, 包含所有的cookie. 键和值都是字符串
{'csrftoken': 'xINH6A3h92N0jd38Jck3AhBnYp9xCZqSu7SR1AU7JP4Vmvs24Cc3hj3DYaGxhfj5'}
-
request.session 一个既可读又可写的类似字典的对象, 表示当前的会话. 只有当django启用会话的支持时才可用
<django.contrib.sessions.backends.db.SessionStore object at 0x10b764ef0>
-
request.scheme 表示请求方案的字符串(通常为http或https)
http
请求相关的常用方法
- request.get_full_path() 路径加参数
/test/?id=1
- request.get_host() 获取IP:port
127.0.0.1:8000
-
request.get_port() 获取port
8000
-
request.get_raw_uri() 获取
url
http://127.0.0.1:8000/test/?id=
-
request.is_secure() 判断url连接是否是受信任的链接
False
-
request.is_ajax() 判断是否通过ajax提交post请求
False
Response对象
与由Django自动创建的HttpRequest对象相比,HttpResponse对象是我们的职责范围了。我们写的每个视图都需要实例化,填充和返回一个HttpResponse。
HttpResponse类位于django.http模块中
from django.shortcuts import render, HttpResponse, redirect
1. HttpResponse HttpResponse('字符串')
2. render(request,'html文件名',{}) —— 》 HTML代码
3. redirect(跳转的地址)
redirect如何跳转?
1. 当输入 http://127.0.0.1:8000/add_press 时候, 此时的响应头如下
HTTP/1.0 301 Moved Permanently
Date: Sat, 02 Nov 2019 11:26:54 GMT
Server: WSGIServer/0.2 CPython/3.6.8
Content-Type: text/html; charset=utf-8
Location: /add_press/
Content-Length: 0
2. 根据 Location: /add_press/, 即后端程序中redirect后面定义的地址, 向http://127.0.0.1:8000/add_press/ 发送一次请求并接收响应
HTTP/1.0 200 OK
Date: Sat, 02 Nov 2019 11:26:54 GMT
Server: WSGIServer/0.2 CPython/3.6.8
Content-Type: text/html; charset=utf-8
X-Frame-Options: SAMEORIGIN
Vary: Cookie
Content-Length: 3356
Set-Cookie: csrftoken=xINH6A3h92N0jd38Jck3AhBnYp9xCZqSu7SR1AU7JP4Vmvs24Cc3hj3DYaGxhfj5; expires=Sat, 31-Oct-2020 11:26:54 GMT; Max-Age=31449600; Path=/
返回Json格式
JsonResponse是HttpResponse的子类,专门用来生成JSON编码的响应。
HttpResponse(json.dumps(ret)) # Content-Type: text/html; charset=utf-8
JsonResponse(ret) # Content-Type: application/json
需求
要求浏览器返回一个字典, 而不是一个字符串或者页面, 这种场景就需要用Json进行序列化python的字典编程json对象在浏览器显示
版本1
urls.py
from django.conf.urls import url
from django.contrib import admin
from app01 import views
urlpatterns = [
url(r'test',views.Test.as_view()),
]
views.py
from django.views import View
from django.shortcuts import HttpResponse, render, redirect
class Test(View):
def get(self, request):
data={'name': 'lyysb', 'password': 123332}
return HttpResponse(data)
def post(self, request):
pass
浏览器输入http://127.0.0.1:8000/test/
只是返回来字典的键值
所以如果要这个字典完整在浏览器上渲染,尝试使用json格式字符串
版本2
修改views.py里的代码
views.py
from django.views import View
from django.shortcuts import HttpResponse
import json
class Test(View):
def get(self, request):
data = {'name': 'lyy傻缺', 'password': 123332}
return HttpResponse(json.dumps(data, ensure_ascii=False))
# ensure_ascii=False参数的作用是使得中文能够在浏览器上渲染
def post(self, request):
pass
浏览器输入http://127.0.0.1:8000/test/
版本3
使用 JsonResponse 返回json字符串
views.py
from django.http import JsonResponse
from django.views import View
class Test(View):
def get(self, request):
data = {'name': 'lyy傻缺', 'password': 123332}
return JsonResponse(data,json_dumps_params={'ensure_ascii':False})
def post(self, request):
pass
urls.py
from django.contrib import admin
from django.urls import path, re_path
from app01 import views
urlpatterns = [
url('^admin/', admin.site.urls),
url('^test', views.MyCls.as_view())
]
为什么 JsonResponse(ret) 的响应头的Content-Type是 application/json
在源码中定义了 kwargs.setdefault('content_type', 'application/json')
JsonResponse 源码
内部调用的也是Json模块,封装了json.dumps()
如果需要传递参数,通过 json_dumps_params 关键字传参,传参的值以字典的形式传值,因为这样 json_dumps_params的值就打散成key=value的形式为json.dumps()传递
比如说 json.dumps(data, ensure_ascii=False),要使用JsonResponse得到同样的效果
就这么用: JsonResponse(data,json_dumps_params={'ensure_ascii':False})
class JsonResponse(HttpResponse):
def __init__(self, data, encoder=DjangoJSONEncoder, safe=True,
json_dumps_params=None, **kwargs):
if safe and not isinstance(data, dict):
raise TypeError(
'In order to allow non-dict objects to be serialized set the '
'safe parameter to False.'
)
if json_dumps_params is None:
json_dumps_params = {}
kwargs.setdefault('content_type', 'application/json')
data = json.dumps(data, cls=encoder, **json_dumps_params)
super(JsonResponse, self).__init__(content=data, **kwargs)
为什么 HttpResponse(ret) 的响应头的Content-Type是 text/html; charset=utf-8
-
在HttpResponse源码中, Content-Type是它的父类的一个默认值形参, 默认值是None
-
在HttpResponse源码中, 如果 Content-Type是None, 则 Content-Type 是取global_settings里的Content-Type的值
class HttpResponseBase(six.Iterator):
status_code = 200
def __init__(self, content_type=None, status=None, reason=None, charset=None):
if content_type is None:
content_type = '%s; charset=%s' % (settings.DEFAULT_CONTENT_TYPE,
self.charset)
self['Content-Type'] = content_type
- 在global_settings中默认Content-Type 是 text/html; charset=utf-8
DEFAULT_CONTENT_TYPE = 'text/html'
DEFAULT_CHARSET = 'utf-8'