Flask Web视频学习(1)

Flask框架介绍

  • 作者: Armin ronacher
  • 诞生时间: 2010年
  • 组成: werkzueg + jinja2
    • werkzueg: 一个包含WSGI、路由、调试的工具集;专门用来处理请求相关的内容的,比如: 地址。
    • jinja2: 默认的模板引擎,用来做页面渲染处理的。
    • 额外的扩展包: 可以处理数据库的链接, 站点管理,flask-cache做缓存处理。

Hello Flask

在这里插入图片描述

路由和视图

与路由有关的知识点

app.url_map:查看哪些路由(地址)可以访问
  • 格式: 使用app.url_map, 返回的是app装饰的所有的路由和路径之间的映射关系
  • 注意点: 只有被app.url_map包含进来的路由(地址)才能被访问
from flask import Flask

app = Flask(__name__)

@app.route('/')
def index():
    return "This is index."

# 判断是否直接使用当前模块运行程序
if __name__ == '__main__':
	print(app.url_map)
    app.run()

在这里插入图片描述

app.run()参数讲解
  • 参数1: host, 如果我们不指定,默认值是127.0.0.1
  • 参数2: port, 如果我们不指定,默认的值是5000
  • 参数3: debug,调试模式,如果不指定,默认的值是False
    • 如果设置为True,两个好处
    • 1.如果在运行的过程中,直接改动代码了,不需要重新启动程序,只需要ctrl+s保存即可部署程序
    • 2.如果程序报错了,会有友情提示

通常会将debug设置为true,如:

app.run(debug=True)
动态URL:在访问路由的时候指定参数
  • 格式: @app.route("/<类型:变量名>")
  • 常见的参数类型
    • 整数 int
    • 小数 float
    • 字符串 path(未指定类型时,默认为path)
自定义参数类型(自定义转换器)
  • 背景:
    • 如果系统提供的int、float等参数类型满足不了需求的时候,我们需要自定义。之所以int、float、path可以接收不同的数据类型,是因为系统已经提供好对应的转换器了。
  • 自定义转换器格式
    • 1.定义一个继承自BaseConverter的类
    • 2.重写init()(类似于构造函数)
    • 3.初始化父类成员变量和子类自己的规则
    • 4.将转换器类添加到系统默认的转换器列表中
from flask import Flask
from werkzeug.routing import BaseConverter

app = Flask(__name__)


# 1 定义一个继承自BaseConverter的类
class MyRegexConverter(BaseConverter):
    # 事实上,只写regex正则表达式也可以算作自定义,但这样不够灵活。
    # regex = "\d{3}"

    # 2 重写init()(类似与构造函数)
    def __init__(self, map, regex):
        # 3 初始化父类成员变量和子类自己的规则
        super(MyRegexConverter, self).__init__(map)
        self.regex = regex  # self.regex是实例类的regex,通过参数regex确定下来


# 4 将转换器类添加到系统默认的转换器列表中
app.url_map.converters['re'] = MyRegexConverter

# 可以打印输出所有的转换器,包括系统定义的和自己定义的
print(app.url_map.converters)

# 接收3位参数
@app.route('/<re("\d{3}"):number>')  # 使用了自定义参数类型re,并传入规则
def test(number):
    return "三位数:%s" % number


# 接收4位参数
@app.route('/<re("\d{4}"):number>')  # 使用了自定义参数类型re,并传入规则
def test1(number):
    return "四位数:%s" % number


# 接收一个手机号
@app.route('/<re("1[3-9]\d{9}"):tel>')  # 使用了自定义参数类型re,并传入规则
def get_phone_number(tel):
    return "手机号:%s" % tel


if __name__ == '__main__':
    app.run(debug=True)
  • ctrl+r :批量替换
  • 引号问题:@app.route('/<re("\d{4}"):number>')该语句最外层为单引号,内层为双引号,不能单引号内还是单引号。只能单引号内为双引号,或双引号内为单引号。
给路由增加其他的访问方式
  • 格式: @app.route(‘路径’,methods=[‘请求方式1’,‘请求方式2’,…])
  • 常见的请求方式:
    • GET, POST, PUT, DELETE
  • 注意点: 如果不指定请求方式,那么默认支持的是GET
from flask import Flask

app = Flask(__name__)


# 接收一个手机号
@app.route('/', methods=["post"])  # 使用了自定义参数类型re,并传入规则
def test():
    return "test"


