Flask--01

1. HTTP通信与Web框架

1.1 流程

客户端将请求打包成HTTP的请求报文(HTTP协议格式的请求数据)

采用TCP传输发送给服务器端

服务器接收到请求报文后按照HTTP协议进行解析

服务器根据解析后获知的客户端请求进行逻辑执行

服务器将执行后的结果封装成HTTP的响应报文(HTTP协议格式的响应数据)

采用刚才的TCP连接将响应报文发送给客户端

客户端按照HTTP协议解析响应报文获取结果数据

1.2 细节

客户端不一定是浏览器,也可以是PC软件、手机APP、程序

根据服务器端的工作,将其分为两部分:

服务器:与客户端进行tcp通信,接收、解析、打包、发送http格式数据

业务程序:根据解析后的请求数据执行逻辑处理,形成要返回的数据交给服务器

服务器与Python业务程序的配合使用WSGI协议

1.3 Web框架

能够被服务器调用起来,根据客户端的不同请求执行不同的逻辑处理形成要返回的数据的 程序

核心:实现路由和视图(业务逻辑处理)

1.4 框架的轻重

重量级的框架:为方便业务程序的开发,提供了丰富的工具、组件,如Django

 

轻量级的框架:只提供Web框架的核心功能,自由、灵活、高度定制,如Flask、Tornado

1.5 明确Web开发的任务

视图开发:根据客户端请求实现业务逻辑(视图)编写

模板、数据库等其他的都是为了帮助视图开发,不是必备的

2. 认识Flask

2.1 简介

Flask诞生于2010年,是Armin ronacher(人名)用Python语言基于Werkzeug工具箱编写的轻量级Web开发框架。它主要面向需求简单的小应用。

Flask本身相当于一个内核,其他几乎所有的功能都要用到扩展(邮件扩展Flask-Mail,用户认证Flask-Login),都需要用第三方的扩展来实现。比如可以用Flask-extension加入ORM、窗体验证工具,文件上传、身份验证等。Flask没有默认使用的数据库,你可以选择MySQL,也可以用NoSQL。其 WSGI 工具箱采用 Werkzeug(路由模块) ,模板引擎则使用 Jinja2 。

可以说Flask框架的核心就是Werkzeug和Jinja2。

Python最出名的框架要数Django,此外还有Flask、Tornado等框架。虽然Flask不是最出名的框架,但是Flask应该算是最灵活的框架之一,这也是Flask受到广大开发者喜爱的原因。

2.2 与Django对比

django提供了:

django-admin快速创建项目工程目录

manage.py 管理项目工程

orm模型(数据库抽象层)

admin后台管理站点

缓存机制

文件存储系统

用户认证系统

而这些,flask都没有,都需要扩展包来提供

2.3 Flask扩展包:

Flask-SQLalchemy:操作数据库;

Flask-migrate:管理迁移数据库;

Flask-Mail:邮件;

Flask-WTF:表单;

Flask-script:插入脚本;

Flask-Login:认证用户状态;

Flask-RESTful:开发REST API的工具;

Flask-Bootstrap:集成前端Twitter Bootstrap框架;

Flask-Moment:本地化日期和时间;

3. 创建虚拟环境 见(Linux下python相关设置笔记)

https://blog.csdn.net/weixin_42569562/article/details/84203979

虚拟环境是一个互相隔离的目录

  1. mkvirtualenv flask_py2

  2. pip install flask==0.10.1

pip freeze > requirements.txt 把已安装的工具包搜集到txt文件

pip install –r requirements.txt 安装文件中包含的包

4. Flask的Hello world程序

# coding:utf-8
​
# 导入Flask类
from flask import Flask
​
#Flask类接收一个参数__name__
app = Flask(__name__)
​
# 装饰器的作用是将路由映射到视图函数index
@app.route('/')
def index():
    return 'Hello World'
​
# Flask应用程序实例的run方法启动WEB服务器
if __name__ == '__main__':
    app.run()
​

 

# coding:utf-8
​
from flask import Flask, current_app
# import demo
​
# 创建flask的应用对象
# __name__表示当前的模块名字
#           模块名,flask以这个模块所在的目录为总目录,默认这个目录中的static为静态目录,templates为模板目录
app = Flask(__name__,
            static_url_path="/python",  # 访问静态资源的url前缀, 默认值是static
            static_folder="static",  # 静态文件的目录,默认就是static
            template_folder="templates",  # 模板文件的目录,默认是templates
            )
# app = Flask("__main__")
# app = Flask("abcdefg")
​
# 配置参数的使用方式
# 1. 使用配置文件
# app.config.from_pyfile("config.cfg")
​
# 2. 使用对象配置参数
class Config(object):
    DEBUG = True
    ITCAST = "python"
