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请求数据

request.png


请求相关的常用值


假如提交的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_LENGTHCONTENT_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'
posted @ 2019-10-02 01:19  cjw1219  阅读(258)  评论(0编辑  收藏  举报