if __name__ == '__main__':
    app.run(debug=True)

Postman应用软件用来模拟post请求。

与响应有关的知识点

所谓“响应”即视图函数的返回值,返回什么就是对请求者作出了什么回应。

返回字符串
  • 1.直接返回响应体数据
    • return ‘字符串’
  • 2.直接返回响应体数据+状态码
    • return ‘字符串’, 状态码
  • 3.直接返回响应体数据+状态码+响应头信息
    • return ‘字符串’, 状态码, {‘key’:‘value’}
通过jsonify返回json数据
  • 格式: jsonify(dict)
  • 简化格式: jsonify(key=value,key2=value2)
@app.route('/')
def test():
    # 1 定义一個字典
    dict = {
        "name":"Jenny",
        "age":18
    }
    # 2 返回json数据
    # return jsonify(dict)

    # 3 返回json数据-简化格式
    return jsonify(name="Lisa", age=18)
返回一个重定向
redirect
  • 格式: redirect(‘地址’), 地址既可以是本地服务器的地址,也可以是其他服务器的地址
  • 注意点: 重定向的代号是302
  • 特点: 重定向是两次请求
@app.route('/')
def taobao():
    return redirect("http://taobao.com")
url_for
  • 解释: 称为反向解析——根据路由函数名转向该视图函数(如果该视图函数的路由有变量,则需要传入),即返回的是视图函数对应的路由地址
  • 格式: url_for(‘视图函数’,key=value)

key是视图函数中的参数名,value是传入的实参。

  • 注意点: url_for经常配合redirect使用,传递参数
@app.route('/jingdong')
def jingdong():
    # 下面语句打印出来的结果就是/taobao/0
    # print(url_for("taobao", token = 0))
    # 因此,可以将其作为redirect的参数
    response = redirect(url_for("taobao", token=0))
    return response

@app.route('/yamaxun')
def yamaxun():
    response = redirect(url_for("taobao", token=1))
    return response

@app.route('/taobao/<int:token>')
def taobao(token):
    if token == 0:
        return "欢迎京东用户,您将享受7折优惠"
    elif token == 1:
        return "欢迎亚马逊用户,您将享受9折优惠"
    else:
        return "欢迎"
abort & errorhandler
  • 使用场景: 当访问服务器资源的时候,如果找不到该资源,可以报出异常信息,可以自定义此异常信息的内容;使用errorhandler捕捉。
  • 格式: abort(错误代号)
  • 格式: @app.errorhandler(错误代号)
@app.route('/')
def test():
    # abort(404) # 直接使用abort()使直接产生404错误
    return "hello"

# 只要请求了未设置的路由地址,就会产生此效果
@app.errorhandler(404)
def page_not_found(e):
    print(e)
    return "页面找不到o(╥﹏╥)o"

其他

request对象
  • 介绍:request就是flask中代表当前请求的request对象,其中一个请求上下文变量(理解成全局变量,在视图函数中直接使用时可以取到当前本次请求)。

  • 参数:

    • request.data: 获取的是非表单(ajax)以post提交的数据
    • request.form: 获取的是表单以post方式提交的数据
    • request.args: 获取的是问号后面的查询参数。如www.taobao.com?name=Lisa&age=18
    • request.method: 获取的请求方式
    • request.url: 获取请求的地址
    • request.files: 获取的是input标签中type类型为file的文件
@app.route('/')
def test():
    print(request.method)
    print(request.url)
    print(request.args)
    
    # 使用字典的取值方法取到对应的值。但这种方式太死板,倘若并未出现?及后面应该有的内容,那么就会报错:Bad Request
    # print(request.args['name'])
    
    # 推荐使用以下方法。即使未出现?及后面应该有的内容,也不会报错,将返回None
    print(request.args.get("name"))
    # 如果获取不到age,则赋默认值0
    print(request.args.get("age", 0))
    
    return "hello"

输入http://127.0.0.1:5000/后控制台的打印结果为:
在这里插入图片描述
且页面可以正常显示:
在这里插入图片描述
如果print(request.args['name']),则结果为:
在这里插入图片描述
输入http://127.0.0.1:5000/?name=Lisa&age=18
在这里插入图片描述
在这里插入图片描述

加载app程序运行参数

