Django视图层 - 视图层如何处理客户端请求?| Python 主题月

本文正在参加「Python主题月」,详情查看 活动连接html

微信公众号搜索【程序媛小庄】,Rest cannot be enjoyed by lazy people.~前端

前言

django路由层的文章中介绍了如何进行路由和视图函数的匹配关系,本文将介绍django的视图函数,即django的视图层。python

视图层的功能

在路由层进行路由和视图函数的匹配,路由匹配成功后将会执行对应的视图函数,视图函数就是用来处理对应url的请求而且返回响应数据,在这个请求-响应的过程当中大体涉及到两点,第一就是视图层会和模型层(数据相关)进行交互,对数据进行增删改查,第二是视图层须要将某些数据传递给前端HTML页面。django

request对象

django请求生命周期中,客户端浏览器发送的请求经过网关以后会将客户端的请求解析成HTTP格式的数据封装到request对象中,在视图函数中的每一个函数都必须有request形参(名字能够任意,约定俗成是request)来接收request对象,在若是在视图函数中打印request对象会获得什么呢?编程

def index(request):
    print(request)   # <WSGIRequest: GET '/first/index/'>,表示请求方式为get,请求的路由是/first/index
    return HttpResponse('js_str')
复制代码

request对象中有不少方法能够获取客户端浏览器请求的具体数据,好比请求方式、请求携带的数据等。结合前端的form表单一块儿来看看request对象的方法,主要分为request.GETrequest.POST两方面,分别表示客户端浏览器的get请求和post请求。json

request.GET

form表单的默认请求方式就是get,当请求方式是get时前端向后端提交的数据会直接跟在url的后面以?做为参数和url的分隔符,不一样参数之间以&链接,问号后面携带的参数对路由没有任何影响,只是向后端传送get请求方式的数据。后端

注意:get请求通常是用来请求页面的,问号后边携带的参数一般用来在后端进行数据筛选之类的操做,而不用于前端向后端提交数据。任何第一次打开的页面发送的请求都是get请求。浏览器

<!--前端html页面:mu.html-->
<body>
    <form method="get">  
        用户名:<input type="text" name="username" placeholder="请输入用户名" id="username">
        密码:<input type="text" name="pwd" placeholder="请输入密码" id="pwd">
        <input type="submit" value="登陆">
    </form>
</body>
复制代码
# views.py
def login(request):
    print(f'请求方式{request.method}')
	print(request.get_full_path())   # 获取完整的url及问号后面的参数
	print(request.path)   # 获取url
    print(f'get请求携带的数据{request.GET}')  # 获得的是相似字典的对象,取值方式和字典相同
    print('获取请求携带的数据')
    print(request.GET.get('username'))
    print(request.GET.get('pwd'))
    return render(request, 'mu.html')
复制代码

当访问127.0.0.1:8000/login时,会打开mu.html页面。输入用户名和密码以后点击登陆按钮,浏览器的地址栏中就会变成http://127.0.0.1:8000/login/?username=root&pwd=123,然后端对应的视图函数会输出对应的结果,以下:微信

请求方式GET
/first/login/?username=root&pwd=123
/first/login/
get请求携带的数据<QueryDict: {'username': ['root'], 'pwd': ['123']}>
获取请求携带的数据
root
123
复制代码

request.POST

通常post请求是用来向后端提交数据,当使用post请求方式向后端发送请求时,提交的数据将再也不显示在url的后面。post请求的前端页面使用HTML文章中的前端注册页面markdown

# views.py
def register(request):
    if request.method == 'POST':

        print(f'请求方式{request.method}')
		
        # request.POST,获取用户提交的数据,不包括文件,每一个键值对中的值都是列表
        res = request.POST
        # res.get('username'),只获取列表中的最后一个元素
        print(res.get('username'), type(res.get('username')))

        # res.getlist('hobby')直接将列表取出
        print(res.getlist('hobby'), type(res.getlist('hobby')))

        # 获取文件数据
        print(request.FILES)
        avatar_obj = request.FILES.get('avatar')   # 获取文件对象
        print(avatar_obj.name)   # 获取文件名称
        # 保存文件至服务端的静态文件中
        with open(avatar_obj.name, 'wb') as f:
            for line in avatar_obj.chunks():  # 保存文件数据须要使用chunks()
                f.write(line)
        return HttpResponse('注册成功')
    return render(request, 'render_html.html')
复制代码

当访问127.0.0.1:8000/register时,首先会打开render_html.html页面,输入用户名和密码以后点击提交,前端输入的数据将会传给后端,后端视图函数的执行结果以下:

请求方式POST
root <class 'str'> ['read', 'outdoors'] <class 'list'> <MultiValueDict: {'avatar': [<InMemoryUploadedFile: 5076e4181de45b78727ffca4e1f8e0a.png (image/png)>]}>
5076e4181de45b78727ffca4e1f8e0a.png
复制代码

