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文件' %}
  • 相关阅读:
    【翻译】谈 focus 和 blur 的事件代理
    【翻译】细分域名的优势
    Form窗体的Combobox键值对绑定
    日志
    ajaxload
    c#文件整理程序
    每天工作4小时的程序员
    每年这一天
    转载从交友到社交的个人成长
    旅行的意义
  • 原文地址:https://www.cnblogs.com/bigb/p/11939461.html
Copyright © 2011-2022 走看看