视图层

内容概要

  • request 对象方法
  • FBV 与 CBV
  • CBV 源码刨析
  • 三板斧介绍
  • JsonResponse 对象
  • 文件上传

内容详细

request 对象方法

参考官方文档: https://docs.djangoproject.com/en/1.11/ref/request-response/

当浏览器向服务端请求一个页面时,请求数据会先经过wsgiref 模块进行处理,再把处理过后的请求数据给到Django,Django会创建一个HttpRequest对象,该对象包含关于请求的原数据,然后再通过路由配置调用相应的视图函数,并把HttpResponse对象当作参数传给视图函数,约定俗成视图函数用 request 来接收 HttpResponse 对象,此时 HttpResponse 对象也成为 request 对象

视图函数执行完毕之后,需要有一个返回值,也是返回一个 HttpResponse 对象。

视图函数:

def test1(request):
    return HttpResponse('app01 test1')

def index(request):
    return render(request, 'index.html')

def beauty(request):
    return redirect('https://www.mmzztt.com')

注意: django 三板斧 render, HttpResponse, redirect,通过源码也可以知道 render,redirect 最终也是返回一个 HttpResponse 对象。

image

request 对象常用的方法以及作用

def test(request):
    if request.method == 'POST':
        print(request.POST)
    if request.method == 'GET':
        print(request.GET)
    return HttpResponse('ok')
  • method 请求中使用的HTTP方法的字符串表示,全大写表示。

包含 GET、POST、等提交方式字符串

  • GET 包含所有HTTP GET参数的类字典对象

接收GET请求数据,一般为urlencode类型的数据,也可以接收url问号后面传输过来的数据

  • POST 包含所有HTTP POST参数的类字典对象

当form表单添加method="post",数据提交方式变成了POST请求

  • FILES 包含所有上传的文件信息

注意!form表单提交数据前需要添加属性 enctype="multipart/form-data" ,在后端 request.FILES 才能拿到文件数据

  • body 请求体,byte类型 request.POST的数据就是从body里面提取到的

一个字符串,代表请求报文的主体。在处理非 HTTP 形式的报文时非常有用,例如:二进制图、XML,Json等。可以用 request.body 来获取前端传过来的 Json 数据类型

  • path_info 返回用户访问url,不包括域名
  • get_full_path_info() 返回用户访问的url地址(路径),还可以拿到问号后面的键值
"/music/bands/the_beatles/?print=true"

FBV 与 CBV

FBV (function base views)

顾名思义基于函数的视图类

我们之前写过的都是基于函数的view,就叫FBV

def login(request):
    request.get_full_path_info()
    if request.method == "POST":
        print(request.POST)
        print(request.FILES)
    return render(request, 'login.html')

路由与视图函数对应关系:

urlpatterns = [
    path('login/', views.login),
]
  • 给 FBV 添加装饰器

直接正常在函数头上添加语法糖即可

@login_auth
def login(request):
    if request.method == "POST":
        print(request.POST)
        print(request.FILES)
    return render(request, 'login.html')

CBV (class base views)

基于类的视图类

用类来写视图函数,类中定义相应的请求方式函数,GET请求来了就去get的视图函数,POST请求来了就去get的视图函数

class MyClass(View):
    def get(self, request):
        return HttpResponse('get 请求')

    def post(self, request):
        return HttpResponse('post 请求')

路由与视图函数对应关系:

urlpatterns = [
    path('CBV/', views.MyClass.as_view()),
]

# 记住: as_view() 要加括号调用,返回的是view
  • 给 CBV 添加装饰器

类中的方法与独立函数不完全相同,因此不能直接将函数装饰器应用于类中的方法 ,我们需要先将其转换为方法装饰器。

Django中提供了method_decorator装饰器用于将函数装饰器转换为方法装饰器。需要导入 django.utils.decorators 装饰器模块 的 method_decorator 函数

给 CBV 添加方法装饰器的三种方式:

# 简易装饰器
def login_auth(func):
    def inner(*args, **kwargs):
        res = func(*args, **kwargs)
        return res

    return inner

1、在类中函数头上添加(不推荐使用)

from django.utils.decorators import method_decorator

class MyClass(View):
    @method_decorator(login_auth)
    def get(self, request):
        return HttpResponse('get 请求')

    @method_decorator(login_auth)
    def post(self, request):
        return HttpResponse('post 请求')

2、在类头上添加

这种方法还可以指定给不同的方法添加不同的装饰器

