• Python-Flask 视图和模板(4)


    一、视图(V)

    视图是一个应用对请求进行响应的函数。 Flask 通过模型把进来的请求 URL 匹配到 对应的处理视图。视图返回数据, Flask 把数据变成出去的响应。 Flask 也可以反 过来,根据视图的名称和参数生成 URL 。

    返回值:其实返回值返回的都是一个响应对象,底层将返回的字符串包装成一个response对象。其实就是底层的Response父类的default-minetype设置成text/html类型,用户看到的就是网页内容。
    在这里插入图片描述

    1.response响应对象

    视图函数的返回值会自动转换为一个响应对象。如果返回值是一个字符串,那么会被转换为一个包含作为响应体的字符串、一个 200 OK 的状态代码 和一个 text/html 类型的响应对象(response对象)。返回值的类型其实不只仅仅有字符串类型,还支持返回字典,元组,响应对象,WSGI回调函数(# The return type must be a string, dict, tuple, Response instance, or WSGI callable)。

    • 如果视图返回的是一个响应对象,那么就直接返回它。

    • 如果返回的是一个字符串,那么根据这个字符串和缺省参数生成一个用于返回的 响应对象。

      • 如果视图函数单纯返回"****"的字符串的话,flask会自动进行一些封装让他变成浏览器可以读取的格式,也就是content-type = text/html,状态码为200。
    • 如果返回的是一个字典dict,那么它会被转换为一个 JSON 响应。

      • 如果字典还不能满足需求,还需要创建其他类型的 JSON 格式响应,那么调用 jsonify()创建一个响应对象,该函数会序列化任何支持的 JSON 数据类型。。
    • 如果返回的是一个元组,那么元组中的项目可以提供额外的信息。元组中必须至少 包含一个项目,且项目应当由 (response, status) 、 (response, headers) 或者 (response, status, headers) 组成。 status 的值会重载状态代码, headers 是一个由额外头部值组成的列表 或字典。

    • 如果以上都不是,那么 Flask 会假定返回值是一个有效的 WSGI 应用并把它转换为 一个响应对象。

    如果想要在视图内部掌控响应对象的结果,也就是响应头的信息不满足你的需求,需要在响应头中添加额外的信息,或者想要返回一个字符串页面内容,那么可以使用 make_response() 函数。
    在这里插入图片描述
    假设有如下视图:

    @app.errorhandler(404)
    def not_found(error):
        return render_template('error.html'), 404
    
    • 1
    • 2
    • 3

    可以使用 make_response() 包裹返回表达式,获得响应对象,并对该对象 进行修改,然后再返回:

    @app.errorhandler(404)
    def not_found(error):
        resp = make_response(render_template('error.html'), 404)
        resp.headers['X-Something'] = 'A value'
        return resp
    
    • 1
    • 2
    • 3
    • 4
    • 5
    @app.route('/index4')
    def index4():
        content = '''
    
    
    
        
        首页
        
    
    
    

    欢迎来到京东购物网站

    • hello
    • abc
    • world
    ''' response = make_response(content) # 返回值就是一个response对象 # 定制响应头 response.headers['mytest'] = '123abc' response.headers['myhello'] = 'hellohello' # 将定制好的response返回 return response
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37

    总结:
    (1)什么时候需要使用make_response?
    当返回一个视图的时候,需要定制响应头,则可以使用make_response()包裹相应信息。

    (2)response对象常用的属性与方法:

    print(response.content_type)
    print(response.headers)
    print(response.status_code)  # 200
    print(response.status)  # 200 OK
    
    • 1
    • 2
    • 3
    • 4

    (3)视图函数的返回值,response响应:

    • str 自动转成response对象
    • dict json
    • response对象 response对象
    • make_response() response对象
    • redirect() 重定向 302状态码
    • render_template() 模板渲染 + 模板

    2.request请求对象

    只需要导入,通过from flask import request,导入之后可以获取对象的属性和方法。

    属性:
    print(request.headers) # request对象 对象访问属性,也可以调用方法
    print(request.path)
    print(request.full_path)
    # 获取到提交的参数内容,例如get形式提交表单
    # /register2?username=zhangsan&address=Beijing
    print(request.base_url)
    print(request.url)

    跟请求方法相关的: 获取页面提交的内容
    (1)get:
    request.args 底层是字典的形式 主要获取get提交的请求参数
    如果是get请求格式是这个样子的:/register2?username=zhangsan&address=Beijing
    此时的username是form表单中表单元素的name值

    	print(request.args)  # dict类型  d = {'a':'aaa','b':'7878'}  d.get('b')  只能取到get请求的
        print(request.args.get('username'))   # 获取值
        print(request.args.get('address'))
    
    • 1
    • 2
    • 3

    (2)post:
    request.form 底层是字典的形式 主要获取post提交的请求参数
    注意post提交必须在路由中进行设置,通过methods = [‘GET’,‘POST’]
    按照此种形式:

        @app.route('/register2', methods=['GET', 'POST'])
        def register2():  # 获取页面提交的内容
            .......  内容省略
        获取数据:
        print(request.form)  # 如果请求方法是post,则需要通过request.form取值
        print(request.form.get('username'))
        print(request.form.get('address'))
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    二、模板

    1.返回模板文件

    如果想在视图函数中获取模板xxx.html的内容则通过render_template()

    render_template(‘模板名称’) 返回值是一个字符串,主要是通过模板引擎(Jinjia2)去找到文件并将模板内容转成字符串的形式。
    在这里插入图片描述

    @app.route('/register')
    def register():
        return render_template('register.html')  # 默认去模板文件夹中找模板文件。
    
    • 1
    • 2
    • 3

    疑问:怎么知道哪个文件时模板文件夹?
    答:创建Flask对象的时候,有一个template_folder参数设置成了template,声明好了模板文件夹,就回去这里指定的文件里面找模板文件了。

    2.重定向+url_for(路径反向解析)

    redirect(‘/’)实现重定向,两次响应。但是如果在项目中,会有很多路径或者路径比较长,不易记,所以可以使用url_for来指定endpoint(在路由的装饰器上添加endpoint),去访问真正的路径。

    url_for有两种使用方式:

    • 如果路由有配置endpoint的话,那么url_for的参数直接使用endpoint来反向解析路径
    • 如果没有配endpoint的话,就填写函数名,url_for会根据函数名去反向解析路径
      • 注意:如果路由是注册在蓝图上的话,函数名前需要 指定蓝图的名字,以蓝图名.函数名的形式作为url_for的参数
    @app.route('/', endpoint='index')
    def index():
        return render_template('index.html')
    
    @app.route('/register', methods=['GET', 'POST'])
    def register():
        print(request.method)
        if request.method == 'POST':
            username = request.form.get('username')
            password = request.form.get('password')
            repassword = request.form.get('repassword')
            # 用户密码一致性验证
            if password == repassword:
                # 保存用户
                user = {'username': username, 'password': password}
                users.append(user)
                return redirect(url_for('index'))  # 有两次响应:1。302状态码 + location  2。 返回location请求地址内容
            else:
                return '两次密码不一致'
    
        return render_template('register.html')
    
    @app.route('/show')
    def show():
        # users[] ----> str''   json字符串
        j_str = json.dumps(users)
        return j_str
    
    
    @app.route('/test')
    def test():
        url = url_for('index')  # 路径反向解析
        print(url)  # /
        return 'test'
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34

    3.模板的语法

    如果需要传值到模板页面中,渲染在页面上显示的话,可以在视图函数中返回的时候添加参数,例如:return render_template(‘show_1.html’, girls=girls, users=users)
    (1)在模板中获取view中传递的变量值:{{ 变量名}},类似于vue中使用mastache语法,也叫双大括号语法显示变量值。变量值支持字符串、列表、元组、set、对象等常用的数据类型

    render_template(‘模板名字’,key=value,key=value)

    @app.route('/show')
    def show():
        name = 'xzz'  # str
        age = 18  # int
        friends = ['迪丽热巴', 'cr', '任嘉伦']  # list
        dict1 = {'gift': 'YSL', 'gift1': '鲜花', 'gift2': '包包'}  # dict
        # 创建对象
        girlfriend = Girl('cr', 'mz')   # 自定义的类构建的类型:Girl对象
        return render_template('show.html', name=name, age=age, gender='男', friends=friends, dict1=dict1, girl=girlfriend)
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    模板文件显示内容:

    用户信息展示

    用户名是:{{ name }} -- {{ age }} ---{{ gender }}
    {{ friends.0 }}
    {{ dict1.get('gift') }} --- {{ dict1.gift1 }}
    {{ girl.gender }} -- {{ girl.name }} -- {{ girl.addr }}

    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    在这里插入图片描述

    模板文件获取值的一些简化写法:
    {{ list.0 }} 同 {{ list[0] }}
    {{ dict.key }} 同 {{ dict.get(key) }}
    {{ girl.name }} 同 {{ 对象.属性 }}

    (2)控制块:
    if语句

     {% if  条件 %}
    
     {% endif %}
    
     {% if  条件 %}
         条件为True
     {% else %}
         条件为False
     {% endif %}
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
      {% for girl in girls %} {# |相当于过滤器 #} {% if girl|length >= 3 %}
    • {{ girl }}
    • {% else %}
    • {{ girl }}
    • {% endif %} {% endfor %}
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    在这里插入图片描述

    for语句

     {% for 变量 in 可迭代的对象 %}
        for循环要做的任务
    
     {% endfor %}
    
    • 1
    • 2
    • 3
    • 4

    for循环还有一个loop变量,通过该变量可以获取到数据的下标(序号):
    loop.index 序号从1开始
    loop.index0 序号从0开始

    loop.revindex reverse 序号是倒着的
    loop.revindex0

    loop.first 布尔类型 是否是第一行
    loop.last 布尔类型 是否是第二行

    
        {% for user in users %}
            
        {% endfor %}
    
    {{ loop.index }} {{ user.username }} {{ user.password }} {{ user.addr }} {{ user.phone }}
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    在这里插入图片描述

    4.过滤器

    过滤器的本质就是函数,模板语法中过滤器:
    {{ 变量名 | 过滤器(*args) }}

    {{ 变量名 | 过滤器 }}

    (1)常见的过滤器:

    1.safe : 禁用转译
    msg = '

    520快乐!

    ' return render_template('show_2.html', girls=girls, users=users, msg=msg) 不想让其转译: {{ msg | safe }} 2.capitalize:单词的首字母大写 {{ n1 | capitalize }} 3.lower和upper 大小写的转换 4.title 一句话中每个单词的首字母大写 msg = 'She is a beautiful girl' {{ msg | title}} 5.reverse 翻转 {{ n1 | reverse}} 6.format {{ '%s is %d years old' | format('cr',18) }} 7.truncate 字符串截断
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23

    (2)list的操作:

    {# 列表过滤器的使用 #}
    {{ girls | first }}
    {{ girls | last }}
    {{ girls | length }}
    {#{{ girls | sum }} 整型的计算 #} {{ [1,3,5,7,9] | sum }}
    {{ [1,8,5,7,3] | sort }}
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    (3)dict

    {% for v in users.0.values() %}   ---->获取值
        

    {{ v }}

    {% endfor %}
    {% for k in users.0.keys() %} ----》获取键

    {{ k }}

    {% endfor %}
    {% for k,v in users.0.items() %} ---》获取键值

    {{ k }}---{{ v }}

    {% endfor %}
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14

    (4)自定义过滤器:

    • 通过flask模块中的add_template_filter方法
      a. 定义函数,带有参数和返回值
      b. 添加过滤器 app.add_template_filter(function,name=‘’)
      c. 在模板中使用: {{ 变量 | 自定义过滤器 }}
    • 使用装饰器完成
      a. 定义函数,带有参数和返回值
      b. 通过装饰器完成,@app.template_filter(‘过滤器名字’)装饰步骤一的函数
      c. 在模板中使用: {{ 变量 | 自定义过滤器 }}

    5.模板复用

    (1)模板继承
    需要模版继承的情况:

    • 多个模板具有完全相同的顶部和底部
    • 多个模板具有相同的模板内容,但是内容中有部分不一样
    • 多个模板具有完全相同的模板内容

    模板复用标签:
    {% block 名字 %}

    {% endblock %}


    模板复用的实现思路: 1.定义父模板 2.子模板继承父模板

    实现步骤:

    • 父模板:

      • 定义一个base.html的模板,也就是父模板
      • 分析模板中哪些是变化的,变化的部分用模板的复用标签block做个占位。比如:{% block title %}父模板的title{% endblock %}
      • 注意:样式和脚本 需要提前预留。否则在子页面中只有继承自父模板的样式以及脚本,加上子页面的样式和脚本无法生效。也就是说子页面只能在block标签里面添加style,script这些内容。
        {% block mycss %}{% endblock %}
        {% block myjs %}{% endblock %}
    • 子使用父模板:

      • {% extends ‘父模板的名称(html文件名)’ %}将父模板继承过来
      • 找到对应的block(坑)填充,每一个block都是有名字的。
    #如果需要引入外部文件,比如说引入样式文件,脚本文件等公共的文件。url_for默认只有一个参数,就是static,filename是关键字参数,必须要加上filename关键字才能成功访问。
     
    
    • 1
    • 2

    include: 包含,在A,B,C页面都共同的部分,但是其他页面没有这部分,这个时候考虑使用include。

    步骤:

    • 先定义一个公共的模板部分,xxx.html
    • 谁使用则include过来, {% include ‘文件夹/xxx.html’ %}

    6.宏:macro

    (1)把它看作是jinja2的一个函数,这个函数可以返回一个HTML字符串
    (2)目的:代码可以复用,避免代码冗余

    定义两种方式:

    • 在模板中直接定义:
      {% macro 函数声明 %} 函数体 {% endmacro %}
    {#  定义宏  #}
    {% macro form(action,value='登录',method='post') %}
        


    {% endmacro %} {# 调用宏 #} {{ form('/') }}
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 将所有宏提取到一个模板中(macro.html),哪里需要哪里导入,推荐使用这种方式定义宏,维护性比较好。
      {% import ‘macro.html’ as xxx %}
      {{ xxx.宏名字(参数) }}
    # 将需要复用的宏单独放到一个文件中,哪里需要就导入调用即可。
    {% macro form(action,value='登录',method='post') %}
        


    {% endmacro %} # 在其他文件中使用 {% import 'macro/macro.html' as func %} {{ func.form('/welcome',value='注册') }}
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14

    三、总结

    (1)变量: {{ 变量 }}
    (2)块:
    成对出现
    {% if 条件 %} …{% endif %}
    {% for 条件 %} …{% endfor %}
    {% block 条件 %} …{% endblock %}
    {% macro 函数声明 %} 函数体 {% endmacro %}

    单个出现
    {% include ‘’ %} 包含
    {% import ‘’ %} 导入宏
    {% extends ‘’ %}

    函数/宏使用
    {{ url_for(‘static’,filename=‘’) }}
    {{ hongname(xxx) }}

  • 相关阅读:
    唯众数据标注实训系统
    jmeter中csv参数化
    一个简单的删除,我发现这么多知识...
    Android studio 断点调试、日志断点
    艾美捷ProSci丨ProSci TM4SF1 抗体解决方案
    rabbitMQ:绑定Exchange发送和接收消息(topic)
    PyTorch使用Tricks:学习率衰减 !!
    2022年6月,海尔集团提前批算法岗面试题5道|含解析
    MeterSphere 监控方案
    【总结】深度学习阶段性总结
  • 原文地址:https://blog.csdn.net/victory_CEO/article/details/127633947