• Django之视图层与模板层


    一、视图层

    视图函数(类)简称为视图,就是一个普通的函数(类),它的功能是接收web请求,并返回web响应.

    研究视图函数需要熟练掌握请求对象(HttpRequest)和相应对象(HttpResponse)

    1.1请求对象(HttpRequest)

    1.1.1HttpRequest请求对象常用属性

    #part1
    一.HttpRequest.method
    获取请求使用的方法(值为纯大写的字符串格式)。例如:"GET"、"POST"
     应该通过该属性的值来判断请求方法
    二.HttpRequest.GET
    值为一个类似于字典的QueryDict对象,封装了GET请求的所有参数,可通过HttpRequest.GET.get('键')获
    取相对应的值
    
    三.HttpRequest.POST
     值为一个类似于字典的QueryDict对象,封装了POST请求所包含的表单数据,可通过
    HttpRequest.POST.get('键')获取相对应的值
    
     针对表单中checkbox类型的input标签、select标签提交的数据,键对应的值为多个,需要用:
    HttpRequest.POST.getlist("hobbies")获取存有多个值的列表,同理也有HttpRequest.GET.getlist("键")
    #part2
    一.HttpRequest.body
     当浏览器基于http协议的POST方法提交数据时,数据会被放到请求体中发送给django,django会将接收到的请求
    体数据存放于HttpRequest.body属性中,因为该属性的值为Bytes类型,所以通常情况下直接处理Bytes、并从中提
    取有用数据的操作是复杂而繁琐的,好在django会对它做进一步的处理与封装以便我们更为方便地提取数据,比如
     对于form表单来说,提交数据的常用方法为GET与POST
     1:如果表单属性method='GET',那么在提交表单时,表单内数据不会存放于请求体中,而是会将表单数据按照
    k1=v1&k2=v2&k3=v3的格式放到url中,然后发送给django,django会将这些数据封装到request.GET中,注意此
    时的request.body为空、无用
     2:如果表单属性method='POST',那么在提交表单时,表单内的所有数据都会存放于请求体中,在发送给django
    后会封装到request.body里,此时django为了方便我们提取数据,会request.body的数据进行进一步的处理,具
    体如何处理呢,需要从form表单提交数据的编码格式说起:
     form表单对提交的表单数据有两种常用的编码格式,可以通过属性enctype进行设置,如下
     编码格式1(默认的编码格式):enctype="application/x-www-form-urlencoded"
     编码格式2(使用form表单上传文件时只能用该编码):enctype="multipart/form-data"
     如果form表单提交数据是按照编码格式1,那么request.body中数据的格式类似于GET方法的数据格式,如
    k1=v1&k2=v2,此时django会将request.body中的数据提取出来封装到request.POST中方便我们提取
     如果form表单提交数据是按照编码格式2,那么request.body中数据的格式为b'------
    WebKitFormBoundaryKtcwuksQltpNprep
    Content-Disposition: form-data;......',,此时django
    会将request.body中的数据提取出来封装到request.POST中,将上传的文件数据专门提取出来封装到
    request.FILES属性中
     强调:毫无疑问,编码格式2的数据量要大于编码格式1,如果无需上传文件,还是推荐使用更为精简的编码格式1
    
     我们除了可以采用form表单向django提交数据外,还可以采用ajax技术,ajax可以提交的数据格式有:1、编码
    格式1 2、编码格式2 3、json,当ajax采用POST方法提交前两种格式的数据时,django的处理方案同上,但是当
    ajax采用POST方法提交json格式的数据时,django会将接收到的数据存放于HttpRequest.body,此时需要我们自
    己对HttpRequest.body属性值做反序列化操作,
    具体的,我们在讲解ajax时再做具体介绍
    
    二.HttpRequest.FILES
     如果使用form表单POST上传文件的话,文件数据将包含在HttpRequest.FILES属性中。
    该属性值为一个类似于字典的对象,可以包含多组key:value(对应多个上传的文件),其中每个key为<input
    type="file" name="" /> 中name属性的值,而value则为对应的文件数据
    强调:HttpRequest.FILES 只有在请求的方法为POST 且提交的<form> 带有enctype="multipart/formdata" 的情况下才会包含数据。否则,FILES 将为一个空的类似于字典的对象。
    
    #part3
    一.HttpRequest.path
    获取url地址的路径部分,只包含路径部分
    二.HttpRequest.get_full_path()
    获取url地址的完整path,既包含路径又包含参数部分
    如果请求地址是http://127.0.0.1:8001/order/?name=ylpb&age=10#_label3,
    HttpRequest.path的值为"/order/"
    HttpRequest.get_full_path()的值为"/order/?name=ylpb&age=10"
    
    #part4
    一.HttpRequest.META
     值为包含了HTTP协议的请求头数据的Python字典,字典中的key及期对应值的解释如下
     CONTENT_LENGTH —— 请求的正文的长度(是一个字符串)。
     CONTENT_TYPE —— 请求的正文的MIME类型。
     HTTP_ACCEPT —— 响应可接收的Content-Type。
     HTTP_ACCEPT_ENCODING —— 响应可接收的编码。
     HTTP_ACCEPT_LANGUAGE —— 响应可接收的语言。
     HTTP_HOST —— 客服端发送数据的目标主机与端口
     HTTP_REFERER —— Referring 页面。
     HTTP_USER_AGENT —— 客户端使用的软件版本信息
     QUERY_STRING —— 单个字符串形式的查询字符串(未解析过的形式)。
     REMOTE_ADDR —— 客户端的IP地址。
     REMOTE_HOST —— 客户端的主机名。
     REMOTE_USER —— 服务器认证后的用户。
     REQUEST_METHOD —— 一个字符串,例如"GET" 或"POST"。
     SERVER_NAME —— 服务器的主机名。
     SERVER_PORT —— 服务器的端口(是一个字符串)。
     从上面可以看到,除 CONTENT_LENGTH 和 CONTENT_TYPE 之外,HTTP协议的请求头数据转换为 META 的键
    时,
     都会
     1、将所有字母大写
     2、将单词的连接符替换为下划线
     3、加上前缀HTTP_。
     所以,一个叫做 X-Bender 的头部将转换成 META 中的 HTTP_X_BENDER 键。
    
    注意:下述常用属性暂且了解即可,待我们讲到专门的知识点时再专门详细讲解
    二.HttpRequest.COOKIES
    一个标准的Python 字典,包含所有的cookie。键和值都为字符串。
    三.HttpRequest.session
     一个既可读又可写的类似于字典的对象,表示当前的会话。只有当Django 启用会话的支持时才可用。
    11.HttpRequest.user(用户认证组件下使用)
    一个 AUTH_USER_MODEL 类型的对象,表示当前登录的用户。
    2.HttpRequest.is_ajax()
    如果请求是通过XMLHttpRequest 发起的,则返回True,方法是检查 HTTP_X_REQUESTED_WITH 相应的首部
    是否是字符串'XMLHttpRequest'。
    大部分现代的 JavaScript 库都会发送这个头部。如果你编写自己的 XMLHttpRequest 调用(在浏览器端),
    
    你必须手工设置这个值来让 is_ajax() 可以工作。
    如果一个响应需要根据请求是否是通过AJAX 发起的,并且你正在使用某种形式的缓存例如Django 的 cache
    middleware,
     你应该使用 vary_on_headers('HTTP_X_REQUESTED_WITH') 装饰你的视图以让响应能够正确地缓存
    

    1.2响应对象(HttpResponse)

    响应可以是一张HTML网页、一个404错误,一张图片,一个XML文档、重定向到其他视图等。特点:无论视图本身包含什么逻辑都必须要返回响应,另外视图函数放在views.py是约定俗成的,并不是必须要放在这里。

    1.2.1HttpResponse()

    括号内直接跟一个具体的字符串作为响应体。

    1.2.2render()

    render(request,template_name[,context])
    参数:
    1. request:用于生成响应的请求对象,固定必须传入的第一个参数
    2.template_name:要使用模板的完整名称,必须传入,render默认回去templates目录下查找模板文件
    3.context:可选参数,可以传入一个字典用来替代模板文件中的变量
    render的功能可总结为:根据给定的字典渲染模板,并返回一个渲染后的HttpResponse对象。
    

    1.2.3redirect()

    重定向为指定的地址。

    def home(request):
        #return redirect('/login')如果重定向为本站的其他页面则可直接写本站其他页面的后缀
        return redirect('https://www.cnblogs.com/ghylpb/')#如果重定向为其他网站则直接写其它网站的网址即可
    

    1.3JsonResponse

    JsonResponse内部使用json模块对传入的数据类型型进行序列化,它的默认数据类型只有字典,当将safe参数置为False时,可以序列化其它数据类型,它继承了HttpResponse类,可以对请求做出响应。

    用json实现JsonResponse的功能:

    def index(request):
        user_dic = {'name':'小张','password':'123'}
        # json内部会使用ASCII码对所有的数据进行转码,所以如果转码之后我们将无法获得中文信息处理方法如下,将json的ensure_ascii参数置为False就可以
        json_str = json.dumps(user_dic,ensure_ascii=False)
        return HttpResponse(json_str)
    

    JsonResponse:

      def index(request):
        user_dic = {'name':'小张','password':'123'}
        return JsonResponse(user_dic,json_dumps_params={'ensure_ascii':False})
    

    更改JsonResponse序列化的数据类型:

      def index(request):
        l = [1,2,3,4,5,6,7,]
        # JsonResponse默认只序列化字典 如果你想序列化其他数据类型(json模块能够序列化的) 你需要加一个safe参数
        return JsonResponse(l,safe=False)
    

    1.4FBV与CBV

    Django的视图层由两种形式构成:FBV基于函数的视图(Function base view)和CBV基于类的视图(Class base view)

    1.4.1FBV

    我们前面使用的视图函数就是FBV。

    路由的书写方法:url(r'^name/',views.name)

    1.4.2CBV

    CBV引入面向对象的思想对数据进行更高程度的封装。如下例所示:

    class MyLogin(View):
        def get(self,request):
            print('我是MyLogin里面的get方法')
            return render(request,'login.html')
    
        def post(self,request):
            print('我是MyLogin里面的post方法')
            return HttpResponse('post')
    

    路由的书写方法:url(r'^login/',views.MyLogin.as_view())

    从路由的书写可以看出这里执行的是类的方法,而方法的本质还是函数所以CBV在路由匹配上的本质还是FBV。

    1.5CBV源码

    为什么CBV能够根据不同的请求方式自动执行不同的代码呢?下面我们一起看一下CBV的源码

    class View(object):
        """
        Intentionally simple parent class for all views. Only implements
        dispatch-by-method and simple sanity checking.
        """
    
        http_method_names = ['get', 'post', 'put', 'patch', 'delete', 'head', 'options', 'trace']
    
        def __init__(self, **kwargs):
            """
            Constructor. Called in the URLconf; can contain helpful extra
            keyword arguments, and other things.
            """
            # Go through keyword arguments, and either save their values to our
            # instance, or raise an error.
            for key, value in six.iteritems(kwargs):
                setattr(self, key, value)	
        @classonlymethod
        def as_view(cls, **initkwargs):
            """
            Main entry point for a request-response process.
            """
            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)#cls是我们自己定义的类Mylogin,self是我们自定义的类实例化的对象。
                if hasattr(self, 'get') and not hasattr(self, 'head'):#这里的get是干啥使的?
                    self.head = self.get
                self.request = request
                self.args = args
                self.kwargs = kwargs
                # 对象查找属性和方法的顺序:先自己再自己的类再父类
                return self.dispatch(request, *args, **kwargs)
            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
    
        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.
            #判断当前的请求方式在不在默认的八个方法内,以get请求为例
            if request.method.lower() in self.http_method_names:
                #这里利用反射去我们自己定义的类实例化的对象中查找get属性或方法getattr(obj,'get')
                handler = getattr(self, request.method.lower(), self.http_method_not_allowed)
            else:
                handler = self.http_method_not_allowed
            return handler(request, *args, **kwargs)#加括号调用该方法
    

    给CBV内部的方法加装饰器:

    方法1:使用内置模块

    from django.utils.decorators import method_decorator
    #先导入 method_decorator
    #可以指定给哪个方法装(outter是我们定义的装饰器名称,这里略去装饰器的定义代码)
    # @method_decorator(outter,name='post')
    # @method_decorator(outter,name='dispatch')
    class MyLogin(View):
        @method_decorator(outter)
        def dispatch(self, request, *args, **kwargs):  # 如果你想在视图函数执行之前做一些操作,你可以在你的CBV中定义dispatch方法来拦截
            return super().dispatch(request,*args,**kwargs)
      
        # @method_decorator(outter)  # 1.推荐写法
        def get(self,request):
            print('我是MyLogin里面的get方法')
            return render(request,'login.html')
        def post(self,request):
            print('我是MyLogin里面的post方法')
            time.sleep(1)
            return HttpResponse('post')
    

    方法2:把类的方法当成普通函数,直接在对应的方法上添加。

    class MyLogin(View):
        @outter
        def get(self,request):
            print('我是MyLogin里面的get方法')
            return render(request,'login.html')
        @outter
        def post(self,request):
            print('我是MyLogin里面的post方法')
            time.sleep(1)
            return HttpResponse('post')
    

    二、模板层

    2.1模板语法

    2.1.1模板语法的取值

    模板语法的取值方式只有一种:统一采用句点符取值(点的方式取值)

    如:

    #python代码
    user_obj = {'name':'zgh','pwd':123,'hoppy':['book','music','movie']}
    #模板语法取值
    {{ user_obj.hobby.0}}#book
    #句点符取值,如果从字典取值则点key值,如果从列表取值则点索引号
    

    模板语法有两种书写格式:

    {{}}#变量相关
    {% %}#逻辑相关
    

    2.1.2模板传值

    模板支持的数据类型

    模板支持的数据类型:整型、浮点型、字符串、字典、列表、元组、集合、bool,也就是支持python基本的数据类型全都支持。

    模板传值

    1.传函数名:{{ 函数名 }}

    给HTML传函数名的时候,模板语法会自动加括号调用该函数,并将函数的返回值当做页面展示的依据,注意模板语法不支持函数传参,也就是说只能给页面传无参函数。

    2.传类名:{{ 类名 }}

    给HTML传类名的时候会自动加括号实例化产生对象,在HTML页面可以进行如下对对象的使用。

    <p>传对象:{{ obj }}</p>
    <p>{{ obj.get_self }}</p>
    <p>{{ obj.get_cls }}</p>
    <p>{{ obj.get_func }}</p>
    

    模板传值特点:只要能够加括号调用的类函数等传到HTML页面都会自动加上括号调用。

    2.2过滤器

    过滤器类似于python的内置函数,用来把视图函数传入的变量值加以修饰以后再显示

    语法结构:{{ 变量名 | 过滤器名 : 传给过滤器的参数 }}

    注意:过滤器最多只能有两个参数

    常用的内置过滤器:

    #1、default
    #作用:如果一个变量值是False或者为空,使用default后指定的默认值,否则,使用变量本身的值,如果
    value=’‘则输出“nothing”
    {{ value|default:"nothing" }}
    #2、length
    #作用:返回值的长度。它对字符串、列表、字典等容器类型都起作用,如果value是 ['a', 'b', 'c', 'd'],那
    么输出是4
    {{ value|length }}
    #3、filesizeformat
    #作用:将值的格式化为一个"人类可读的"文件尺寸(如13KB、4.1 MB、102bytes等等),如果 value 是12312312321,输出将会是 11.5 GB
    {{ value|filesizeformat }}
    #4、date
    #作用:将日期按照指定的格式输出,如果value=datetime.datetime.now(),按照格式Y-m-d则输出2019-02-02
    {{ value|date:"Y-m-d" }}
    #5、slice
    #作用:对输出的字符串进行切片操作,顾头不顾尾,如果value=“ylpb“,则输出"yl"
    {{ value|slice:"0:2" }}
    #6、truncatechars
    #作用:如果字符串字符多于指定的字符数量,那么会被截断。截断的字符串将以可翻译的省略号序列(“...”)结尾,
    如果value="hello world abc def ",则输出"hello...",注意8个字符也包含末尾的3个点
    {{ value|truncatechars:8 }}
    #7、truncatewords
    #作用:同truncatechars,但truncatewords是按照单词截断,注意末尾的3个点不算作单词,如果value="
    hello world abc def" ,则输出"hello world ..."
    {{ value|truncatewords:2 }}
    #8、safe
    #作用:出于安全考虑,Django的模板会对HTML标签、JS等语法标签进行自动转义,例如value="
    <script>alert(123)</script>",模板变量{{ value }}会被渲染成
    &lt;script&gt;alert(123)&lt;/script&gt;交给浏览器后会被解析成普通字符”<script>alert(123)
    </script>“,失去了js代码的语法意义,但如果我们就想让模板变量{{ value }}被渲染的结果又语法意义,那么就
    用到了过滤器safe,比如value='<a href="https://www.baidu.com">点我啊</a>',在被safe过滤器处理后
    就成为了真正的超链接,不加safe过滤器则会当做普通字符显示’<a href="https://www.baidu.com">点我啊
    </a>‘
    {{ value|safe }}
    

    最为常用的过滤器有:统计长度、自动转文件大小格式、展示带有标签的文本。

    2.3标签

    标签(逻辑相关)是为了在模板中完成一些特殊的功能,语法为{% %},下面介绍几个常用的标签。

    2.3.1for标签

    '''语法:{% for user in 容器类数据类型 %}
                 for循环体
             {% endfor %}'''
    #如下面代码循环循环出列表中的每一个元素并展示元素的属性
    {% for user in user_list %}
    <tr>
    <td>{{ user.id }}</td>
    <td>{{ user.auth_name }}</td>
    <td>{{ user.detail }}</td>
    <td>
    <a href="{% url 'app01_edit_author'%}?edit_id={{ user.id }} " class="btn btn-primary btn-xs">编辑</a>
    <a href="{% url 'app01_delete_author' %}?delete_id={{ user.id }}" class="btn btn-danger btn-xs">删除</a>
    </td>
    </tr>
    {% endfor %}
    

    2.3.2if标签

    {% if 条件1 %}
    	执行内容1
    {% elif 条件2%}
    	执行内容2
    {% else %}
    	执行内容3
    {% endif %}
    
    #if 标签长和for标签联合使用如:
    {% for foo in l %}#l是一个列表
    {% if forloop.first %}
    <p>first</p>
    {% elif forloop.last %}
    <p>last</p>
    {% else %}
    <p>{{ foo }}</p>
    {% endif %}
    {% empty %}
    <p>当for循环的对象是空的时候会走</p>
    {% endfor %}
    

    2.3.3with标签

    with标签用来给一个复杂的变量名起别名,如果变量的值来自于数据库,在起别名后只需要使用别名即可,无需每次都向数据库发送请求重新获取变量的值,这里需要说明的是别名只能在with标签内部使用,如果在外部还是要用原名的。

    {% with 原变量名 as 别名 %}
    <p>{{ 别名 }}</p>
    {% endwith %}
    

    2.3.4csrf_token标签

    # 当用form表单提交POST请求时必须加上标签{% csrf_token%},该标签用于防止跨站伪造请求
    <form action="" method="POST">
     {% csrf_token %}
     <p>用户名:<input type="text" name="name"></p>
     <p>密码:<input type="password" name="pwd"></p>
     <p><input type="submit" value="提交"></p>
    </form>
    # 具体工作原理为:
    # 1、在GET请求到form表单时,标签{% csrf_token%}会被渲染成一个隐藏的input标签,该标签包含了由服务端
    生成的一串随机字符串,如<input type="hidden" name="csrfmiddlewaretoken"
    value="dmje28mFo...OvnZ5">
    # 2、在使用form表单提交POST请求时,会提交上述随机字符串,服务端在接收到该POST请求时会对比该随机字符
    串,对比成功则处理该POST请求,否则拒绝,以此来确定客户端的身份
    

    2.4自定义过滤器和标签

    当内置的过滤器或标签无法满足我们的需求时,我们可以自定义标签和过滤器。

    2.4.1自定义前的准备

    django支持用户自定义过滤器和标签但前提必须要先执行以下三步:

    1.在应用名下新建一个名为templatetags(必须是这个名字)的文件夹

    2.在该文件夹内新建一个任意名称的py文件

    3.在该py文件中先写下面两行代码(必须)

    from django.template import Library
    
    register = Library()
    

    完成上面的步骤就可以利用register来自定义过滤器和标签了。

    2.4.2自定义过滤器

    @register.filter(name='test')
    def index(a,b):
        return a + b
    #name为给过滤器起的名字,可以不写
    

    自定义的过滤器最多只能有两个参数。

    2.4.3自定义标签

    # 自定义标签,可以接受任意多个参数
    @register.simple_tag(name='mytag')
    def mytag(a,b,c,d):
        return '%s?%s?%s?%s'%(a,b,c,d)
    

    2.4.4自定义inclusion_tag

    inclusion_tag是一个函数,能够接受外界传入的参数,然后传递给一个HTML页面,页面获取数据,渲染完成后将渲染好的页面放到调用inclusion_tag的地方。

    @register.inclusion_tag('mytag.html',name='xxx')
    def index666(n):
        l = []
        for i in range(n):
            l.append('第%s项'%i)
            return locals()  # 将l直接传递给mytag.html页面
        # 给html页面传值的两种方式
        # 第一种,指名道姓当需要传递的变量名特别多的情况下 有点麻烦
        # return render(request,'test.html',{'n':n})
        # 第二种,使用locals()会将当前所在名称空间中所有的名字全部传递给html页面
    

    2.5模板的继承和导入

    在实际开发中,模板文件彼此之间可能会有大量的冗余代码,为此Django提供了专门的语法来解决这一问题,即模板的继承和导入。

    2.5.1继承

    如果你想使用某个已有的页面,首先你需要先在你想使用的页面上划定区域,在继承这个区域之后,你就可以使用划定的这个区域。

    block标签

    划定区域使用block标签,只需将你想要修改的区域放在block内部即可:

    {% block content %}
    划定的区域
    {% endblock %}
    

    extends标签

    在新的页面通过extends标签继承前面划定的区域:

    {% extends 'XXX.html' %} {#XXX.html是之前的页面#}
    				  
    {% block content %}
    修改模板中content区域内容
    {% endblock %}
    

    建议一个模板页面至少划分为三个区域:css区、html代码区、JS区,这样方便每一个页面都有自己独立的css和JS代码。

    2.5.2模板的导入

    include标签

    作用:在一个模板文件中引入另一个模板文件的内容,与继承不同的是include引用了目标模板的整个文件。

    {% include 'xxx.html' %}
    
  • 相关阅读:
    POJ--3164--Command Network【朱刘算法】最小树形图
    金典 SQL笔记(6)
    hdoj1106排序
    linux程序设计——运行SQL语句(第八章)
    iOS-UITextView-文本输入视图的使用
    HDU 5305 Friends(简单DFS)
    Android IntentService全然解析 当Service遇到Handler
    概要设计的要点
    DispatcherTimer
    原型模式
  • 原文地址:https://www.cnblogs.com/ghylpb/p/11938284.html
Copyright © 2020-2023  润新知