@method_decorator(login_auth, name='get')
@method_decorator(login_auth, name='post')
class MyClass(View):
    def get(self, request):
        return HttpResponse('get 请求')

    def post(self, request):
        return HttpResponse('post 请求')

3、根据源码,在 dispatch 函数上添加装饰器,既可以做到作用于所有类方法

class MyClass(View):
    @method_decorator(login_auth)
    def dispatch(self, request, *args, **kwargs):
        pass

    def get(self, request):
        return HttpResponse('get 请求')

    def post(self, request):
        return HttpResponse('post 请求')

CBV 源码刨析

从路由文件 urls.py 的调用语句中看起

urlpatterns = [
    path('CBV/', views.MyClass.as_view()),
]
# as_view() 函数加括号优先调用 as_view函数

按住 ctrl 键,鼠标点击查看 as_view 源码

@classonlymethod
def as_view(cls, **initkwargs):
    def view(request, *args, **kwargs):
        self = cls(**initkwargs)		# self 是我们自定义类产生的对象
        if hasattr(self, 'get') and not hasattr(self, 'head'):
            self.head = self.get
        self.setup(request, *args, **kwargs)
        if not hasattr(self, 'request'):	# 自定义类必须接收 request 对象,否则主动报错
            raise AttributeError(
                "%s instance has no 'request' attribute. Did you override "
                "setup() and forget to call super()?" % cls.__name__
            )
         return self.dispatch(request, *args, **kwargs)		# view 函数调用 View 类中的dispath函数
    # ... 中间省略部分源码
    return view		# 返回view函数对象

从源码中可以看出,as_view 调用之后得到的是 view 函数对象,这是个闭包函数,当视图函数被触发,调用的是 view函数,view函数return一个 dispatch 函数调用结果

我们再查看 dispatch 的源码:

http_method_names = ['get', 'post', 'put', 'patch', 'delete', 'head', 'options', 'trace']


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:	# 如果是request中的属性,则用反射调用自身的响应的函数
        handler = getattr(self, request.method.lower(), self.http_method_not_allowed)
    else:
        handler = self.http_method_not_allowed
    return handler(request, *args, **kwargs)

image

image

三板斧介绍

三板斧官方文档: https://docs.djangoproject.com/en/1.11/topics/http/shortcuts/

render

image

结合一个给定的模板和一个给定的上下文字典,并返回一个渲染后的 HttpResponse 对象。

参数:
     request: 用于生成响应的请求对象。

     template_name:要使用的模板的完整名称,可选的参数

     context:添加到模板上下文的一个字典。默认是一个空字典。如果字典中的某个值是可调用的,视图将在渲染模板之前调用它。

     content_type:生成的文档要使用的MIME类型。默认为 DEFAULT_CONTENT_TYPE 设置的值。默认为'text/html'

     status:响应的状态码。默认为200。   useing: 用于加载模板的模板引擎的名称。一个简单的例子:
from django.shortcuts import render

def my_view(request):
    # 视图的代码写在这里
    return render(request, 'myapp/index.html', {'foo': 'bar'})

上面的代码等于

from django.http import HttpResponse
from django.template import loader

def my_view(request):
    # 视图代码写在这里
    t = loader.get_template('myapp/index.html')
    c = {'foo': 'bar'}
    return HttpResponse(t.render(c, request))

redirect

参数可以是:

  • 一个模型:将调用模型的get_absolute_url() 函数
  • 一个视图,可以带有参数:将使用urlresolvers.reverse 来反向解析名称
  • 一个绝对的或相对的URL,将原封不动的作为重定向的位置。

默认返回一个临时的重定向;传递permanent=True 可以返回一个永久的重定向。

示例:

你可以用多种方式使用redirect() 函数。

传递一个具体的ORM对象(了解即可)

将调用具体ORM对象的get_absolute_url() 方法来获取重定向的URL:

from django.shortcuts import redirect
 
def my_view(request):
    ...
    object = MyModel.objects.get(...)
    return redirect(object)

传递一个视图的名称

def my_view(request):
    ...
    return redirect('some-view-name', foo='bar')

传递要重定向到的一个具体的网址

def my_view(request):
    ...
    return redirect('/some/url/')

当然也可以是一个完整的网址

def my_view(request):
    ...
    return redirect('http://example.com/')

默认情况下,redirect() 返回一个临时重定向。以上所有的形式都接收一个permanent 参数;如果设置为True,将返回一个永久的重定向:

