Django 视图层

Django视图层

   Django视图层相关的知识都是在APP下的views.py中所应用的,视图层的主要功能就是如何处理由路由层解析过来的request请求,主要包含以下几块内容

  1.如何处理request请求对象

  2.如何返回一个HTML文档或其他内容给前端页面

  3.如何对接上数据库实时获取数据

返回内容

HttpResponse

   HttpResoponse()接收一个字符串并返回。

from django.shortcuts import HttpResponse

def return_HttpResponse(request):
    return HttpResponse("返回的内容")

redirect

   redirect()接受一个url并返回,状态码为302,即重定向。

from django.shortcuts import redirect

def return_redirect(request):
    return redirect("http://www.google.com") # 跳转到www.Google.com

render

   render()有多个参数,其中第一个参数为返回request请求对象,其他参数则用于返回HTML文档及其局部命名空间内变量用作模板渲染。

参数返回项
参数1 request对象
参数2 HTML文档
参数3 局部命名空间变量(字典形式从换入),或locals()函数
from django.shortcuts import render

def return_render(request):

    user_msg = {"name":"Yunya","age":18,"gender":"male"}

    return render(request,"login.html",{"user_msg":user_msg}) 

    # 参数3:{"user_msg":user_msg}  只传递user_msg到HTML文档
    # 参数3:locals() 将当前函数的命名空间(return_render)下的所有变量传递到HTML文档

Jsonresponse

   JsonresponseDjango中自带的一个基于json模块的封装,可以直接返回json类型的数据至模板层的前端页面。

  1.只支持字典,如果想传递其他数据类型需要将safe设置为False

  2.如果想让转换后的json格式字符串中存在中文,请设置参数json_dumps_params={"ensure_ascii":False},即关闭自动转码

from django.http import JsonResponse

def f1(request):
    data = {"name":"云崖","age":18}
    return JsonResponse(data,json_dumps_params={"ensure_ascii":False})  # 只支持字典,关闭自动转码

扩展知识

   其实不管是redirect也好,reder也罢,或者说Jsonresponse也好,实际上内部都是返回了HttpResponse,比如render的模板替换其实原理非常简单,就是将后端的变量映射到模板上,就好比于字符串替换方法,把字符串替换成一个对象。

   展示的模板页面其实就是经过替换之后来完成的。

def f1(request):
    data = {"name":"云崖","age":18}

    from django.template import Template,Context
    
    res = Template("<h1>{{user}}<h1>")  # 模板,将HTML代码渲染成模板
    con = Context({"user":{"name":"Yunya"}}) # 替换的内容封装成Context对象
    ret = res.render(con) # 执行替换
    
    return HttpResponse(ret)

request

   request对象即是用户发送过来的资源请求,内置了很多方法及对象供我们使用。

   其实Djangorequest对象是基于wsgirefenv的一个封装,是我们获取资源请求中的信息更加方便。

提交方式

   通过对request.method指向判断即可判定提交方式。

from django.shortcuts import render
from django.shortcuts import HttpResponse

def register(request):

    if request.method == "GET":
        print(request.GET)
        return render(request,"register.html")

    elif request.method == "POST":
        print(request.POST)
        username = request.POST.get("username")
        password = request.POST.get("password")
        hobby = request.POST.getlist("hobby")
        
        print(
            """
            username:{0},
            password:{1},
            hobby:{2}
            """
            .format(username,password,hobby)
        )
        return HttpResponse("注册成功")

GET&POST

   request.GETrequest.POST都是两个QueryDict对象,其中保存了用户提交过来的数据(不包含文件)。

   如下示例,在地址栏中手动填入数据,request.GET获取信息如下:

http://127.0.0.1:8000/register/?msg1=Django&msg2=request # 手动在地址栏填入的数据

print(request.GET)
<QueryDict: {'msg1': ['Django'], 'msg2': ['request']}>

数据获取

   数据获取可使用get()getlist()方法。

   get()用于从QueryDict中获取单个值,但无法获取多个,如<QueryDict: {'hobby': ['music','game']}>,那么获取到的始终是列表中的最后一个。

   而getlist()可获取多个,以列表形式进行返回,比如用户注册中有爱好等多选行为,应该使用getlist()进行获取。

   如下示例结合提交方式与数据获取,将一个视图函数分成两部分,结合不同的请求方式返回不同的请求内容。

from django.shortcuts import render
from django.shortcuts import HttpResponse

def register(request):

    if request.method == "GET":
        print(request.GET)
        return render(request,"register.html")

    elif request.method == "POST":
        print(request.POST)
        username = request.POST.get("username")
        password = request.POST.get("password")
        hobby = request.POST.getlist("hobby")  # 获取用户爱好等多选项时,应该使用getlist
        
        print(
            """
            username:{0},
            password:{1},
            hobby:{2}
            """
            .format(username,password,hobby)
        )
        return HttpResponse("注册成功")
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <script src='https://cdn.bootcdn.net/ajax/libs/jquery/3.5.1/jquery.js'></script>
    <link rel='stylesheet' href='https://cdn.jsdelivr.net/npm/bootstrap@3.3.7/dist/css/bootstrap.min.css' integrity='sha384-BVYiiSIFeK1dGmJRAkycuHAHRg32OmUcww7on3RYdg4Va+PmSTsz/K68vbdEjh4u' crossorigin='anonymous'>
    <script src='https://cdn.jsdelivr.net/npm/bootstrap@3.3.7/dist/js/bootstrap.min.js' integrity='sha384-Tc5IQib027qvyjSMfHjOMaLkfuWVxZxUPnCJA7l2mCWNIpG9mGCD8wGNIcPD7Txa' crossorigin='anonymous'></script>

    <title>Document</title>
