zoukankan      html  css  js  c++  java
  • Django 04

    视图层

    三个常用方法

    • HttpResponse
    • render
    • redirect
    • render和redirect都继承了HttpResponse
    • 视图函数必须要有一个返回值, 而且这个返回值必须是HttpResponse对象

    JsonResponse

    通常情况下, 前后端数据交互采用的都是json字符串 (字典)

    我们先来回忆下前后端序列化和反序列化

    操作 python后端 js前端
    序列化 json.dumps() JSON.stringify()
    反序列化 json.loads() JSON.parse()

    现在我们要向前端返回一个json字符串, 该怎么操作呢?

    利用我们已经学习的知识, 很容易实现

    def index(request):
        user_dic = {'name': 'bigb', 'password': '123'}
        json_str = json.dumps(user_dic)
        return HttpResponse(json_str)
    

    现在我们访问 /index/, 页面显示为 {"name": "bigb", "password": "123"}

    so far so good, right?

    现在我们把字典改一下

    def index(request):
        # 将name改为我的中文名
        user_dic = {'name': '彭于晏', 'password': '123'}
        json_str = json.dumps(user_dic)
        return HttpResponse(json_str)
    

    现在我们再来访问 /index/, 结果: {"name": "u5f6du4e8eu664f", "password": "123"}

    这又是咋回事呢?

    我们来看一下dumps方法的源代码

    原来dumps方法有一个默认参数 ensure_ascii=True , 默认的编码格式是ascii编码, ascii是不支持中文字符的

    因此我们只要修改 ensure_ascii=Flase 就可以了

    def index(request):
        user_dic = {'name': '彭于晏', 'password': '123'}
        # 修改默认参数ensure_ascii为Flase
        json_str = json.dumps(user_dic, ensure_ascii=False)
        return HttpResponse(json_str)
    

    再来访问 /index/, 完美将我的中文名显示出来了 {"name": "彭于晏", "password": "123"}


    扯了半天, 差点把主角 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(JsonResponse, self).__init__(content=data, **kwargs)
    

    显然, JsonResponse是一个类, 同样也继承了HttpResponse

    再来看这一行代码 data = json.dumps(data, cls=encoder, **json_dumps_params)

    可见JsonResponse帮我们对数据进行了json序列化处理, 我们直接传入数据即可

    def index(request):
        user_dic = {'name': '彭于晏', 'password': '123'}
        return JsonResponse(user_dic)
    

    来, /index/走一个, 然后... {"name": "u5f6du4e8eu664f", "password": "123"}

    还是同样问题, 反正都是用的也是dumps方法, 将 ensure_ascii=Flase 传给dumps方法不就行了吗

    怎么传呢? 看图

    因此

    def index(request):
        user_dic = {'name': '彭于晏', 'password': '123'}
      	# 借助JsonResponse, 给其中的dumps方法传参, 修改其默认的ascii编码为Flase
        return JsonResponse(user_dic, json_dumps_params={'ensure_ascii': False})
    

    然后, 我们再来 /index/, 结果: {"name": "彭于晏", "password": "123"}

    至此, 我们就把 JsonResponse 方法给拿下了!


    然而自觉告诉我, 事情没那简单, 我们再来看

    可见 JsonResponse 默认只支持字典这一种数据格式

    如果我们想让其支持更多的数据格式, 很简单, 源码中注释也提示我们了, 将 safe=True 这个默认参数设置为 Flase 就行了

    def index(request):
        lis = ['彭于晏', '123']
      	# 设置safe=False参数, 使得JsonResponse支持更多数据格式
        return JsonResponse(user_dic, json_dumps_params={'ensure_ascii': False}, safe=Flase)
    

    FBV 和 CBV

    • FBV 基于函数视图
    • CBV 基于类视图, 也就是说我们可以在视图层来定义类

    FBV我们已经很熟悉了, 我们来看下CBV如何使用

    首先我们先来看下CBV的路由层的写法

    urlpatterns = [
      
        url(r'^login/', views.Login.as_view()),
    ]
    

    再来看下CBV 视图层的写法, 注意我们定义视图类时要继承View类, 需要先导入View

    from django.shortcuts import render, HttpResponse, redirect
    from django.views import View
    
    
    # 要继承View类
    class Login(View):
        def get(self, request):
            return render(request, 'login.html')
    
        def post(self, request):
            return HttpResponse('login_post')
    

    get方法我们返回了一个html文件

    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>login</title>
    </head>
    <body>
    <form action="" method="post">
        <h1>点击提交post请求</h1>
        <input type="submit">
    </form>
    </body>
    </html>
    

    路由层和视图层还有模板层都写好了, 访问一下 /login/ 试试吧

    显然, 我们访问 /login/, 既向后端发送get请求, 触发了Login这个类下面的get方法, get方法将我们的login.html文件返回了到了前端

    然后我们点击页面上的提交按钮

    当我们点击提交按钮, 向后端发放post请求, 触发了Login这个类下面的post方法

    感觉CBV有点牛逼的样子啊, 可以根据前端的请求方式, 触发类中对应的方法

    是如何做到的呢?

    我们从路由层开始入手

    通过源码我们发现 as_view 是一个函数, 我们知道, 函数加括号就会调用, 因此项目一启动, 就执行了 as_view 这个函数

    执行这个函数发生了啥呢? 不废话, 我们直接上源码 (为了方便分析, 这里剔除了一些代码)

     @classonlymethod
        def as_view(cls, **initkwargs):
           
            def view(request, *args, **kwargs):
                # self是我们定义视图类的对象
                self = cls(**initkwargs)
                if hasattr(self, 'get') and not hasattr(self, 'head'):
                    self.head = self.get
                self.request = request
                self.args = args
                self.kwargs = kwargs
                # 这里调用了dispatch函数, 因此view函数的返回值就是dispatch的返回值
                return self.dispatch(request, *args, **kwargs)
            
            view.view_class = cls
            view.view_initkwargs = initkwargs
    
            return view
    
        def dispatch(self, request, *args, **kwargs):
         	# 判断请求方式是符合http的请求方式
            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)
    
    1. 显然, as_view 是一个闭包函数, 调用 as_view 会返回内层函数 view , 那这个 view 函数又是个什么鬼啊

    2. 我们来看 view 函数的返回值: return self.dispatch(request, *args, **kwargs), 这里调用了dispatch 函数, 因此 view 函数的返回值就是 dispatch 函数的返回值, 现在我们将目光转移到 dispatch 函数上

    3. 老规矩, 我们先来看 dispatch 函数的返回值: return handler(request, *args, **kwargs), 又来了

    handler 函数, 快被整懵逼了... 不慌, 容我对源码稍作处理:

    handler = getattr(self, 请求方式, 默认值) self是我们定义的视图类的对象

    这下明白了吗? 就是利用反射来获取对象的方法, 如果请求方式是post, 这个handler就是对象的post方法啦

    总结:

    1. 不管是FBV还是CBV, 路由层中匹配的都是函数
    2. 执行view函数---> 执行dispatch函数---> 通过反射得到类对象的方法并执行, 最终执行view函数返回的就是执行类对象方法返回的结果


    别急, 还没完, 再补充一个知识点: 如何 给视图类加装饰器

    1. 在方法上直接加装饰器 @method.decorator(time_counter)
    2. 在类上面 @method.decorator(time_counter, name='get') 给get方法添加装饰器
    # 需要导入
    from django.utils.decorators import method_decorator
    
    
    # 在类上面给某个方法添加装饰器
    @method_decorator(time_counter, name='get')
    class Login(View):
        def get(self, request):
            return render(request, 'login.html')
    
        # 在方法上添加装饰器
        @method.decorator(time_counter)
        def post(self, request):
            return HttpResponse('login_post')
    
    1. 在类中定义一个dispatch方法, 给dispatch添加装饰器, 这样就给类下面的所有方法都加装了装饰器

      (把上面CBV原理搞懂了, 这个就不难理解了, 这里就不做过多阐述了)

    from django.utils.decorators import method_decorator
    
    
    @method_decorator(time_counter, name='dispatch')
    class Login(View):
      
        def dispatch(self, request, *args, **kwargs):
            return super().dispatch(request, *args, **kwargs)   
    

    模板层

    模板语法

    模板语法只有两种格式:

    • {{ }} 变量相关
    • {% %} 逻辑和功能相关

    模板传值

    我们来看下模板传值支持哪些数据格式

    首先我们需要知道的是视图层 给html文件传值的两种方式

    1. 用字典传值
    2. locals() 传值, 会将当前所在的名称空间中所有的名字传递给html文件
    # views.py
    def test(request):
        i = 1
        f = 1.1
        s = 'django'
        l = [1, 2, 3]
        d = {'name': 'bigb', 'super_power': 'handsome'}
        t = (1, 2, 3)
        se = {1, 2, 3}
        b = True
    
        # 字典传值
        # return render(request, 'test.html', {'i': i, 'f': f, 's':s, ...})
    
        # locals()传值
        return render(request, 'test.html', locals())
    

    模板层接收视图层传过来的数据

    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>模板传值</title>
    </head>
    <body>
    <p> {{ i }}</p>
    <p> {{ f }}</p>
    <p> {{ s }}</p>
    <p> {{ l }}</p>
    <p> {{ d }}</p>
    <p> {{ t }}</p>
    <p> {{ se }}</p>
    <p> {{ b }}</p>
    </body>
    </html>
    

    别忘了路由层的的路由和视图函数的匹配

    万事俱备, 让我们走起...

    可见模板语法传值支持python 8大基本数据类型

    我们再来试下模板传值可不可以传函数

    def test(request):
    
        def func():
            return 'func的返回值'
        
        # locals()传值
        return render(request, 'test.html', locals())
    
    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>模板传值</title>
    </head>
    <body>
    
    <p>{{ func }}</p>
        
    </body>
    </html>
    
    

    结果

    因此在给html文件传递函数名的时候, 会自动加括号调用, 并将函数的返回值展示出来

    需要注意的是:

    ​ 模板语法不支持函数传参, 也就意味着 在给html文件传递函数名的时候, 必须是无参函数或者无须传参的函数

    既然都可以传函数, 那能不能传类和对象呢?

    def test(request):
      
        class MyClass:
            def func1(self):
                return 'func1'
    
            @classmethod
            def func2(cls):
                return 'func2'
            
            @staticmethod
            def func3():
                return 'func3'
            
        my_obj = MyClass()
    
        # locals()传值
        return render(request, 'test.html', locals())
    
    
    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>模板传值</title>
    </head>
    <body>
    
    <p>传类{{ MyClass }}</p>
    <p>传对象{{ my_obj }}</p>
        
    </body>
    </html>
    

    我们访问/test/看下结果

    我们传的明明是一个类和一个对象, 咋变成两个对象了呢?

    原来在给html文件传递类的时候, 模板语法会自动加括号调用类实例化对象

    当然上面的结果也证明了: 模板语法支持传递对象

    需要说明的是我们还可以在html文件中调用对象的属性和方法

    我们来总结一下:

    1. 对象传值支持python 8大基本数据类型, 函数, 类和对象

    2. 只要是能加括号调用的, 传给到html页面上都会自动加括号调用

    过滤器

    什么是过滤器呢?

    过滤器就是模板语法提供的一些内置方法, 可以帮助我们在html文件里快速处理数据

    我们先来看下过滤器的语法结构

    {{ 参数1|过滤器:[参数2 ]}}

    注意: 过滤器参数最多不超过两个

    下面是几个常用的过滤器

    过滤器 效果
    {{ arg|length }} 返回参数arg的长度
    {{ arg1|add:arg2 }} 数字相加, 字符串拼接
    {{ arg|slice:'0:5:2' }} 切片, 顾头不顾尾, 支持切片
    {{ file_size|filesizeformat }} 文件大小的格式处理
    {{ str|truncatechars:n }} 从str中截取n个字符, 包含 '...'
    {{ str|truncatewords:n }} 从str中截取n个单词, 不包含三个点
    {{ arg|default:s为空 }} arg有值展示值, 没值返回默认值
    {{ str|safe }} 前端取消转义, 将含有标签的str转成前端代码

    后端取消转义: mark_safe(str)

    from django.utils.safestring import mark_safe
    
    def test(request):
       
        s = '<h1>这是一个h1标签</h1>'
        res = mark_safe(s)
    
        # locals()传值
        return render(request, 'test.html', locals())
    

    标签

    **if判断 **

    {% if s %}
        <p>s</p>
    {% else %}
        <p>s为空</p>
    {% endif %}
    

    for循环

    <!--对lis=[1, 2, 3]进行for循环-->
    {% for foo in lis %}
        <p>{{ foo }}</p>
    {% endfor %}
    

    for循环中有一个forloop对象

    这玩意可以判断第一次循环和最后一次循环, 还可以记录循环次数

    <!--forloop对象-->
    {% for foo in lis %}
        <p>{{ fooloop }}</p>
    {% endfor %}
    

    {% empty %} 当for循环的对象是空的时候, 就会触发

    <!--lis为空-->
    {% for foo in lis %}
        <p>{{ foo }}</p>
    {% empty %}
    	<p>lis为空</p>
    {% endfor %}
    
    

    结合使用

    <!--对lis=[1, 2, 3]进行for循环-->
    {% for foo in lis %}
        {% if forloop.first %}
            <p>这是第一次循环</p>
    
        {% elif forloop.last %}
            <p>这是最后一次循环</p>
    
        {% else %}
            <p>这是中间的循环</p>
    
        {% endif %}
    {% empty %}
    	<p>lis为空</p>
    {% endfor %}
    
    

    取别名: {% with... as ... %}

    注意只能在with上下文中使用

    {% with lis as l %}
    	<p>l</p>
    {% endwith %}
    

    注意: 不管你是字典还是列表, 模板语法的取值只有一种方式, 统一采用 . 取值的方式

    自定义过滤器和标签

    1. 在应用名下新建一个 templatetags 文件夹

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

    3. mytag.py 文件中

    from django.template import Library
    
    register = Library()
    
    # 自定义过滤器
    @register.filter(name='my_sum')
    def index1(a, b):
        return a + b
    
    
    # 自定义标签
    @register.simple_tag(name='my_tag')
    def index2(a, b, c, d):
        return f'{a}-{b}-{c}-{d}'
    
    # 自定义inclusion_tag
    """
    是一个函数, 能够接收参数, 可以返回一段html代码, 在哪里调用, 就把这段html代码放在哪里
    """
    @register.inclusion_tag(filename='mytag.html', name='my_inclu')
    def index3(n):
        lis = []
        for i in range(n):
            lis.append(f'第{n}项')
        return locals()
        pass
    
    
    
    
    • 如何使用自定义过滤器和标签
    <!--首先将我们的自定义的py文件给导到html页面来-->
    {% load mytag %}
    
    <p>{ 1|my_sum:1 }</p>
    <p>{% my_tag 'a' 'b' 'c' 'd' %}</p>
    {% my_inclu 5 %}
    
    

    注意: 自定义的过滤器可以在逻辑语句中使用, 而自定义的标签不可以

    模板的继承

    • 首先要在我们想要继承的html文件上划定可修改区域
    {% block <blockname> %}
    可修改区域
    {% endblock %}
    
    • 继承
    {% extends 'home.html' %}
    
    • 写可修改区域代码
    {% block <对应父模板的可修改区域的名字> %}
    可修改区域
    {% endblock %}
    
    • 在子页面沿用父页的内容
    {% block <对应父模板的可修改区域的名字> %}
    
    {{ block.super }}
    
    {% endblock %}
    

    模板的导入

    • 将html文件当做模块导入使用
      • {% include 'html文件' %}
  • 相关阅读:
    PAT (Advanced Level) 1060. Are They Equal (25)
    PAT (Advanced Level) 1059. Prime Factors (25)
    PAT (Advanced Level) 1058. A+B in Hogwarts (20)
    PAT (Advanced Level) 1057. Stack (30)
    PAT (Advanced Level) 1056. Mice and Rice (25)
    PAT (Advanced Level) 1055. The World's Richest (25)
    PAT (Advanced Level) 1054. The Dominant Color (20)
    PAT (Advanced Level) 1053. Path of Equal Weight (30)
    PAT (Advanced Level) 1052. Linked List Sorting (25)
    PAT (Advanced Level) 1051. Pop Sequence (25)
  • 原文地址:https://www.cnblogs.com/bigb/p/11939461.html
Copyright © 2011-2022 走看看