app程序运行所有的参数信息存储在app.config中。打印结果:<Config {‘ENV’: ‘production’, ‘DEBUG’: True, ‘TESTING’: False, ‘PROPAGATE_EXCEPTIONS’: None, ‘PRESERVE_CONTEXT_ON_EXCEPTION’: None, ‘SECRET_KEY’: None, ‘PERMANENT_SESSION_LIFETIME’: datetime.timedelta(days=31), ‘USE_X_SENDFILE’: False, ‘SERVER_NAME’: None, ‘APPLICATION_ROOT’: ‘/’, ‘SESSION_COOKIE_NAME’: ‘session’, ‘SESSION_COOKIE_DOMAIN’: None, ‘SESSION_COOKIE_PATH’: None, ‘SESSION_COOKIE_HTTPONLY’: True, ‘SESSION_COOKIE_SECURE’: False, ‘SESSION_COOKIE_SAMESITE’: None, ‘SESSION_REFRESH_EACH_REQUEST’: True, ‘MAX_CONTENT_LENGTH’: None, ‘SEND_FILE_MAX_AGE_DEFAULT’: datetime.timedelta(seconds=43200), ‘TRAP_BAD_REQUEST_ERRORS’: None, ‘TRAP_HTTP_EXCEPTIONS’: False, ‘EXPLAIN_TEMPLATE_LOADING’: False, ‘PREFERRED_URL_SCHEME’: ‘http’, ‘JSON_AS_ASCII’: True, ‘JSON_SORT_KEYS’: True, ‘JSONIFY_PRETTYPRINT_REGULAR’: False, ‘JSONIFY_MIMETYPE’: ‘application/json’, ‘TEMPLATES_AUTO_RELOAD’: None, ‘MAX_COOKIE_SIZE’: 4093}>

  • 1.从配置类(对象)中加载
    • app.config.from_object(obj)
  • 2.从配置文件中加载
    • app.config.from_pyfile(file)
  • 3.从环境变量中加载
    • app.config.from_envvar(环境变量)
# 1 从配置类(对象)中加载
class MyConfig(object):
    # 配置调试模式
    DEBUG = True
    
app.config.from_object(MyConfig)
# 先创建Config.ini文件,文件内容此处只写了 DEBUG = True

# 2 从配置文件中加载
app.config.from_pyfile("Config.ini")

在这里插入图片描述

请求钩子
  • 解释: 访问正常视图函数时顺带执行的方法。在客户端和服务器交互的过程中,有些准备工作需要处理,比如在请求开始时,建立数据库连接、根据需求进行权限校验,在请求结束时,指定数据的交互格式。为了让每个视图函数避免编写重复功能的代码,flask体用了通用设施的功能,即请求钩子。

  • 常见的请求钩子有四种:

    • 1.before_first_request: 在处理第一个请求前执行

    • [记住] 2.before_request: 在每次请求前执行,在该装饰函数中,一旦return,视图函数不再执行

    • [记住] 3.after_request: 如果没有抛出错误,在每次请求后执行。接受一个参数:视图函数作出的响应。 在此函数中可以对响应值,在返回之前做最后一步处理后再返回。

    • 4.teardown_request: 在每次请求后执行。接受一个参数: 用来接收错误信息。

# before_first_request装饰的函数在第一次访问的时候执行,里面适合做初始化操作,比如,io文件读写
@app.before_first_request
def before_first_request():
    print("before_first_request")  # step1

# before_request在每次请求前都执行,适合对请求参数做校验、访问统计
@app.before_request
def before_request():
    print("before_request")  # step2

# after_request在视图函数执行之后执行,适合对视图函数的返回值做统一处理,比如: 返回统一的json数据格式
@app.after_request
def after_request(resp): # resp就是相应类Response的实例对象,其包含了很多内容,这些内容就是与视图函数有关的
    print("after_request")  # step4
    # 对视图函数的返回值做统一处理,比如: 返回统一的json数据格式
    resp.headers["Content-Type"] = "application/json"
    return resp

# teardown_request在请求销毁之后执行,适合做异常信息统计
@app.teardown_request
def teardown_request(e):
    print(e)
    print("teardown_request")   # step5

@app.route('/')
def test():
    # print(app.config)
    return "hello"  # step3

利用断点调试法,可以看到以上几个print语句的执行流程,已标注在上面(step1,2,3,4,5)。

视图内容和模板

状态保持

实现状态保持(保持服务器和浏览器交互的状态)的主要方式:

  • 在客户端存储信息——Cookie
  • 在服务器端存储信息——Session