</head>
<body>
    
    <h1 class="text-center">欢迎注册</h1>
        
        <div class="container">
            
            <div class="col-xs-8 col-sm-8 col-md-8 col-lg-8 col-xs-offset-2 col-sm-offset-2 col-md-offset-2 col-lg-offset-2">
                <form action="" method="POST">  <!-- 当action不填时,提交至当前页面 -->
                    <p><input type="text" name="username" class="form-control"></p>
                    <p><input type="password" name="password" class="form-control"></p>
                    <p class="checkbox-inline"><input type="checkbox" name="hobby" value="basketball">篮球</p>
                    <p class="checkbox-inline"><input type="checkbox" name="hobby" value="football">足球</p>
                    <p></p>
                    <p><input type="submit" class="btn btn-success form-control"></p>

                </form>
            </div>
            
        </div>
        

</body>
</html>

文件获取

   对于form中上传的文件,需要用request.FILES.get()进行获取。进行写入的时候推荐使用chunkS()(其实直接写也没啥问题)。

  注意:前端必须为POST请求类型,并且必须指定enctype属性为multpart/form-data。关于enctypeAjax中已有介绍。

def f1(request):

    if request.method == "GET":
        return render(request,"f1.html")
        
    elif request.method == "POST":
        file_obj = request.FILES.get("file_1")
        print(file_obj.name)
        print(file_obj.size)

        import os
        if not os.path.exists("./user_files"):
            os.mkdir("./user_files")

        with open("./user_files/{0}".format(file_obj.name),"wb") as f:
            for line in file_obj.chunks():  # 推荐使用chunks()
                f.write(line)

        return HttpResponse("写入完成")
    <form action="{% url 'app01:f1' %}" method="POST" enctype="multipart/form-data">
        <p><input type="file" name="file_1"></p>
        <p><button type="submit">上传文件</button></p>
    </form>

路径获取

   获取请求资源路径的方式有三种,两种属性一种方法,记住两种即可。

属性/方法描述
request.path 只拿请求资源路径,不拿GET请求的数据
request.path_info 同上
request.get_full_path() 即拿请求资源路径,也拿GET请求中的数据
http://127.0.0.1:8000/app01/f1/?id=1&username=Yunya

print(request.path)  # /app01/f1/
print(request.path_info) # /app01/f1/
print(request.get_full_path()) # /app01/f1/?id=1&username=Yunya

原生数据

   通过request.body属性可获取原生的资源路径请求,包括请求头,请求体等。

  注意:拿到的是字节类型,这个是在页面请求方式中没有注明任何数据编码格式,将会放入request.body

http://127.0.0.1:8000/app01/f1/  # POST方式提交,表单未input有个file类型,其他都没有了。

print(request.body)

b'------WebKitFormBoundary7YEgYjof25C6E8z9\r\nContent-Disposition: form-data; name="file_1"; filename=""\r\nContent-Type: application/octet-stream\r\n\r\n\r\n------WebKitFormBoundary7YEgYjof25C6E8z9--\r\n'

请求头

   通过request.META可获取到请求头,这个请求头非常有用。

   它中间封装了很多属性,比如有一个属性就是来判断是将数据存放进request.body/request.POST/request.GET/request.FEILS种的哪一个里面。

   我们可以看一下request.POST的源码:

   def _get_post(self):
        if not hasattr(self, '_post'):
            self._load_post_and_files()  # 执行
        return self._post

# self._load_post_and_files() 方法中的两个重要判断

if self.content_type == 'multipart/form-data':  # 判断请求头中请求方式的编码格式,这个会将POST提交中的文件放入reques.FILES里
               
elif self.content_type == 'application/x-www-form-urlencoded':  # 判断是否有该请求头,有的话放入request.POST中

FBV&CBV

区别差异

   FBV即在视图层里通过函数的方式进行逻辑处理。

   CBV则是在视图层里通过类的方式进行逻辑处理。

   CBV对比FBV有一个很明显的又是,就是能够自动的识别资源请求的提交方式。

   其实CBV内部就是FBV

基本使用

   使用CBV需要注意导入模块View,并且书写的类要继承该类。

   同时在urls.py做路由解析时要使用as_view()来进行解析。

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

urlpatterns = [
    url('^c1/', views.C1.as_view())  # 注意,需要加括号
]
from django.shortcuts import HttpResponse
from django.views import View # 导入