def my_view(request):
    ...
    object = MyModel.objects.get(...)
    return redirect(object, permanent=True)  

扩展阅读:

临时重定向(响应状态码:302)和永久重定向(响应状态码:301)对普通用户来说是没什么区别的,它主要面向的是搜索引擎的机器人。

A页面临时重定向到B页面,那搜索引擎收录的就是A页面。

A页面永久重定向到B页面,那搜索引擎收录的就是B页面。

HttpResponse

image

JsonResponse 对象

json 格式数据的作用:

前后端在进行跨语言数据交互时,需要传输字典等更多特殊数据类型,而不是仅仅只有字符串

json格式的数据在经过序列化和反序列化,可以实现多种数据类型跨语言传输,所以几乎每种语言都会有专门序列化和反序列化json格式数据的方法

# pyhton
import json
data = {'name': 'elijah', 'age': 18}
res = json.dumps(data, ensure_ascii=False)		# ensure_ascii=False 取消中文转二进制
json.loads(res)		# 反序列化
// javascript
data = {'name': 'elijah', 'age': 18}
res = json.stringfy(data)		// 序列化
json.parse(res)			// 反序列化

视图函数中使用 JsonResponse 方法可以直接把字典转为json数据并传给前端

首先得导入 JsonResponse 类

from django.http import JsonResponse

def my_json(request):
    data = {'name': 'elijah', 'age': 18}
    return JsonResponse(data)

这是 JsonResponse 的源码:

image

  • safe

safe 值为 True 表示JsonResponse 只能接收字典类型,为 False 则表示可以接收列表等其它数据类型

  • json_dumps_params

由源码可知,json_dumps_params 参数最后时一个字典, ** 符号的作用就是把字典中的键值对打散变为关键字参数传给 json.dump() 函数

所以如果不想中文被转为二进制,则需要给json_dumps_params赋值一个字典,值为 {'ensure_ascii': 'False'}

def my_json(request):
    data = {'name': 'elijah', 'age': 18}
    return JsonResponse(data, json_dumps_params={'ensure_ascii': False}, safe=False)

文件上传

前端:

前端给后端传输文件数据,则需要给form表单标签添加属性 enctype="multipart/form-data"

这样,前端的数据会以 formdata 的数据格式传给后端

前端常见传输的数据格式:

  • urlencode: name=elijah&age=18

  • formdata:可以传输文件数据

  • json:跨语言传输数据格式

<form action="" method="post" enctype="multipart/form-data">
    <label for="username">username:</label>
    <input type="text" class="form-control" id="username" name="username">
    <div class="form-group">
        <label for="f1">头像:
            <img src="" alt="">
        </label>
        <input type="file" name="file" id="f1">
    </div>
    <input type="button" id="ajax_btn">
    <input type="submit" class="btn btn-primary btn-lg pull-left">
</form>

ajax 传输文件数据:

如果是以 ajax 的方式传输文件数据,则需要使用 javascript 的 formdata 对象,并且传输数据时,需要告诉浏览器不要对formdata对象进行修改

<script>
    $('#ajax_btn').on('click', function () {
        // 实例化FormData对象
        let formDataObj = new FormData()
        // 添加普通数据
        formDataObj.append('username', 'elijah')
        // 添加文件数据
        formDataObj.append('file', $('#f1')[0].file[0])
        // ajax
        $.ajax({
            url:"",
            type: 'post',
            data: formDataObj,

            // 提示浏览器不要修改传输的数据,和不定义数据类型
            processData:false,
            contentType:false,

            success:function (arg){
                console.log(arg)
            }
        })
    })
</script>

后端:

后端用 request.FILES 方法接收文件数据(是个字典),再用.get() 方法获取文件对象,并保存到目录中,文件路径保存在表的文件字段中。

def login(request):
    request.get_full_path_info()
    if request.method == "POST":
        print(request.POST)
        print(request.FILES)
        files_obj = request.FILES.get('file')   # 获取的是文件对象
        print(files_obj.name)   # 获取文件名字
        with open(files_obj.name, 'wb') as f:
            for line in files_obj:      # 文件对象可以循环读取并写入
                f.write(line)
    return render(request, 'login.html')

打印结果:

image

posted @ 2022-03-04 17:34  elijah_li  阅读(358)  评论(0编辑  收藏  举报
//一下两个链接最好自己保存下来,再上传到自己的博客园的“文件”选项中