Cookie

  • 解释: 由服务器设置,存储在浏览器。
  • 作用: 用来做广告推送
  • cookie的设置和获取
    • 设置cookie: response.set_cookie(key,value,max_age)
      • max_age: 表示cookie在浏览器的存储时间,单位是秒
    • 获取cookie: request.cookies.get(“key”)
from flask import Flask, make_response, request

app = Flask(__name__)

@app.route('/set_cookie')
def set_cookie():
    # 调用make_response(),获取响应体对象
    response = make_response("set cookie")
    # 设置cookie
    response.set_cookie("computer", "mac")
    response.set_cookie("price", "4000", 10) # cookie存在的时间设置为10秒
    return response

# 获取cookie
@app.route('/get_cookie')
def get_cookie():
    name = request.cookies.get("computer")
    price = request.cookies.get("price")
    return "name is %s, price is %s" % (name, price)


if __name__ == "__main__":
    app.run(debug=True)

在这里插入图片描述

Session

  • 解释: 里面存储的是敏感信息(比如身份证、登陆信息),由服务器设置,并存储在服务器。(存储在浏览器就危险了,都能看到了)
  • 作用: 用来做用户的登陆状态保持
  • session的设置和获取
    • 设置session: sessioin[key] = value
    • 获取session: value = session.get(key)
  • 注意点:
    • 1.session的存储依赖于cookie
    • 2.存储在cookie中的sessionID需要加密,需要设置秘钥(SECRET_KEY)(只要需要把变量存储到session中,就需要设置秘钥)
from flask import Flask, make_response, request, session

app = Flask(__name__)

# 设置SECRET_KEY
app.config["SECRET_KEY"] = "argbshnjyrikymjfnhtbrge"


# 设置session
@app.route('/set_session/<name>')
def set_session(name):
    session["name"] = name
    return "set session"


# 获取session
@app.route('/get_session')
def get_session():
    value = session.get("name")
    return "name is %s" % value


if __name__ == "__main__":
    app.run(debug=True)

上下文

  • 解释: 就是一个容器,保存了flask程序运行过程中的一些信息。
请求上下文(request context):

在flask中,可以直接在视图函数中使用request这个对象获取相关数据,而request就是请求上下文的对象,保存了当前本次请求的相关数据,如地址、cookie等。请求上下文对象有两种:

  • request: 封装的是HTTP请求相关的数据。如,user = request.args.get(‘user’)。
  • session: 封装的是和用户相关的敏感信息。如,session[‘name’] = user.id或session.get(‘name’)。
应用上下文(application context)

应用上下文有以下两种:

  • current_app: 是app的一个代理对象,可以通过它获取app身上设置的各种属性,主要用在模块化开发中
  • g: 一个局部的全局变量,主要用在装饰器中,可以通过它传递一些数据。g保存的是当前请求的全局变量。

在这里插入图片描述

假设你来到一家自助餐餐厅,餐厅老板就是app,餐厅服务员就是curren_app,老板雇佣服务员,让其作为自己的代理对象,给客人服务。你需要拿起盘子选菜,“你”作为一个客户端,这个盘子就是g对象。当你吃完后离开餐厅,盘子就需要进行回收,服务员也要清除自己的当前服务状态,开始为其他人服务。

flask_script扩展

flask_XX或Flask-XX都是flaks的扩展。

  • 解释: 属于flaks的扩展

  • 作用: 用来动态运行程序,配合flask_migrate做数据库迁移

  • 使用步骤:

    • 1.安装

      • pip install flask_script
    • 2.导入Manager类

    • 3.创建manager对象,管理app

    • 4.使用manager启动程序

      • 启动命令: python xxx.py runserver -h -p -d
        • -h:即host,表示IP地址
        • -p:端口号
        • -d:调试模式
from flask import Flask, make_response, request, session
from flask_script import Manager

app = Flask(__name__)

# 1 创建对象manager,管理app
manager = Manager(app)

@app.route('/')
def test():
    return "hello"


if __name__ == "__main__":
    # 2 使用manager启动程序
    manager.run()

在这里插入图片描述
右键Run无法启动,但并非只能用上面的命令行方式启动。只需做以下修改:
在这里插入图片描述

jinja2模板概念&render_template函数

  • 解释: jinja2模板用来展示数据的HTML页面,这个过程通常称为渲染。render_template函数属于jinja2的模板函数。
  • 好处:
    • 1.以后的视图函数,只负责业务逻辑的处理,比如: 数据库的增删改查
    • 2.以后数据的展示,全部都由jinja2的模板负责
  • 使用格式:
    • response = render_template(‘模板文件’)