class C1(View): # 必须继承
 
    def get(self,request):  # 当get请求来,执行该方法
        return HttpResponse("get方法...")

    def post(self,request): # 当post请求来,执行该方法
        return HttpResponse("post方法...")

源码分析

   CBV如何做到区分请求方式的?这个要从路由层的as_view()方法开始看。

 @classonlymethod  # 类方法
    def as_view(cls, **initkwargs):
        for key in initkwargs:
            if key in cls.http_method_names:
                raise TypeError("You tried to pass in the %s method name as a "
                                "keyword argument to %s(). Don't do that."
                                % (key, cls.__name__))
            if not hasattr(cls, key):
                raise TypeError("%s() received an invalid keyword %r. as_view "
                                "only accepts arguments that are already "
                                "attributes of the class." % (cls.__name__, key))

        def view(request, *args, **kwargs):  # 闭包函数,现在不运行。下次的时候运行
            self = cls(**initkwargs) # self == C1,即我们自己定义的类
            if hasattr(self, 'get') and not hasattr(self, 'head'): # 如果有C1类有get方法并且没有head方法
                self.head = self.get 
            self.request = request  # 参数赋值
            self.args = args  # 参数赋值
            self.kwargs = kwargs  # 参数赋值
            return self.dispatch(request, *args, **kwargs)  # 执行并返回
        view.view_class = cls  # cls 即 C1 类
        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=()) # 可以看到,这里的cls.dispatch是一个属性或是没运行的方法,根据属性查找顺序。往C1的继承类中找,会找到dispatch()方法,并且注意,即使是方法这里并没有执行,所以会接着往下走
        return view   # 返回view,接下来才执行。

   经过闭包函数的返回,下面将会进行变形。

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

urlpatterns = [
    # url('^c1/', views.C1.as_view())  # 注意,需要加括号
    url('^c1/', views.C1.view) 自动执行view方法,其实就是执行闭包函数中的逻辑块
]

   继续向下取找View类中的dispatch属性/方法。

    def dispatch(self, request, *args, **kwargs):
		# 说白了就是判断请求方式,是不是在这个 self.http_method_names 里头,如果在handler就是一个请求模式的方法,比如get或者post
        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) # 执行并返回了

   好了,也就是说最后的as_view()会执行View.dispatch()这个方法,这个方法内部其实就会执行GET或者POST方法。

  django-CBV源代码分析

方法扩展

   基于上面的源码分析,反正都会运行dispatch(),那么我们就可以对其进行方法扩展。

from django.shortcuts import HttpResponse
from django.views import View

class C1(View):
    def dispatch(self, request, *args, **kwargs):
        # 添加逻辑,如登录。必须登录后才能进行操作
        return super(C1,self).dispatch(request, *args, **kwargs)
    def get(self,request):
        return HttpResponse("get方法...")

    def post(self,request):
        return HttpResponse("post方法...")

FBV装饰器

   给FBV添加装饰器,一般我们会单独将request提取出来。

from django.shortcuts import HttpResponse

def wrapp(func):
	def inner(request,*args,**kwargs):
		return func(request,*args,**kwargs):
	return inner

@wrapp
def other(request):
	return HttpResponse("other...")

CBV装饰器

   CBV添加装饰器的方式有三种。针对不同的应用场景可以使用不同的装饰器添加方法,但是有一点是相同的。

   都需要先导入下面这个模块:

from django.utils.decorators import method_decorator

   方式一:为单独的某个功能添加装饰器。

from django.shortcuts import HttpResponse
from django.views import View
from django.utils.decorators import method_decorator

def wrapp(func):
	def inner(request,*args,**kwargs):
		return func(request,*args,**kwargs):
	return inner

class Other(View):
    @method_decorator(wrapp)
    def get(self, request):
        return HttpResponse("get方法...")
        
     @method_decorator(wrapp)
    def post(self,request):
        return HttpResponse("post方法...")

   方式二:为CBV类添加装饰器,可指定该装饰器作用与类下的那些方法(可添加多个)。

from django.shortcuts import HttpResponse
from django.views import View
from django.utils.decorators import method_decorator

def wrapp(func):
	def inner(request,*args,**kwargs):
		return func(request,*args,**kwargs):
	return inner


@method_decorator(wrapp,name="get")
@method_decorator(wrapp,name="post")
class Other(View):
    def get(self,request):
        return HttpResponse("get方法...")

    def post(self,request):
        return HttpResponse("post方法...")

   方式三:为dispatch方法添加装饰器,该装饰器会作用于所有的方法。(因为入口方法不是post,就是get

from django.shortcuts import HttpResponse
from django.views import View
from django.utils.decorators import method_decorator

def wrapp(func):
	def inner(request,*args,**kwargs):
		return func(request,*args,**kwargs):
	return inner


class Other(View):
	@method_decorator(wrapp)
    def dispatch(self, request, *args, **kwargs):
        return super(Other,self).dispatch(request, *args, **kwargs)
        
    def get(self,request):
        return HttpResponse("get方法...")

    def post(self,request):
        return HttpResponse("post方法...")
posted @ 2020-09-17 21:33  云崖先生  阅读(262)  评论(0编辑  收藏  举报