Django视图层 - 视图层如何处理客户端请求?

前言

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

视图层的功能

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

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请求。

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文章中的前端注册页面

# 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','}
    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':'小庄','}

    # 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方法内采用了反射的方式,根据请求方式的不同动态匹配到视图类中对应的方法中。

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
好的,关于Django的模型、视图和模板,我可以做个简单的介绍: Django是一个MVC框架,其的M、V、C分别指的是模型视图和控制器(其控制器Django对应的是URLconf),而这里的MVC与传统的MVC有所不同,更倾向于MVT的架构,即模型(Model)、视图(View)和模板(Template)。 - 模型(Model):模型主要是把应用需要用到的数据以类似面向对象的方式进行定义、管理和操作,通常对应database的表。Django的ORM(Object-relational mapping)对开发者屏蔽了底的SQL操作,开发者可以直接以Python语言去操作数据库,而不需要关心底SQL的实现细节。 - 视图(View):视图最主要的作用是处理用户的请求,响应相应的结果给用户。一般来说,视图会从数据库、缓存等数据源获取数据,然后将结果进行组装,返回HttpResponse给用户。Django视图可以通过函数或者类的方式来定义,对外提供一个可被URLconf调用的可调用对象。 - 模板(Template):模板是视图生成响应结果的主要组成部分,可以理解为一个动态生成的HTML页面,其包含了数据展示、控制逻辑、页面渲染等元素。Django的模板提供了超过100个内置的指令和过滤器,开发者可以非常方便的实现模板的渲染和页面的实现。 总结一下,模型主要和数据打交道,视图主要和操作和生成Http Response联系在一起,模板主要负责页面的渲染和数据展示。希望这个介绍可以帮到你。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值