request对象其余方法

除了上述的request对象的方法,还有一些其余经常使用的request对象方法:

# 原生浏览器发过来的二进制数据
print(request.body)

# 获取url
print(request.path)

# 获取完整的url及问号后面的参数
print(request.get_full_path())
复制代码

HttpResponse & render & redirect

在视图函数中最基本的三种向前端页面返回数据的方式:

from django.shortcuts import render, redirect, HttpResponse

# HttpResponse返回字符串给前端
return HttpResponse('hello world')

# render,将模板文件渲染成一个html文件而后返回给浏览器,还支持给模板文件传数据
return render(request, 'index.html', locals())

# redirect, 重定向
return redirect('/index/')
复制代码

这三者内部返回的都是HttpResponse对象,所以视图函数必须返回一个HttpResponse对象。

JsonResponse

老生常谈,json格式的数据一般用来不一样编程语言之间进行数据交互,先后端数据的交互也可使用json格式的数据进行传输。可使用json模块进行序列化:

import json
from django.shortcuts import HttpResponse

def my_json(request):
    user_dict = {'name':'python','password':'123'}
    js_str = json.dumps(user_dict,ensure_ascii=False)
    return HttpResponse(js_str)
复制代码

django框架提供了另种更加方便的方法将json格式的数据JsonResponseJsonResponse也是一个类,该类继承了HttpResponse用法以下:

from django.http import JsonResponse

def ab_json(request):
    user_dic = {'name':'小庄','password':'123'}

    # JsonResponse方式 --- 经过源码掌握用法,若是json_dumps_params参数中ensure_ascii的值为真,返回的序列化字符串中文将不能正常显示
    return JsonResponse(user_dic, json_dumps_params={'ensure_ascii':False})
复制代码

须要注意的是,JsonResponse默认只能序列化字典,若是想要序列化其余格式的数据须要指定safe参数为False,不然会抛出异常。源码以下:

# JsonResponse部分源码
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().__init__(content=data, **kwargs)
复制代码

所以在对非字典类型的数据类型进行序列化时须要指定safe参数:

from django.http import JsonResponse

def test(request):
    l = ['a', '庄']
    return JsonResponse(l, safe=False, json_dumps_params={'ensure_ascii': False})
复制代码

FBV和CBV

视图层中的进行逻辑处理部分的代码既能够是函数,也能够是类,在这以前的视图层的逻辑处理都是函数处理请求,即FBV(function base views),下面就来介绍CBV是如何与url进行对应并处理逻辑的。

CBV(class base views),在视图中使用类处理请求,python是一个面向对象的编程语言,若是只用函数进行开发,可能不少面向对象的优势就错失了(封装、继承、多态),所以Django在后来加入了Class-Based-View。可让咱们用类写View。这样作的优势主要下面两种:

①提升了代码的复用性,可使用面向对象的技术,好比Mixin(多继承)

②能够用不一样的函数针对不一样的HTTP方法处理,而不是经过不少if判断,提升代码可读性

CBV的特色就是可以根据请求方式的不一样直接匹配到对应的方法执行,下面是使用CBV的视图层和路由层的代码:

# views.py
from django.views import View

class CBV(View):
    def get(self, request):
        return HttpResponse('get')
    
    def post(self, request):
        return HttpResponse('post')
    
# urls.py
from django.conf.urls import url
from first import views

urlpatterns = [
    url(r'cbv/', views.CBV.as_view())
]
复制代码

当访问127.0.0.1:8000/cbv时,会根据请求方式的不一样自动执行CBV类中对应的方法,那么django是如何实现这一效果的呢?就须要从源码入手分析,请求到达django后端时首先须要通过路由层,即urls.py,路由匹配以后才会执行对应的视图函数,所以就从urls.py着手进行源码分析,这里就要注意对象的属性查找顺序,千万不能搞错了噢~先从类自己查找,找不到去父类中找,必定要按照这个顺序

首先查看as_view的源码,发现是类方法,该方法调用后会返回闭包函数view,因而此时url与视图函数的关系变成了url(r'cbv/', views.view),这就和FBV的形式相同了。

再看闭包函数view,在该函数内实例化视图层定义的CBV类,并调用对象的dispatch方法,因为自定义的CBV类中并无该方法,按照对象的属性查找顺序,须要去父类View中查找dispatch方法,换句话说若是在CBV中定义了dispatch方法就会覆盖View类中的。

最后在dispatch方法内采用了反射的方式,根据请求方式的不一样动态匹配到视图类中对应的方法中。

image-20210714112402429

结语

文章首发于微信公众号**「程序媛小庄」**,同步于掘金

码字不易,转载请说明出处,走过路过的小伙伴们伸出可爱的小指头点个赞再走吧(╹▽╹)

image.png

相关文章
相关标签/搜索