58、Django视图层

Django框架 / 2021-02-19

视图函数,简称视图,一般定义在views.py;用于接受web请求与相应请求,相应地有请求对象HttpRequest对象、HttpResponse对象,我们前面的学习也用到这两个对象的一些方法。

一、HttpRequest对象

当有web请求时,Django会创建一个包含本次请求信息的HttpRequest对象并将其传递给urls定义的视图层函数,而我们应当约定俗成地使用request形参来接收它。

常用方法

path获取用户访问的路由,但拿不到参数
path_full_path能获取用户完整的资源,包括域名、路由、参数
method获取请求的HTTP方法(get post put head delete connect options trace),全大写表示。
GET获取用户get请求的所有相关参数
POST获取用户post请求的所有相关参数
FILES获取用户POST提交的文件对象。
body原生的浏览器发过来的二进制数据

二、HttpResponse对象

3.1、HTTPresponse

为客户端返回字符串类型,常见方法有

  • HttpResponse.content:响应内容。
  • HttpResponse.charset:响应内容的编码。
  • HttpResponse.status_code:响应的状态码。

3.2、render

返回HTML页面,且在返回给浏览器前还可以给它传值

3.3、redirect

重定向

视图函数必须要返回一个HTTPresponse对象,而render和redirect源码其实都是继承了HTTPresponse。

比如render内部源码:

def render(request, template_name, context=None, content_type=None, status=None, using=None):
    """
    Returns a HttpResponse whose content is filled with the result of calling
    django.template.loader.render_to_string() with the passed arguments.
    """
    content = loader.render_to_string(template_name, context, request, using=using)
    return HttpResponse(content, content_type, status)

三、JsonResponse对象

JsonResponse是HttpResponse的子类,专用于转换数据格式为json格式以实现跨语言数据交互。

3.1、基本使用

我们以前都是使用json模块来对数据进行转换,而Django则帮我们封装了json模块。

JsonResponse({'sanxi':666})
# 这个JsonResponse默认只能序列化字典类型,如需序列化其它类型需要指定参数safe
JsonResponse([123, 222], safe=False)
# 原生json中文等编码默认自动转为Unicode,如果不想转码需要指定ensuure_ascii=false,在Django中更麻烦了
# 我们去看下JsonResponse的源码就知道了

3.2、JsonResponse源码

class JsonResponse(HttpResponse):
    """
    An HTTP response class that consumes data to be serialized to JSON.

    :param data: Data to be dumped into json. By default only ``dict`` objects
      are allowed to be passed due to a security flaw before EcmaScript 5. See
      the ``safe`` parameter for more information.
    :param encoder: Should be an json encoder class. Defaults to
      ``django.core.serializers.json.DjangoJSONEncoder``.
    :param safe: Controls if only ``dict`` objects may be serialized. Defaults
      to ``True``.
    :param json_dumps_params: A dictionary of kwargs passed to json.dumps().
    """

    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)

看倒数第二行,这里的**json_dumps_params默认是None,而接下来的代码为None则为空字典;那我们给他传一个字典的话就会被打散替换掉这个空字典,那么init的json_dumps_params会把这个键值对打散从而获得我们想要的类似json不转码ensuure_ascii=false的效果。

如下所示,很明显,这个办法不好用,越搞越麻烦。

JsonResponse('哈哈哈', safe=False, json_dumps_params={'ensure_ascii':False})

在JavaScript中序列化用的JSON

JSON.stringify()
JSON.parse()

四、上传文件

在学习HTML时我们知道,HTML使用form表单提交数据给后端,但是method必须是post,enctype必须是multipart/form-data

<form action="{% url 'study_index' %}" method="post" enctype="multipart/form-data">
</form>

且在后端views中,POST方法只能获取普通的键值对数据,对文件则不好用了,得用FILES方法。

def index(request):
    if request.method == 'POST':
        file_obj = request.FILES.get('filename')  # 获取文件对象,其中get里面的参数是input:file里面的value的值
        print(file_obj.name)
    return render(request, 'login.html')