​
​
app.config.from_object(Config)
​
# # 3. 直接操作config的字典对象
# app.config["DEBUG"] = True
​
​
@app.route("/")
def index():
    """定义的视图函数"""
    # a = 1 / 0
    # 读取配置参数
    # 1. 直接从全局对象app的config字典中取值
    # print(app.config.get("ITCAST"))
    # 2. 通过current_app获取参数
    print(current_app.config.get("ITCAST"))
​
    return "hello flask"
​
​
if __name__ == '__main__':
    # 启动flask程序
    # app.run()
    app.run(host="0.0.0.0", port=5000, debug=True)

4.1 Flask创建app对象

4.1.1 初始化参数

import_name: 
​
static_url_path:
​
static_folder: 默认‘static’
​
template_folder: 默认‘templates’

4.1.2 配置参数

app.config.from_pyfile(“yourconfig.cfg”) 或
​
app.config.from_object()

4.1.3 在视图读取配置参数

app.config.get()  或者 current_app.config.get()

4.1.4 app.run的参数`

app.run(host=”0.0.0.0”, port=5000)

4.2 路由

4.2.1 app.url_map 查看所有路由

4.2.2 同一路由装饰多个视图函数

4.2.3 同一视图多个路由装饰器

4.2.4 利用methods限制访问方式

@app.route('/sample', methods=['GET', 'POST'])

4.2.5 使用url_for进行反解析

# coding:utf-8
​
from flask import Flask, current_app, redirect, url_for
# import demo
​
# 创建flask的应用对象
# __name__表示当前的模块名字
#           模块名,flask以这个模块所在的目录为总目录,默认这个目录中的static为静态目录,templates为模板目录
app = Flask(__name__)
​
​
@app.route("/index")
def index():
    """定义的视图函数"""
    return "hello flask"
​
​
# 通过methods限定访问方式
@app.route("/post_only", methods=["GET", "POST"])
def post_only():
    return "post only page"
​
​
@app.route("/hello", methods=["POST"])
def hello():
    return "hello 1"
​
​
@app.route("/hello", methods=["GET"])
def hello2():
    return "hello 2"
​
​
@app.route("/hi1")
@app.route("/hi2")
def hi():
    return "hi page"
​
​
@app.route("/login")
def login():
    # url = "/"
    # 使用url_for的函数,通过视图函数的名字找到视图对应的url路径
    url = url_for("index")
​
    return redirect(url)
​
​
@app.route("/register")
def register():
    # url = "/"
    url = url_for("index")
    return redirect(url)
​
​
if __name__ == '__main__':
    # 通过url_map可以查看整个flask中的路由信息
    print(app.url_map)
    # 启动flask程序
    app.run(host = "0.0.0.0",port = 5000,=True)

4.2.6 动态路由

# 路由传递的参数默认当做string处理,这里指定int,尖括号中冒号后面的内容是动态的
@app.route('/user/<int:id>')
def hello_itcast(id):
    return 'hello itcast %d' %id

 

4.2.7自定义转换器

from flask import Flask
from werkzeug.routing import BaseConverter
​
class Regex_url(BaseConverter):
    def __init__(self,url_map,*args):
        super(Regex_url,self).__init__(url_map)
        self.regex = args[0]
​
app = Flask(__name__)
app.url_map.converters['re'] = Regex_url
​
@app.route('/user/<re("[a-z]{3}"):id>')
def hello_itcast(id):
    return 'hello %s' %id
​

 

# coding:utf-8
​
from flask import Flask, current_app, redirect, url_for
from werkzeug.routing import BaseConverter
​
# 创建flask的应用对象
# __name__表示当前的模块名字
#           模块名,flask以这个模块所在的目录为总目录,默认这个目录中的static为静态目录,templates为模板目录
app = Flask(__name__)
​
​
# 转换器
# 127.0.0.1:5000/goods/123
# @app.route("/goods/<int:goods_id>")
@app.route("/goods/<goods_id>")  # 不加转换器类型, 默认是普通字符串规则(除了/的字符)
def goods_detail(goods_id):
    """定义的视图函数"""
    return "goods detail page %s" % goods_id
​
​
# 1. 定义自己的转换器
class MobileConverter(BaseConverter):
    def __init__(self, url_map):
        super(MobileConverter, self).__init__(url_map)
        self.regex = r'1[34578]\d{9}'
​
​
class RegexConverter(BaseConverter):
    """"""
    def __init__(self, url_map, regex):
        # 调用父类的初始化方法
        super(RegexConverter, self).__init__(url_map)
        # 将正则表达式的参数保存到对象的属性中,flask会去使用这个属性来进行路由的正则匹配
        self.regex = regex
​
    def to_python(self, value):
        """"""
        print("to_python方法被调用")
        # return "abc"
        # value是在路径进行正则表达式匹配的时候提取的参数
        return value
​
    def to_url(self, value):
        """使用url_for的方法的时候被调用"""
        print("to_url方法被调用")
        # return "15811111111"
        return value
​
​
# 2. 将自定义的转换器添加到flask的应用中
app.url_map.converters["re"] = RegexConverter
app.url_map.converters["mobile"] = MobileConverter
​
​
# 127.0.0.1:5000/send/18612345678
# @app.route("/send/<mobile:mobile_num>")
@app.route("/send/<re(r'1[34578]\d{9}'):mobile_num>")
def send_sms(mobile_num):
    return "send sms to %s" % mobile_num
​
​
@app.route("/index")
def index():
    url = url_for("send_sms", mobile_num="18922222222")
    # /send/18922222222
    return redirect(url)
​
​
@app.route("/call/<re(r''):tel>")
def call_tel(tel):
    pass
​
​
if __name__ == '__main__':
    # 通过url_map可以查看整个flask中的路由信息
    print(app.url_map)
    # 启动flask程序
    app.run(host = "0.0.0.0",port = 5000,debug=True)

4.3 获取请求参数

from flask import request

# coding:utf-8
​
from flask import Flask, request
​
​
app = Flask(__name__)
​
​
# 接口  api
# 127.0.0.1:5000/index?city=shenzhen&country=china  查询字符串 QueryString
@app.route("/index", methods=["GET", "POST"])
def index():
    # request中包含了前端发送过来的所有请求数据
    # form和data是用来提取请求体数据
    # 通过requset.form可以直接提取请求体中的表单格式的数据, 是一个类字典的对象
    # 通过get方法只能拿到多个同名参数的第一个
    name = request.form.get("name")
    age = request.form.get("age")
    name_li = request.form.getlist("name")
​
    # 如果是请求体的数据不是表单格式的(如json格式),可以通过request.data获取
    print("request.data: %s" % request.data)
​
    # args是用来提取url中的参数(查询字符串)
    city = request.args.get("city")
    return "hello name=%s, age=%s, city=%s, name_li=%s" % (name, age, city, name_li)
​
​
# def register():
#     if request.method == 'GET':
#         return render(request, "register.html")
#     else:
#         
if __name__ == '__main__':
    app.run(debug=True)
​

 

4.3.1 上传文件

已上传的文件存储在内存或是文件系统中一个临时的位置。你可以通过请求对象的 files 属性访问它们。每个上传的文件都会存储在这个字典里。它表现近乎为一个标准的 Python file 对象,但它还有一个 save() 方法,这个方法允许你把文件保存到服务器的文件系统上。这里是一个用它保存文件的例子:

from flask import request
​
@app.route('/upload', methods=['GET', 'POST'])
def upload_file():
    if request.method == 'POST':
        f = request.files['the_file']
        f.save('/var/www/uploads/uploaded_file.txt')

如果你想知道上传前文件在客户端的文件名是什么,你可以访问 filename 属性。但请记住, 永远不要信任这个值,这个值是可以伪造的。如果你要把文件按客户端提供的文件名存储在服务器上,那么请把它传递给 Werkzeug 提供的 secure_filename() 函数:

from flask import request
from werkzeug import secure_filename
​
@app.route('/upload', methods=['GET', 'POST'])
def upload_file():
    if request.method == 'POST':
        f = request.files['the_file']
        f.save('/var/www/uploads/' + secure_filename(f.filename))

代码演示

# coding:utf-8
​
from flask import Flask, request
​
​
app = Flask(__name__)
​
​
@app.route("/upload", methods=["POST"])
def upload():
    """接受前端传送过来的文件"""
    file_obj = request.files.get("pic")
    if file_obj is None:
        # 表示没有发送文件
        return "未上传文件"
​
    # 将文件保存到本地
    # # 1. 创建一个文件
    # f = open("./demo.png", "wb")
    # # 2. 向文件写内容
    # data = file_obj.read()
    # f.write(data)
    # # 3. 关闭文件
    # f.close()
​
    # 直接使用上传的文件对象保存
    file_obj.save("./demo1.png")
    return "上传成功"
​
​
if __name__ == '__main__':
    app.run(debug=True)

上下文管理

# coding:utf-8
​
​
# f = open("./1.txt", "wb")
# # 2. 向文件写内容
# try:
#     f.write("hello flask")
# except Exception:
#     pass
# finally:
#     # 3. 关闭文件
#     f.close()
​
​
# 上下文管理器
# with open("./1.txt", "wb") as f:
#     f.write("hello flask")
#     f.write("hello flask")
#     f.write("hello flask")
#     f.write("hello flask")
​
​
class Foo(object):
    def __enter__(self):
        """进入with语句的时候被with调用"""
        print("enter called")
​
    def __exit__(self, exc_type, exc_val, exc_tb):
        """离开with语句的时候被with调用"""
        print("exit called")
        print("exc_type: %s" % exc_type)
        print("exc_val: %s" % exc_val)
        print("exc_tb: %s" % exc_tb)
​
​
with Foo() as foo:
    print("hello python")
    a = 1 / 0
    print("hello end")
​
# 进入with语句的时候,with帮助我们调用对象的__enter__方法,
# 离开with语句的时候,with帮助我们调用对象的__exit__方法
​

4.4 abort函数与自定义异常处理

4.4.1 abort函数

from flask import abort

4.4.2 自定义异常处理

@app.errorhandler(404)
​
def error(e):
​
    return '您请求的页面不存在了,请确认后再次访问!%s'%e

代码演示

# coding:utf-8
​
from flask import Flask, request, abort, Response
​
​
app = Flask(__name__)
​
​
@app.route("/login", methods=["GET"])
def login():
    # name = request.form.get()
    # pwd = request.form.get()
    name = ""
    pwd = ""
    if name != "zhangsan" or pwd != "admin":
        # 使用abort函数可以立即终止视图函数的执行
        # 并可以返回给前端特定的信息
        # 1 传递状态码信息, 必须是标准的http状态码
        abort(404)
        # # 2. 传递响应体信息
        # resp = Response("login failed")
        # abort(resp)
​
    return "login success"
​
​
# 定义错误处理的方法
@app.errorhandler(404)
def handle_404_error(err):
    """自定义的处理错误方法"""
    # 这个函数的返回值会是前端用户看到的最终结果
    return u"出现了404错误, 错误信息:%s" % err
​
​
if __name__ == '__main__':
    app.run(debug=True)

 

4.5 返回的响应数据

4.5.1 元组

可以返回一个元组,这样的元组必须是 (response, status, headers) 的形式,且至少包含一个元素。 status 值会覆盖状态代码, headers 可以是一个列表或字典,作为额外的消息标头值。

4.5.2 make_response

resp = make_response()
​
resp.headers[“sample”] = “value”
​
resp.status = “404 not found”
# coding:utf-8
​
from flask import Flask, request, abort, Response, make_response
​
​
app = Flask(__name__)
​
​
@app.route("/index")
def index():
    # 1 使用元祖,返回自定义的响应信息
    #        响应体       状态码 响应头
    # return "index page", 400, [("Itcast", "pyton"), ("City", "shenzhen")]
    # return "index page", 400, {"Itcast1": "python1", "City1": "sz1"}
    # return "index page", 666, {"Itcast1": "python1", "City1": "sz1"}
    # return "index page", "666 itcast status", {"Itcast1": "python1", "City1": "sz1"}
    # return "index page", "666 itcast status"
​
    # 2 使用make_response 来构造响应信息
    resp = make_response("index page 2")
    resp.status = "999 itcast"  # 设置状态码
    resp.headers["city"] = "sz"  # 设置响应头
    return resp
​
​
if __name__ == '__main__':
    app.run(debug=True)
​

 

4.5.3使用jsonify返回json数据

# coding:utf-8
​
from flask import Flask, jsonify
import json
​
​
app = Flask(__name__)
​
​
@app.route("/index")
def index():
    # json就是字符串
    data = {
        "name": "python",
        "age": 24
    }
    # # json.dumps(字典)  将python的字典转换为json字符串
    # # json.loads(字符串)  将字符串转换为python中的字典
    #
    # json_str = json.dumps(data)
    #
    # return json_str, 200, {"Content-Type": "application/json"}
​
    # jsonify帮助转为json数据,并设置响应头 Content-Type 为application/json
    # return jsonify(data)
​
    return jsonify(city="sz", country="china")
​
​
if __name__ == '__main__':
    app.run(debug=True)

4.5.4重定向

from flask import redirect

4.6 设置和读取cookie

make_response 模块名

set_cookie(key, value=’’, max_age=None) 设置cookie

delete_cookie(key) 删除cookie

# coding:utf-8
​
from flask import Flask, make_response, request
​
​
app = Flask(__name__)
​
​
@app.route("/set_cookie")
def set_cookie():
    resp = make_response("success")
    # 设置cookie, 默认有效期是临时cookie,浏览器关闭就失效
    resp.set_cookie("Itcast", "Python")
    resp.set_cookie("Itcast1", "Python1")
    # max_age设置有效期,单位:秒
    resp.set_cookie("Itcast2", "Python1", max_age=3600)
    resp.headers["Set-Cookie"] = "Itcast3=Python3; Expires=Sat, 18-Nov-2017 04:36:04 GMT; Max-Age=3600; Path=/"
    return resp
​
​
@app.route("/get_cookie")
def get_cookie():
    c = request.cookies.get("Itcast")
    return c
​
​
@app.route("/delete_cookie")
def delete_cookie():
    resp = make_response("del success")
    # 删除cookie
    resp.delete_cookie("Itcast1")
    return resp
​
​
if __name__ == '__main__':
    app.run(debug=True)

4.7 session

from flask import session

需要设置secret_key

# coding:utf-8
​
from flask import Flask, session, current_app
​
​
app = Flask(__name__)
​
# flask的session需要用到的秘钥字符串
app.config["SECRET_KEY"] = "dhsodfhisfhosdhf29fy989"
​
​
# flask默认把session保存到了cookie中
​
@app.route("/login")
def login():
    # 设置session数据
    session["name"] = "python"
    session["mobile"] = "18611111111"
    return "login success"
​
​
@app.route("/index")
def index():
    # 获取session数据
    name = session.get("name")
    return "hello %s" % name
​
​
if __name__ == '__main__':
    app.run(debug=True)

 

4.8 请求上下文与应用上下文

请求上下文(request context)

request和session都属于请求上下文对象。

应用上下文(application context)

current_app和g都属于应用上下文对象。

current_app:表示当前运行程序文件的程序实例。

g:处理请求时,用于临时存储的对象,每次请求都会重设这个变量。

 

4.9 请求钩子

请求钩子是通过装饰器的形式实现,Flask支持如下四种请求钩子:

before_first_request:在处理第一个请求前运行。

@app.before_first_request

before_request:在每次请求前运行。

after_request(response):如果没有未处理的异常抛出,在每次请求后运行。

teardown_request(response):在每次请求后运行,即使有未处理的异常抛出。

 

# coding:utf-8
​
from flask import Flask, session, current_app, request, url_for
​
​
app = Flask(__name__)
​
​
@app.route("/index")
def index():
    print("index 被执行")
    a = 1 / 0
    return "index page"
​
​
@app.route("/hello")
def hello():
    print("hello 被执行")
    return "hello page"
​
​
@app.before_first_request
def handle_before_first_request():
    """在第一次请求处理之前先被执行"""
    print("handle_before_first_request 被执行")
​
​
@app.before_request
def handle_before_request():
    """在每次请求之前都被执行"""
    print("handle_before_request 被执行")
​
​
@app.after_request
def handle_after_request(response):
    """在每次请求(视图函数处理)之后都被执行, 前提是视图函数没有出现异常"""
    print("handle_after_request 被执行")
    return response
​
​
@app.teardown_request
def handle_teardown_request(response):
    """在每次请求 (视图函数处理)之后都被执行, 无论视图函数是否出现异常,都被执行, 工作在非调试模式时 debug=False"""
    path = request.path
    if path == url_for("index"):
        print("在请求钩子中判断请求的视图逻辑: index")
    elif path == url_for("hello"):
        print("在请求钩子中判断请求的视图逻辑: hello")
    print("handle_teardown_request 被执行")
    return response
​
​
if __name__ == '__main__':
    app.run()

 

5. Flask-Script扩展命令行

pip install Flask-Script

 from flask import Flask
​
from flask_script import Manager
​
 app = Flask(__name__)
​
 manager = Manager(app)
​
 @app.route('/')
​
def index():
​
•    return '床前明月光'
​
 if __name__ == "__main__":
​
•    manager.run()   

 

# coding:utf-8
​
from flask import Flask
from flask_script import Manager   # 启动命令的管理类
​
​
app = Flask(__name__)
​
# 创建Manager管理类的对象
manager = Manager(app)
​
​
@app.route("/index")
def index():
    return "index page"
​
​
if __name__ == '__main__':
    # app.run(debug=True)
    # 通过管理对象来启动flask
    manager.run()

6. Jinja2模板

6.1 基本流程

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Template</title>
</head>
<body>
    <h1>hello {{ name }}</h1>
</body>
</html>
@app.route("/")
def index():
    return render_template("index.html", name="python")

使用flask 中的render_template渲染模板

6.2 变量

<p>{{mydict['key']}}</p>
​
<p>{{mydict.key}}</p>
​
<p>{{mylist[1]}}</p>
​
<p>{{mylist[myvariable]}}</p>
​
from flask import Flask,render_template
app = Flask(__name__)
​
@app.route('/')
def index():
    mydict = {'key':'silence is gold'}
    mylist = ['Speech', 'is','silver']
    myintvar = 0
​
    return render_template('vars.html',
                           mydict=mydict,
                           mylist=mylist,
                           myintvar=myintvar
                           )
if __name__ == '__main__':
    app.run(debug=True)

 

6.3 过滤器

6.3.1 字符串过滤器

**safe****:禁用转义;**
​
  <p>{{ '<em>hello</em>' | safe }}</p>
​
**capitalize****:把变量值的首字母转成大写,其余字母转小写;**
​
  <p>{{ 'hello' | capitalize }}</p>
​
 **lower****:把值转成小写;**
​
  <p>{{ 'HELLO' | lower }}</p>
​
 **upper****:把值转成大写;**
​
  <p>{{ 'hello' | upper }}</p>
​
 **title****:把值中的每个单词的首字母都转成大写;**
​
  <p>{{ 'hello' | title }}</p>
​
 **trim****:把值的首尾空格去掉;**
​
  <p>{{ ' hello world ' | trim }}</p>
​
 **reverse:****字符串反转;**
​
  <p>{{ 'olleh' | reverse }}</p>
​
 **format:****格式化输出;**
​
  <p>{{ '%s is %d' | format('name',17) }}</p>
​
 **striptags****:渲染之前把值中所有的HTML****标签都删掉;**
​
  <p>{{ '<em>hello</em>' | striptags }}</p>
​
 
​
##### 6.3.2 支持链式使用过滤器
​
<p>{{ “ hello world  “ | trim | upper }}</p>

6.3.3 列表过滤器

**first****:取第一个元素**
​
  <p>{{ [1,2,3,4,5,6] | first }}</p>
​
**last****:取最后一个元素**
​
  <p>{{ [1,2,3,4,5,6] | last }}</p>
​
**length****:获取列表长度**
​
  <p>{{ [1,2,3,4,5,6] | length }}</p>
​
**sum****:列表求和**
​
  <p>{{ [1,2,3,4,5,6] | sum }}</p>
​
**sort****:列表排序**
​
  <p>{{ [6,2,3,1,5,4] | sort }}</p>

6.3.4 自定义过滤器

自定义的过滤器名称如果和内置的过滤器重名,会覆盖内置的过滤器。

 方式一:
​
•         通过 **add_template_filter (****过滤器函数****,** **模板中使用的过滤器名字****)**
​
def filter_double_sort(ls):
​
•    return ls[::2]
​
app.add_template_filter(filter_double_sort,'double_2')
 
方式二:
​
通过装饰器  **app.template_filter (****模板中使用的装饰器名字)**
​
@app.template_filter('db3')
​
def filter_double_sort(ls):
​
    return ls[::-3]

代码演示

1、templates.py

# coding:utf-8
​
from flask import Flask, render_template
​
​
app = Flask(__name__)
​
​
@app.route("/index")
def index():
    data = {
        "name": "python",
        "age": 18,
        "my_dict": {"city": "sz"},
        "my_list": [1, 2, 3, 4, 5],
        "my_int": 0
    }
    return render_template("index.html", **data)
​
​
def list_step_2(li):
    """自定义的过滤器"""
    return li[::2]
​
​
# 注册过滤器
app.add_template_filter(list_step_2, "li2")
​
​
@app.template_filter("li3")
def list_step_3(li):
    """自定义的过滤器"""
    return li[::3]
​
​
if __name__ == '__main__':
    app.run(debug=True)
​

2、 index.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
    <p>name = {{ name }}</p>
    <p>age = {{ age }}</p>
    <p>my_dict: city= {{ my_dict["city"] }}</p>
    <p>my_dict: city= {{ my_dict.city }}</p>
    <p>my_list: {{ my_list }}</p>
    <p>my_list[my_int]: {{ my_list[my_int] }}</p>
    <p>my_list[0] + my_list[1] = {{ my_list[0] + my_list[1] }}</p>
    <p>{{"hello" + " python" }}</p>
    <p>a{{"   flask world    " | trim | upper }}a</p>
    <hr/>
    <p>{{my_list | li2 }}</p>
    <p>{{my_list | li3 }}</p>
</body>
</html>

xss.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
    <form method="post">
        <textarea name="text"></textarea>
        <input type="submit" value="提交">
    </form>
    {{ text |safe}}   //加上|safe表示禁止转义,可形成xss攻击,在表单中填入脚本提交后执行·表单内脚本
</body>
</html>

xss.py

# coding:utf-8
​
from flask import Flask, render_template, request
​
​
app = Flask(__name__)
​
​
@app.route("/xss", methods=["GET", "POST"])
def xss():
    text = ""
    if request.method == "POST":
        text = request.form.get("text")
​
    return render_template("xss.html", text=text)
​
​
if __name__ == '__main__':
    app.run(debug=True)
​

6.4 表单

使用Flask-WTF表单扩展,可以帮助进行CSRF验证,帮助我们快速定义表单模板,而且可以帮助我们在视图中验证表的数据

pip install Flask-WTF

6.4.1 不使用Flask-WTF扩展时,表单需要自己处理

#模板文件
<form method='post'>
    <input type="text" name="username" placeholder='Username'>
    <input type="password" name="password" placeholder='password'>
    <input type="submit">
</form>
from flask import Flask,render_template,request
​
@app.route('/login',methods=['GET','POST'])
def login():
    if request.method == 'POST':
        username = request.form['username']
        password = request.form['password']
        print username,password
        return “success”
    else:
        return render_template(“login.html”)

6.4.2 使用Flask-WTF扩展

需要设置 SECRET_KEY 的配置参数

 模板页:
<form method="post">
        #设置csrf_token
        {{ form.csrf_token() }}
        {{ form.us.label }}
        <p>{{ form.us }}</p>
        {{ form.ps.label }}
        <p>{{ form.ps }}</p>
        {{ form.ps2.label }}
        <p>{{ form.ps2 }}</p>
        <p>{{ form.submit() }}</p>
        {% for x in get_flashed_messages() %}
            {{ x }}
        {% endfor %}
 </form>
rf#coding=utf-8
from flask import Flask,render_template, redirect,url_for,session,request,flash
​
#导入wtf扩展的表单类
from flask_wtf import FlaskForm
#导入自定义表单需要的字段
from wtforms import SubmitField,StringField,PasswordField
#导入wtf扩展提供的表单验证器
from wtforms.validators import DataRequired,EqualTo
app = Flask(__name__)
app.config['SECRET_KEY']='1'
​
#自定义表单类,文本字段、密码字段、提交按钮
class Login(Flask Form):
    us = StringField(label=u'用户:',validators=[DataRequired()])
    ps = PasswordField(label=u'密码',validators=[DataRequired(),EqualTo('ps2','err')])
    ps2 = PasswordField(label=u'确认密码',validators=[DataRequired()])
    submit = SubmitField(u'提交')
​
#定义根路由视图函数,生成表单对象,获取表单数据,进行表单数据验证
@app.route('/',methods=['GET','POST'])
def index():
    form = Login()
    if form.validate_on_submit():
        name = form.us.data
        pswd = form.ps.data
        pswd2 = form.ps2.data
        print name,pswd,pswd2
        return redirect(url_for('login'))
    else:
        if request.method=='POST':
flash(u'信息有误,请重新输入!')
​
    return render_template('index.html',form=form)
if __name__ == '__main__':
    app.run(debug=True)

 

6.5 控制语句

6.5.1 if语句

{% if %} {% endif %}
@app.route('/user')
def user():
    user = 'dongGe'
    return render_template('user.html',user=user)
​
​
 <html>
 <head>
     {% if user %}
        <title> hello {{user}} </title>
    {% else %}
         <title> welcome to flask </title>        
    {% endif %}
 </head>
 <body>
     <h1>hello world</h1>
 </body>
 </html>

6.5.2 for语句

{% for item in samples %} {% endfor %}
@app.route('/loop')
 def loop():
    fruit = ['apple','orange','pear','grape']
    return render_template('loop.html',fruit=fruit)
 <html>
 <head>
     {% if user %}
        <title> hello {{user}} </title>
    {% else %}
         <title> welcome to flask </title>        
    {% endif %}
 </head>
 <body>
     <h1>hello world</h1>
    <ul>
        {% for index in fruit %}
            <li>{{ index }}</li>
        {% endfor %}
    </ul>
 </body>
 </html>

 

6.6 宏

类似于python中的函数,宏的作用就是在模板中重复利用代码,避免代码冗余。

6.6.1 不带参数宏的定义与使用

定义:**
​
{% macro input() %}
​
  <input type="text"
•         name="username"
•         value=""
•         size="30"/>
{% endmacro %}
使用
​
{{ input() }}

 

6.6.2 带参数宏的定义与使用

定义
​
{% macro input(name,value='',type='text',size=20) %}
    <input type="{{ type }}"
           name="{{ name }}"
           value="{{ value }}"
           size="{{ size }}"/>
{% endmacro %}
​
使用
{{ input(value='name',type='password',size=40)}}
### 6.6.3 将宏单独封装在html文件中
文件名可以自定义macro.html
{% macro input() %}
    <input type="text" name="username" placeholde="Username">
    <input type="password" name="password" placeholde="Password">
    <input type="submit">                           
{% endmacro %}                 
                    
在其它模板文件中先导入,再调用
{% import 'macro.html' as func %}
​
{% func.input() %}
定义带参数的宏
​
{% macro input(name,value='',type='text',size=20) %}
    <input type="{{ type }}"
           name="{{ name }}"
           value="{{ value }}"
           size="{{ size }}"/>
{% endmacro %}
调用宏,并传递参数
{{ input(value='name',type='password',size=40)}}
把宏单独抽取出来,封装成html文件,其它模板中导入使用
文件名可以自定义macro.html
​
{% macro function() %}
​
    <input type="text" name="username" placeholde="Username">
    <input type="password" name="password" placeholde="Password">
    <input type="submit">
{% endmacro %}
在其它模板文件中先导入,再调用
​
{% import 'macro.html' as func %}
{% func.function() %}

6.4 模板继承

extend

6.5 模板包含

includ

模板继承:
模板继承是为了重用模板中的公共内容。一般Web开发中,继承主要使用在网站的顶部菜单、底部。这些内容可以定义在父模板中,子模板直接继承,而不需要重复书写。
​
{% block top %}``{% endblock %}标签定义的内容,相当于在父模板中挖个坑,当子模板继承父模板时,可以进行填充。
​
子模板使用extends指令声明这个模板继承自哪?父模板中定义的块在子模板中被重新定义,在子模板中调用父模板的内容可以使用super()。
​
父模板:base.html
  {% block top %}
    顶部菜单
  {% endblock top %}
​
  {% block content %}
  {% endblock content %}
​
  {% block bottom %}
    底部
  {% endblock bottom %}
子模板:
  {% extends 'base.html' %}
  {% block content %}
   需要填充的内容
  {% endblock content %}
模板继承使用时注意点:
不支持多继承。
为了便于阅读,在子模板中使用extends时,尽量写在模板的第一行。
不能在一个模板文件中定义多个相同名字的block标签。
当在页面中使用多个block标签时,建议给结束标签起个名字,当多个block嵌套时,阅读性更好。
包含(Include)
Jinja2模板中,除了宏和继承,还支持一种代码重用的功能,叫包含(Include)。它的功能是将另一个模板整个加载到当前模板中,并直接渲染。
​
示例:
​
include的使用
​
{\% include 'hello.html' %}
​
包含在使用时,如果包含的模板文件不存在时,程序会抛出TemplateNotFound异常,可以加上ignore missing关键字。如果包含的模板文件不存在,会忽略这条include语句。
​
示例:
​
include的使用加上关键字ignore missing
​
{\% include 'hello.html' ignore missing %}
​
宏、继承、包含:
宏(Macro)、继承(Block)、包含(include)均能实现代码的复用。
继承(Block)的本质是代码替换,一般用来实现多个页面中重复不变的区域。
宏(Macro)的功能类似函数,可以传入参数,需要定义、调用。
包含(include)是直接将目标模板文件整个渲染出来。

 

6.6 flask在模板中使用特殊变量和方法

6.6.1 config

6.6.2 request

6.6.3 url_for

3.6 Flask中的特殊变量和方法:
在Flask中,有一些特殊的变量和方法是可以在模板文件中直接访问的。
​
config 对象:
​
​
config 对象就是Flask的config对象,也就是 app.config 对象。
​
{{ config.SQLALCHEMY_DATABASE_URI }}
request 对象:
​
就是 Flask 中表示当前请求的 request 对象,request对象中保存了一次HTTP请求的一切信息。
​
request常用的属性如下:
​
属性  说明  类型
data    记录请求的数据,并转换为字符串 *
form    记录请求中的表单数据  MultiDict
args    记录请求中的查询参数  MultiDict
cookies 记录请求中的cookie信息  Dict
headers 记录请求中的报文头   EnvironHeaders
method  记录请求使用的HTTP方法   GET/POST
url 记录请求的URL地址  string
files   记录请求上传的文件   *
​
{{ request.url }}
url_for 方法:
​
url_for() 会返回传入的路由函数对应的URL,所谓路由函数就是被 app.route() 路由装饰器装饰的函数。如果我们定义的路由函数是带有参数的,则可以将这些参数作为命名参数传入。
​
{{ url_for('index') }}
​
{{ url_for('post', post_id=1024) }}
get_flashed_messages方法:
​
返回之前在Flask中通过 flash() 传入的信息列表。把字符串对象表示的消息加入到一个消息队列中,然后通过调用 get_flashed_messages() 方法取出。
​
​
{% for message in get_flashed_messages() %}
    {{ message }}
{% endfor %}  
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值