@app.route('/')
def test():
    # t1.html存放在templates文件夹下,必须命名为templates,因为Flask类已经这样设置好了
    response = render_template('t1.html')
    return response

一个设置:
右键templates文件夹,选择
在这里插入图片描述
在这里插入图片描述

模板语法

  • 解释: 在模板中获取视图函数的变量

  • 模板语法的种类

    • 获取变量:

      {{ 变量 }}
      
    • 分支格式:

      {% if 条件 %}
      	语句1
      {% else %}
      	语句2
      {% endif %}
      
    • 循环语句格式:

      {% for 变量 in 容器 %}
      
      {% endfor %}
      
    • 注释:

      {# 这里是注释的内容 #}
      

示例

@app.route('/')
def test():
    # 1 定义各种类型的变量
    number = 10
    str = "Lisa"
    tuple = (1, 2, 3, 4, 5)
    list = [6, 7, 8, 9, 10]
    dict = {
        "name": "Lisa",
        "age": 18
    }
    # 2 携带变量到模板中展示,=前表示模板中的变量名,=后表示此处(上面设置的)的变量名
    return render_template("t1.html", number=number, str=str, tuple=tuple, list=list, dict=dict)
<body>
    <h1>获取各种变量的值</h1>
    <h2>整数:{{number}}</h2>
    <h2>字符串:{{str}}</h2>
    <h2>元组:{{tuple}}</h2>
    <h2>元组-获取具体值:方法①tuple[0]={{tuple[0]}} 方法②tuple.1={{ tuple.1 }}</h2>
    <h2>列表:{{list}}</h2>
    <h2>列表-获取具体值:方法①list[0]={{list[0]}} 方法②list.1={{ list.1 }}</h2>
    <h2>字典:{{dict}}</h2>
    {# dict["age"]表示字典里age属性,而dict[age]中的age代表的是定义的一个age的变量 #}
    <h2>字典-获取具体值:方法①dict.name={{dict.name}} 方法②dict["age"]={{ dict["age"] }}</h2>

    <h1>1.遍历元祖中的偶数</h1>
    {% for item in tuple %}
        {% if item %2 == 0 %}
            <h3>{{ item }}</h3>
        {% endif %}
    {% endfor %}


    <h1>2.遍历字典</h1>
    {% for key in dict %}
        {# dict.key那么这个key会当成字典中的一个键,  dict[key],那么这个key当成一个变量 #}
        <h3>{{ key }} = {{ dict[key] }}</h3>
    {% endfor %}
</body>

在这里插入图片描述
在这里插入图片描述

模板自带过滤器

系统字符串过滤器[理解]
  • 解释: 过滤器,用来过滤想要的数据
  • 格式: {{ 字符串 | 字符串过滤器 }}
  • 常见的字符串过滤器有:
    • title: 将每个单词的首字母都大写
    • lower: 将每个单词都小写
    • upper: 将每个单词都大写
    • reverse: 反转

示例

<body>

    {#    使用格式:{{ 字符串 | 字符串过滤器 }}#}
    1.safe:禁用转义,让标签生效,否则就直接打印的是字符串<em>hello</em><p>{{ '<em>hello</em>' | safe }}</p>

    2.capitalize:把字符串变量的首字母转成大写,其余字母转小写
    <p>{{ 'hello PYTHON' | capitalize }}</p>

    3.lower:把字符串的所有字符都转成小写(中文没有大小写)
    <p>{{ 'HELLO PYthON 你好' | lower }}</p>

    4.upper:把字符串的所有字符都转成大写
    <p>{{ 'hello python 你好' | upper }}</p>

    5.title:把字符串中的每个单词的首字母都转成大写
    <p>{{ 'hello world python java' | title }}</p>

    6.reverse:字符串反转
    <p>{{ 'olleh' | reverse }}</p>
    <p>{{ '我爱你' | reverse }}</p>

    7.format:格式化输出
    <p>{{ '%s is %d' | format('age',17) }}</p>

    8.striptags:渲染之前就把值中所有的HTML标签都删掉,之后才进行渲染处理
    <p>{{ '<em>hello</em>' | striptags }}</p>

</body>

在这里插入图片描述

系统列表过滤器[理解]
  • 解释: 过滤器,用来过滤想要的数据
  • 格式: {{ 列表 | 列表过滤器 }}
  • 常见的列表过滤器有:
    • first: 获取列表第一个元素
    • last: 最后一个元素
    • sum: 列表和
    • length: 列表长度
    • sort: 列表排序,默认为升序
自定义过滤器[掌握]
  • 解释: 当系统提供的过滤器满足不了需求的时候,需要自定义

  • 自定义过滤器有两种格式:

    • 1.先定义好函数,再将函数添加到系统默认的过滤器列表中

      • def 函数名: pass
      • app.add_template_filter(函数名,‘过滤器名’)
    • 2.定义函数的时候,直接使用系统过滤器进行装饰

      @app.template_filter('过滤器名')
      def 函数名():
          pass
      

示例

from flask import Flask, make_response, request, session, render_template

app = Flask(__name__)


# -----------示例①:求列表中所有偶数的和---------
# 1.定义好函数
def get_even_num(list):
    sum = 0
    for i in list:
        if i % 2 == 0:
            sum += i
    return sum
# 2. 将函数添加到系统默认的过滤器列表中【app.add_template_filter(函数名,‘过滤器名’)】
app.add_template_filter(get_even_num, "even_num")


# ------------示例②:将列表中的元素反转----------
# 定义函数的时候,直接使用系统过滤器进行装饰
@app.template_filter("reverse")
def list_reverse(list):
    list.reverse()
    return list


@app.route('/')
def test():
    return render_template("t1.html")


if __name__ == "__main__":
    app.run(debug=True)
<body>
    <h2>原列表:{{ [1, 2, 7, 4, 5] }}</h2>

    <h2>原列表中偶数之和:{{ [1, 2, 7, 4, 5] | even_num }}</h2>
    {# even_num是我们自定义的过滤器的名称,这样做实际是将[1, 2, 3, 4, 5]当做实参传递给even_num对应的函数#}

    <h2>反转列表:{{ [1, 2, 7, 4, 5] | reverse }}</h2>

    <h2>降序列表:{{ [1, 2, 7, 4, 5] | sort | reverse}}</h2>
</body>

在这里插入图片描述

模板代码复用

  • 解释: 相当于python中的函数,定义好一段功能,在需要的时候进行调用即可

  • 定义格式:

    {% macro 宏名(参数) %}
    
    {% endmacro %}
    
  • 使用格式:

    // 使用当前文件定义好的宏
    {{ 宏名(参数) }}
    
    //使用其他文件定义好的宏
    {% import '文件' as 别名%}
    {{ 别名.宏名(参数) }}
    
继承 [掌握]
  • 解释: 一个子模板继承自父模板

  • 作用: 共性抽取,代码复用

  • 父模板

    • 1.所有子类都具有的相同的内容的, 在父模板中直接写死
    • 2.每个子类的模板中不一样的内容,使用block模板定义好
  • 子模板

    • 1.根据子类自己的需求,去重写父类中的block对应的内容
    • 2.如果重写之后,还想保留父类的内容,那么使用{{super()}}
    • 3.继承格式: {% extends ‘父文件名’%}, 写在页面的顶部
  • 注意点:

    • 定义block的格式

      {% block 名称 %}
      
      {% endblock %}
      

示例
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

包含
  • 解释: 在一个文件中完全拥有另外一个文件,不够灵活,没法扩展

  • 格式:

方式一:
{% include '文件' %}
  
方式二:
{% include '文件' ignore missing %} 
{# ignore missing的作用:如果包含的文件不存在,也不会报错#}

模板使用练习

loop.index0()
loop.index()

from flask import Flask, make_response, request, session, render_template

app = Flask(__name__)


@app.route('/')
def hello_world():
    # 1.定义5条数据
    my_list = [
        {
            "id": 1,
            "value": "我爱工作"
        },
        {
            "id": 2,
            "value": "工作使人快乐"
        },
        {
            "id": 3,
            "value": "沉迷于工作无法自拔"
        },
        {
            "id": 4,
            "value": "日渐消瘦"
        },
        {
            "id": 5,
            "value": "以梦为马,越骑越傻"
        }
    ]

    # 2.在模板中显示4条
    return render_template("t1.html", list=my_list)


if __name__ == "__main__":
    app.run(debug=True)
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
    <ul>
        {# 如果dict.id 不等于5才遍历 #}
        {% for dict in list if dict.id !=5 %}

            {# 方式一 #}
{#            {% if dict.id == 1 %}#}
{#                <li style="background: yellow;"> {{ dict.value }} </li>#}
{#            {% elif dict.id == 2 %}#}
{#                <li style="background: green;"> {{ dict.value }} </li>#}
{#            {% elif dict.id == 3 %}#}
{#                <li style="background: red;"> {{ dict.value }} </li>#}
{#            {% else %}#}
{#                <li style="background: purple;"> {{ dict.value }} </li>#}
{#            {% endif %}#}


            {# 遍历的时候可以获取到从0开始的索引 #}
{#            <h3>{{ loop.index0  }}</h3>#}

            {# 遍历的时候可以获取到从1开始的索引 #}
{#            <h3>{{ loop.index  }}</h3>#}


            {# 方式二 #}
            {% if loop.index == 1 %}
                <li style="background: yellow;"> {{ dict.value }} </li>
            {% elif loop.index == 2 %}
                <li style="background: green;"> {{ dict.value }} </li>
            {% elif loop.index == 3 %}
                <li style="background: red;"> {{ dict.value }} </li>
            {% else %}
                <li style="background: purple;"> {{ dict.value }} </li>
            {% endif %}
    </ul>
</body>
</html>

在这里插入图片描述

模板特有变量

  • 解释: 模板特有变量指的是不需要通过python程序传递就可以直接使用的变量
  • 常见的特有变量如下:
    • config: 就是flask中的app.config, 表示应用程序中的所有配置信息
    • request: 表示请求上下文对象,封装的是请求相关的数据
    • g: 局部的全局变量(了解)
    • url_for(): 反解析,通过函数的名字,解析到视图函数的路径
    • get_flashed_messsages(): 用来消耗flash方法中存储的消息。
      • 场景: 登陆出错,可以显示
      • 注意点:
      • 1.使用flash存储消息的时候需要设置SECRET_KEY
      • 2.因为flash内部的消息存储,依赖于了session(查看flash方法的源代码可以看到相关内容)

csrf攻击

简介

在这里插入图片描述

攻击流程
  • 示意图展示
    在这里插入图片描述

  • 代码演示

(略)

解决攻击
  • 示意图
    在这里插入图片描述
  • 在cookie增加一个csrf_token
  • 在表单中增加一个csrf_token
  • 校验: 取出cookie和表单中的csrf_token比较如果二者一致那么是正常请求
  • CSRFProtect解决攻击

    • 使用流程:
      • 1.安装扩展包:pip install flask-wtf
      • 2.导入包:from flask_wtf.csrf import CSRFProtect
      • 3.创建CSRFProtect对象,保护app对象
      • 4.设置SECRET_KEY便于csrf_token加密
      • 5.在表单中设置csrf_token隐藏字段即可
      • 注意点: .CSRFProtect一旦保护了app之后, 会对’POST’, ‘PUT’, ‘PATCH’, 'DELETE’做校验.

例子:用户注册案例

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
    <form action="" method="post">
        {# 只要使用了CSEFProtect类保护app,就可以直接使用csrf_token()获取csrf_token值 #}
        <input type="hidden" name="csrf_token" value="{{ csrf_token() }}">
        
        用户名:<input type="text" name="username"><br>
        密码:<input type="text" name="password"><br>
        确认密码:<input type="text" name="repassword"><br>
        <input type="submit" value="注册">
    </form>
</body>
</html>
from flask import Flask, make_response, request, session, render_template
from flask_wtf import CSRFProtect

app = Flask(__name__)

# 设置秘钥
app.config["SECRET_KEY"] = "fesfva"

# 保护app
CSRFProtect(app)


@app.route('/', methods=["GET", "POST"])
def hello_world():
    # 1 如果是GET请求,则直接返回注册页面
    if request.method == "GET":
        return render_template("t1.html")

    # 2 如果是POST请求,说明点击了“注册”,故处理注册业务
    else:
        # 2.1 获取参数
        username = request.form.get("username")
        password = request.form.get("password")
        repassword = request.form.get("repassword")

        # 2.2 校验参数,判断是否为空
        if not all([username, password, repassword]):
            return "参数填写不全!"

        # 2.3 两次密码是否一致
        if password != repassword:
            return "两次密码输入不一致!"

        # 2.4 返回注册成功
        return "注册成功!"

if __name__ == "__main__":
    app.run(debug=True)
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值