五、FBV与CBV

视图是可调用的,用于处理请求(request)和响应请求(response),在Django中,视图有两种形式:

  • FBV,function base views,基于函数的视图,我们之前一直都是用的FBV。
  • CBV,class base views,基于类的视图,本次主要讲解它。

基于类的视图和基于函数的视图都是为了让开发变得更加容易,二者跟Python的类和函数的区别是一样,函数是基础,类是更高级的设计。

5.1、CBV使用

CBV允许我们使用不同的函数来响应不同的请求方法,而无需像FBV那样使用if条件分支来判断请求方法!

urls.py代码

CBV的路由写法跟FBV有些不一样,CBV必须要用Django提供的方法as_view()。

from django.conf.urls import url
from study import views


urlpatterns = [
    url(r'^index/', views.Myview.as_view())
]

views.py代码

需要导一个模块:from django.views import View,要自动响应不同的请求方法就必须要定义该方法同名的函数。

from django.shortcuts import render, HttpResponse, redirect, reverse
from django.views import View
# Create your views here.


class Myview(View):  # 所有视图的类必须要继承View
    def get(self, request):
        return HttpResponse('GET request')

    def post(self, request):
        return HttpResponse('POST request')

内部到底是怎么实现的?为什么一定要用as_views?它是被@staticmethod修饰的静态方法,还是被@classmethod修饰的类方法?

5.2、as_view源码分析

其实不用看源码也能猜到,因为我们定义的类里根本没有这个方法,这肯定是继承了View的方法属性。

在as_view源码里有下面这段代码,从最后面的return view可以看出url(r'^index/', views.Myview.as_view()),在启动Django时就会立即执行as_view方法,立刻变形成url(r'^index/', views.view

在看Python源码时,要时刻记住面向对象属性查找顺序,看到self要明白这个self是谁。

        def view(request, *args, **kwargs):
            self = cls(**initkwargs)  # cls是我们自定义的类,这里就是用我们定义的类实例化
            if hasattr(self, 'get') and not hasattr(self, 'head'):
                self.head = self.get
            self.request = request
            self.args = args
            self.kwargs = kwargs
            return self.dispatch(request, *args, **kwargs)  # 返回实例化产生的对象调用dispatch
        view.view_class = cls
        view.view_initkwargs = initkwargs

        # take name and docstring from class
        update_wrapper(view, cls, updated=())

        # and possible attributes set by decorators
        # like csrf_exempt from dispatch
        update_wrapper(view, cls.dispatch, assigned=())
        return view

再看dispatch方法,CBV的精髓所在。

def dispatch(self, request, *args, **kwargs):  # self是我们自定义类实例化产生的对象
    # 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:  # 如请求方法在此列表中
        # getattr,反射:通过字符串来操作对象的属性和方法
        #  handler = getattr(我们的类实例化产生的对象,请求方法,列表中找不到请求的属性方法时就会用第三个参数)
        handler = getattr(self, request.method.lower(), self.http_method_not_allowed) (对象,请求方法)
    else:
        handler = self.http_method_not_allowed
    return handler(request, *args, **kwargs)  # 自动调用请求方法所对应的函数

5.3、CBV添加装饰器

from django.views.decorators.csrf import csrf_protect, csrf_exempt
from django.utils.decorators import method_decorator


# @method_decorator(csrf_protect, name='post')  # 给指定方法添加装饰器,可多个。
class MyCsrfToken(View):
    @method_decorator(csrf_protect)  # 作用于当前类所有方法
    def dispatch(self, request, *args, **kwargs):
        return super(MyCsrfToken, self).dispatch(request, *args, **kwargs)
    
    @method_decorator(csrf_protect)  # 直接上装饰器
    def get(self, request):
        return HttpResponse('get')

    def post(self, request)
        return HttpResponse('post')
世间微尘里 独爱茶酒中