flask
Falsk
这是对于flask的定义:
Flask is a microframework for Python based on Werkzeug, Jinja 2 and good intentions.
“微”(micro) 并不表示你需要把整个 Web 应用塞进单个 Python 文件(虽然确实可以 ),也不意味着 Flask 在功能上有所欠缺。微框架中的“微”意味着 Flask 旨在保持核心简单而易于扩展。Flask 不会替你做出太多决策——比如使用何种数据库。而那些 Flask 所选择的——比如使用何种模板引擎——则很容易替换。除此之外的一切都由可由你掌握。如此,Flask 可以与您珠联璧合。
默认情况下,Flask 不包含数据库抽象层、表单验证,或是其它任何已有多种库可以胜任的功能。然而,Flask 支持用扩展来给应用添加这些功能,如同是 Flask 本身实现的一样。众多的扩展提供了数据库集成、表单验证、上传处理、各种各样的开放认证技术等功能。Flask 也许是“微小”的,但它已准备好在需求繁杂的生产环境中投入使用。
在讲flask之前我们首先看下flask与其他常用的两种框架的优缺点:
Django 主要特点是大而全,集成了很多组件,例如: Models Admin Form 等等, 不管你用得到用不到,反正它全都有,属于全能型框架
Tornado 主要特点是原生异步非阻塞,在IO密集型应用和多任务处理上占据绝对性的优势,属于专注型框架
Flask 主要特点小而轻,原生组件几乎为0, 三方提供的组件请参考Django 非常全面,属于短小精悍型框架
Django 通常用于大型Web应用由于内置组件足够强大所以使用Django开发可以一气呵成
Tornado 通常用于API后端应用,游戏服务后台,其内部实现的异步非阻塞真是稳得一批
Flask 通常应用于小型应用和快速构建应用,其强大的三方库,足以支撑一个大型的Web应用
Django 优点是大而全,缺点也就暴露出来了,这么多的资源一次性全部加载,肯定会造成一部分的资源浪费
Tornado 优点是异步,缺点是干净,连个Session都不支持
Flask 优点是精悍简单,缺点是你不会!哈哈哈哈哈哈!
Start Flask
pip3 insatll flask
werkzeug 简单应用
from werkzeug.wrappers import Request, Response @Request.application def hello(request): return Response('Hello World!') if __name__ == '__main__': # 如果在当前脚本下执行 from werkzeug.serving import run_simple run_simple('127.0.0.1', 9527, hello) # 开启本地 监听9527端口,绑定函数
Flask 简单应用
1.简单应用
from flask import Flask # 导入Flask类 app = Flask(__name__) # 实例出app对象 app.debug = True # 设置自动重启,可以在run()中设置,也可以在这儿设置。 @app.route("/") #APP的route装饰器,为视图函数设置路由。 def sayhello(): # 视图函数 return "hello word" if __name__ == '__main__': app.run(‘127.0.0.1’,9527,debug=True) # 设置host,port,debug=true
2 flask中视图函数的return:
from flask import Flask, render_template app = Flask(__name__) @app.route('/') def index(): return "hello world" #我们直接返回就相当于HTTPresponse return redirect(‘/index2’) # 跟我们的django一样 return render_template()相当于我们的render() app.run('127.0.0.1', 9527, debug=True) 但是有一点就是我们必须写return。
3 flask中的request:
那么首先我们来看GET请求的request:
然后再看POST请求:
4 flask中的args:
request.args 之 你能看见的Url参数全在里面
request.args 中保存的是url中传递的参数
然后会在控制台中看到 ImmutableMultiDict([('id', '1'), ('age', '20')])
print(request.args) # ImmutableMultiDict([('id', '1'), ('age', '20')]) print(request.args["id"]) # 1 print(request.args.get("age")) # 20 print(list(request.args.keys())) # ['id', 'age'] print(list(request.args.values())) # ['1', '20'] req_dict = dict(request.args) # {'id': ['1'], 'age': ['20']} print(req_dict)
request.args 与 request.form 的区别就是:
request.args 是获取url中的参数
request.form 是获取form表单中的参数
5 request.values 之 只要是个参数我都要
这是让我们在使用form表单提交的同时使用url参数提交,那么values拿到的就是所有的数据。
print(request.values) # CombinedMultiDict([ImmutableMultiDict([('id', '1'), ('age', '20')]), ImmutableMultiDict([('user', 'Oldboy'), ('pwd', 'DragonFire')])]) print(request.values.get("id")) # 1 print(request.values["user"]) # Oldboy # 这回喜欢直接操作字典的小伙伴们有惊喜了! to_dict() 方法可以直接将我们的参数全部转为字典形式 print(request.values.to_dict()) # {'user': 'Oldboy', 'pwd': 'DragonFire', 'id': '1', 'age': '20'} 复制代码 注意啦!注意啦! # 注意这里的坑来啦! 坑来啦! # 如果url和form中的Key重名的话,form中的同名的key中value会被url中的value覆盖 # http://127.0.0.1:5000/req?id=1&user=20 print(request.values.to_dict()) # {'user': 20 'pwd': 'DragonFire', 'id': '1'}
6 那么同时还有其他的一些参数:
5.request.cookies 之 存在浏览器端的字符串儿也会一起带过来前提是你要开启浏览器的 cookies request.cookies 是将cookies中信息读取出来
6.request.headres 之 请求头中的秘密 用来获取本次请求的请求头
7.request.data 之 如果处理不了的就变成字符串儿存在data里面
你一定要知道 request 是基于 mimetype 进行处理的
mimetype的类型 以及 字符串儿 : http://www.w3school.com.cn/media/media_mimeref.asp
如果不属于上述类型的描述,request就会将无法处理的参数转为Json存入到 data 中
其实我们可以将 request.data , json.loads 同样可以拿到里面的参数
8.request.files 之 给我一个文件我帮你保管
如果遇到文件上传的话,request.files 里面存的是你上传的文件,但是 Flask 在这个文件的操作中加了一定的封装,让操作变得极为简单
9 request.url:
# 获取当前的url路径 print(request.path)# /req # 当前url路径的上一级路径 print(request.script_root) # # 当前url的全部路径 print(request.url) # http://127.0.0.1:5000/req # 当前url的路径的上一级全部路径 print(request.url_root ) # http://127.0.0.1:5000/
10. request.json 之 前提你得告诉是json
如果在请求中写入了 "application/json" 使用 request.json 则返回json解析数据, 否则返回 None
Flask 的模板语言:jinja2与render_template
① 那么如果我们的数据结构为一个列表:
STUDENT_LIST = [ {'name': 'Abc', 'age': 38, 'gender': '中'}, {'name': 'Boy', 'age': 73, 'gender': '男'}, {'name': 'Cn', 'age': 84, 'gender': '女'} ]
那么我们可以在模板语言中使用:
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Old Boy EDU</title> </head> <body> Welcome to Old Boy EDU <div>{{ student }}</div> <table border="1px"> <tr> <td>{{ student.name }}</td> <td>{{ student["age"] }}</td> <td>{{ student.get("gender") }}</td> </tr> </table> </body> </html>
② 那么如果为一个字典:
STUDENT_DICT = { 1: {'name': 'Old', 'age': 38, 'gender': '中'}, 2: {'name': 'Girl', 'age': 73, 'gender': '男'}, 3: {'name': 'Gay', 'age': 84, 'gender': '女'}, }
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Old Boy EDU</title>
</head>
<body>
Welcome to Old Boy EDU
<table>
{% for foo in student %}
<tr>
<td>{{ foo }}</td>
<td>{{ student.get(foo).name }}</td>
<td>{{ student[foo].get("age") }}</td>
<td>{{ student[foo]["gender"] }}</td>
</tr>
{% endfor %}
</table>
</body>
</html>
③ 那么直接在前端页面显示我们的后端的代码有两种方式:
方式①
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> {{ tag | safe}} <!-- 加上个 \ 管道符,然后 safe --> </body> </html>
方式② :
from flask import Flask from flask import render_template from flask import Markup # 导入 flask 中的 Markup 模块 app = Flask(__name__) @app.route("/") def index(): tag = "<input type='text' name='user' value='DragonFire'>" markup_tag = Markup(tag) # Markup帮助咱们在HTML的标签上做了一层封装,让Jinja2模板语言知道这是一个安全的HTML标签 print(markup_tag, type(markup_tag)) # <input type='text' name='user' value='DragonFire'> <class 'markupsafe.Markup'> return render_template("index.html", tag=markup_tag) app.run("0.0.0.0", 5000, debug=True)
④ 直接在前端执行函数两种方式:
方式一:
from flask import Flask from flask import render_template from flask import Markup # 导入 flask 中的 Markup 模块 app = Flask(__name__) #定义一个函数,把它传递给前端 def a_b_sum(a,b): return a+b @app.route("/") def index(): return render_template("index.html", tag=a_b_sum) app.run("0.0.0.0", 5000, debug=True)
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> {{ tag }} <br> {{ tag(99,1) }} </body> </html>
方式二:
from flask import Flask from flask import render_template from flask import Markup # 导入 flask 中的 Markup 模块 app = Flask(__name__) @app.template_global() # 定义全局模板函数 def a_b_sum(a, b): return a + b @app.template_filter() # 定义全局模板函数 def a_b_c_sum(a, b, c): return a + b + c @app.route("/") def index(): return render_template("index.html", tag="") app.run("0.0.0.0", 5000, debug=True)
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> {{ a_b_sum(99,1) }} <br> {{ 1 | a_b_c_sum(197,2) }} </body> </html>
⑤ jinja2模板复用block与模板的引用include
①extends
{% extends "index.html" %} {% block content %} <form> 用户名:<input type="text" name="user"> 密码:<input type="text" name="pwd"> </form> {% endblock %}
②include
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> <h1>Welcome OldboyEDU</h1> <h2>下面的内容是不一样的</h2> {% include "login.html" %} <h2>上面的内容是不一样的,但是下面的内容是一样的</h2> <h1>OldboyEDU is Good</h1> </body> </html>
⑥ jinja2模板语言的宏定义:
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> <h1>Welcome OldboyEDU</h1> {% macro type_text(name,type) %} <input type="{{ type }}" name="{{ name }}" value="{{ name }}"> {% endmacro %} <h2>在下方是使用宏来生成input标签</h2> {{ type_text("one","text") }} {{ type_text("two","text") }} </body> </html>
flask中的session:
Flask中的Session非常的奇怪,他会将你的SessionID存放在客户端的Cookie中,使用起来也非常的奇怪 1. Flask 中 session 是需要 secret_key 的 from flask import session app = Flask(__name__) app.secret_key = "zhang" secret_key 实际上是用来加密字符串的,如果在实例化的app中没有 secret_key 那么开启session一定会抛异常的
2. session 要这样用 @app.route("/login", methods=["GET", "POST"]) def login(): if request.method == "POST": if request.form["username"] == USER["username"] and request.form["password"] == USER["password"]: session["user"] = USER["username"] return redirect("/student_list") return render_template("login.html", msg="用户名密码错误") return render_template("login.html", msg=None) # 如果前端Jinja2模板中使用了msg,这里就算是传递None也要出现msg session["user"] = USER["username"] 这样用就代表这个请求带上来的session中保存了一个user=name 如果想要验证session的话,就用这种方法吧 3. cookies 中的 session 是什么 cookies 中 session 存储的是通过 secret_key 加密后的 key , 通过这个 key 从flask程序的内存中找到用户对应的session信息 4. 怎么用 session 进行验证呢? @app.route("/student_list") def student(): if session.get("user"): return render_template("student_list.html", student=STUDENT_DICT) return redirect("/login")
flask中的路由系统:
1. @app.route() 装饰器中的参数 如果不明白装饰器 点击这里 methods : 当前 url 地址,允许访问的请求方式 @app.route("/info", methods=["GET", "POST"]) def student_info(): stu_id = int(request.args["id"]) return f"Hello Old boy {stu_id}" # Python3.6的新特性 f"{变量名}" endpoint : 反向url地址,默认为视图函数名 (url_for) from flask import url_for @app.route("/info", methods=["GET", "POST"], endpoint="r_info") def student_info(): print(url_for("r_info")) # /info stu_id = int(request.args["id"]) return f"Hello Old boy {stu_id}" # Python3.6的新特性 f"{变量名}" defaults : 视图函数的参数默认值{"nid":1} from flask import url_for @app.route("/info", methods=["GET", "POST"], endpoint="r_info", defaults={"nid": 100}) def student_info(nid): print(url_for("r_info")) # /info # stu_id = int(request.args["id"]) print(nid) # 100 return f"Hello Old boy {nid}" # Python3.6的新特性 f"{变量名}" strict_slashes : url地址结尾符"/"的控制 False : 无论结尾 "/" 是否存在均可以访问 , True : 结尾必须不能是 "/" # 访问地址 : /info @app.route("/info", strict_slashes=True) def student_info(): return "Hello Old boy info" # 访问地址 : /infos or /infos/ @app.route("/infos", strict_slashes=False) def student_infos(): return "Hello Old boy infos" redirect_to : url地址重定向 # 访问地址 : /info 浏览器跳转至 /infos @app.route("/info", strict_slashes=True, redirect_to="/infos") def student_info(): return "Hello Old boy info" @app.route("/infos", strict_slashes=False) def student_infos(): return "Hello Old boy infos" subdomain : 子域名前缀 subdomian="DragonFire" 这样写可以得到 DragonFire.oldboyedu.com 前提是app.config["SERVER_NAME"] = "oldboyedu.com" app.config["SERVER_NAME"] = "oldboy.com" @app.route("/info",subdomain="DragonFire") def student_info(): return "Hello Old boy info" # 访问地址为: DragonFire.oldboy.com/info 关于路由目前就说这么多,之后的课程中会有关于Flask路由系统的源码剖析,再详细说明Flask路由系统的工作原理 2.动态参数路由: from flask import url_for # 访问地址 : http://127.0.0.1:5000/info/1 @app.route("/info/<int:nid>", methods=["GET", "POST"], endpoint="r_info") def student_info(nid): print(url_for("r_info",nid=2)) # /info/2 return f"Hello Old boy {nid}" # Python3.6的新特性 f"{变量名}" <int:nid> 就是在url后定义一个参数接收 但是这种动态参数路由,在url_for的时候,一定要将动态参数名+参数值添加进去,否则会抛出参数错误的异常 3.路由正则:
配置文件系统
flask中的配置文件是一个flask.config.Config对象(继承字典),默认配置为:
{
'DEBUG': False, # 是否开启Debug模式
'TESTING': False, # 是否开启测试模式
'PROPAGATE_EXCEPTIONS': None, # 异常传播(是否在控制台打印LOG) 当Debug或者testing开启后,自动为True
'PRESERVE_CONTEXT_ON_EXCEPTION': None, # 一两句话说不清楚,一般不用它
'SECRET_KEY': None, # 之前遇到过,在启用Session的时候,一定要有它
'PERMANENT_SESSION_LIFETIME': 31, # days , Session的生命周期(天)默认31天
'USE_X_SENDFILE': False, # 是否弃用 x_sendfile
'LOGGER_NAME': None, # 日志记录器的名称
'LOGGER_HANDLER_POLICY': 'always',
'SERVER_NAME': None, # 服务访问域名
'APPLICATION_ROOT': None, # 项目的完整路径
'SESSION_COOKIE_NAME': 'session', # 在cookies中存放session加密字符串的名字
'SESSION_COOKIE_DOMAIN': None, # 在哪个域名下会产生session记录在cookies中
'SESSION_COOKIE_PATH': None, # cookies的路径
'SESSION_COOKIE_HTTPONLY': True, # 控制 cookie 是否应被设置 httponly 的标志,
'SESSION_COOKIE_SECURE': False, # 控制 cookie 是否应被设置安全标志
'SESSION_REFRESH_EACH_REQUEST': True, # 这个标志控制永久会话如何刷新
'MAX_CONTENT_LENGTH': None, # 如果设置为字节数, Flask 会拒绝内容长度大于此值的请求进入,并返回一个 413 状态码
'SEND_FILE_MAX_AGE_DEFAULT': 12, # hours 默认缓存控制的最大期限
'TRAP_BAD_REQUEST_ERRORS': False,
# 如果这个值被设置为 True ,Flask不会执行 HTTP 异常的错误处理,而是像对待其它异常一样,
# 通过异常栈让它冒泡地抛出。这对于需要找出 HTTP 异常源头的可怕调试情形是有用的。
'TRAP_HTTP_EXCEPTIONS': False,
# Werkzeug 处理请求中的特定数据的内部数据结构会抛出同样也是“错误的请求”异常的特殊的 key errors 。
# 同样地,为了保持一致,许多操作可以显式地抛出 BadRequest 异常。
# 因为在调试中,你希望准确地找出异常的原因,这个设置用于在这些情形下调试。
# 如果这个值被设置为 True ,你只会得到常规的回溯。
'EXPLAIN_TEMPLATE_LOADING': False,
'PREFERRED_URL_SCHEME': 'http', # 生成URL的时候如果没有可用的 URL 模式话将使用这个值
'JSON_AS_ASCII': True,
# 默认情况下 Flask 使用 ascii 编码来序列化对象。如果这个值被设置为 False ,
# Flask不会将其编码为 ASCII,并且按原样输出,返回它的 unicode 字符串。
# 比如 jsonfiy 会自动地采用 utf-8 来编码它然后才进行传输。
'JSON_SORT_KEYS': True,
#默认情况下 Flask 按照 JSON 对象的键的顺序来序来序列化它。
# 这样做是为了确保键的顺序不会受到字典的哈希种子的影响,从而返回的值每次都是一致的,不会造成无用的额外 HTTP 缓存。
# 你可以通过修改这个配置的值来覆盖默认的操作。但这是不被推荐的做法因为这个默认的行为可能会给你在性能的代价上带来改善。
'JSONIFY_PRETTYPRINT_REGULAR': True,
'JSONIFY_MIMETYPE': 'application/json',
'TEMPLATES_AUTO_RELOAD': None,
}
方式一:
app.config['DEBUG'] = True PS: 由于Config对象本质上是字典,所以还可以使用app.config.update(...)
方式二:
app.config.from_pyfile("python文件名称") 如: settings.py DEBUG = True app.config.from_pyfile("settings.py") app.config.from_envvar("环境变量名称") 环境变量的值为python文件名称名称,内部调用from_pyfile方法 app.config.from_json("json文件名称") JSON文件名称,必须是json格式,因为内部会执行json.loads app.config.from_mapping({'DEBUG':True}) 字典格式
app.config.from_object("python类或类的路径") # 主要的应用模式
app.config.from_object('pro_flask.settings.TestingConfig') settings.py class Config(object): DEBUG = False TESTING = False DATABASE_URI = 'sqlite://:memory:' class ProductionConfig(Config): DATABASE_URI = 'mysql://user@localhost/foo' class DevelopmentConfig(Config): DEBUG = True class TestingConfig(Config): TESTING = True PS: 从sys.path中已经存在路径开始写 PS: settings.py文件默认路径要放在程序root_path目录,如果instance_relative_config为True,则就是instance_path目录
那么我们在对Flask进行实例化的时候参数:
static_folder = 'static', # 静态文件目录的路径 默认当前项目中的static目录 static_host = None, # 远程静态文件所用的Host地址,默认为空 static_url_path = None, # 静态文件目录的url路径 默认不写是与static_folder同名,远程静态文件时复用 # host_matching是否开启host主机位匹配,是要与static_host一起使用,如果配置了static_host, 则必须赋值为True # 这里要说明一下,@app.route("/",host="localhost:5000") 就必须要这样写 # host="localhost:5000" 如果主机头不是 localhost:5000 则无法通过当前的路由 host_matching = False, # 如果不是特别需要的话,慎用,否则所有的route 都需要host=""的参数 subdomain_matching = False, # 理论上来说是用来限制SERVER_NAME子域名的,但是目前还没有感觉出来区别在哪里 template_folder = 'templates' # template模板目录, 默认当前项目中的 templates 目录 instance_path = None, # 指向另一个Flask实例的路径 instance_relative_config = False # 是否加载另一个实例的配置 root_path = None # 主模块所在的目录的绝对路径,默认项目目录
而我们常用的参数只有三个:
static_folder = 'static', # 静态文件目录的路径 默认当前项目中的static目录 static_url_path = None, # 静态文件目录的url路径 默认不写是与static_folder同名,远程静态文件时复用 template_folder = 'templates' # template模板目录, 默认当前项目中的 templates 目录
3.路由系统
# 路由系统 函数 视图 from flask import Flask app = Flask(__name__) app.debug = True # 源码实例 router() 装饰器的原理 """ def route(self, rule, **options): def decorator(f): endpoint = options.pop('endpoint', None) self.add_url_rule(rule, endpoint, f, **options) return f return decorator """ @app.route("/index") def sayhello(): return "hello word" def login(): return "login" app.add_url_rule("/", "n2", login, methods=["GET", "POST"] ) if __name__ == '__main__': app.run() # 路由系统支持的 匹配模式 @app.route('/user/<username>') @app.route('/post/<int:post_id>') @app.route('/post/<float:post_id>') @app.route('/post/<path:path>') @app.route('/login', methods=['GET', 'POST']) 常用路由系统有以上五种,所有的路由系统都是基于一下对应关系来处理: DEFAULT_CONVERTERS = { 'default': UnicodeConverter, 'string': UnicodeConverter, 'any': AnyConverter, 'path': PathConverter, 'int': IntegerConverter, 'float': FloatConverter, 'uuid': UUIDConverter, } 3.1 其次我们也可以自己注册自己的规则 from flask import Flask from werkzeug.routing import BaseConverter app = Flask(__name__) # 自定制正则路由匹配 class RegexConverter(BaseConverter): def __init__(self, map, regex): super(RegexConverter, self).__init__(map) self.regex = regex def to_python(self, value): """ 这里面对匹配到的字符串就行加工 :param value: :return: """ return value def to_url(self, value): """ 反向生成url的时候需要用到 :param value: :return: """ val = super(RegexConverter, self).to_python(value) return val app.debug = True app.url_map.converters["regex"] = RegexConverter # 注册自己自己的url路有关系 @app.route("/") def sayhello(): return "hello word" @app.route("/index/<regex('xb\d+'):nid>", methods=["GET"]) def index(nid): print(nid) return "index" if __name__ == '__main__': app.run() @app.route和app.add_url_rule参数: rule, URL规则 view_func, 视图函数名称 defaults=None, 默认值,当URL中无参数,函数需要参数时,使用defaults={'k':'v'}为函数提供参数 endpoint=None, 名称,用于反向生成URL,即: url_for('名称') methods=None, 允许的请求方式,如:["GET","POST"] strict_slashes=None, 对URL最后的 / 符号是否严格要求, 如: @app.route('/index',strict_slashes=False), 访问 http://www.xx.com/index/ 或 http://www.xx.com/index均可 @app.route('/index',strict_slashes=True) 仅访问 http://www.xx.com/index redirect_to=None, 重定向到指定地址 如: @app.route('/index/<int:nid>', redirect_to='/home/<nid>') 或 def func(adapter, nid): return "/home/888" @app.route('/index/<int:nid>', redirect_to=func) subdomain=None, 子域名访问 from flask import Flask, views, url_for app = Flask(import_name=__name__) app.config['SERVER_NAME'] = 'wupeiqi.com:5000' @app.route("/", subdomain="admin") def static_index(): """Flask supports static subdomains This is available at static.your-domain.tld""" return "static.your-domain.tld" @app.route("/dynamic", subdomain="<username>") def username_index(username): """Dynamic subdomains are also supported Try going to user1.your-domain.tld/dynamic""" return username + ".your-domain.tld"
4.CBV 模式的分发规则
# 类视图的路由 from flask import Flask, views app = Flask(__name__) app.debug = True def auth(func): def inner(*args, **kwargs): return func(*args, **kwargs) return inner class IndexView(views.MethodView): methods = ["GET", ] method 的list decorators = [auth, ] # 装饰器的list def get(self): return "index.get" def post(self): return "index.get" # 注册路由规则,加类 name 是 反向的url 名称 app.add_url_rule('/index', view_func=IndexView.as_view(name="index")) if __name__ == '__main__': app.run()
5.模板语言
1、模板的使用
Flask使用的是Jinja2模板,所以其语法和Django无差别。
2、自定义模板方法
Flask中自定义模板方法的方式和Bottle相似,创建一个函数并通过参数的形式传入render_template
from flask import Flask, render_template, Markup, jsonify, make_response app = Flask(__name__) app.debug = True def func(n): return n + "sssss" # 自定义函数 def input(s): text = "<input type='text' value=%s />" % s # 自定义input标签,用makeup来保证安全性 text = Markup(text) return text @app.route("/") def sayhello(): return render_template("s7.html", **{"func": func, "input": input}) if __name__ == '__main__': app.run(port=8001)
注意:Markup等价django的mark_safe
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> <!--支持函数的使用--> <h2>{{func("www")}}</h2> {{input("11")}}{{input("11")|safe}} # 也可以这样 如果后端不做makeup的时候 这样也说明是安全的 <!-- 宏定义 类似函数--> {% macro input(name, type='text', value='') %} <input type="{{ type }}" name="{{ name }}" value="{{ value }}"> {% endmacro %}{{ input('n1') }} </body> </html>
5.1自定义模板语言
@app.template_global() def sb(a1, a2): return a1 + a2 @app.template_filter() def db(a1, a2, a3): return a1 + a2 + a3 使用: {{sb(1,2)}} {{ 1|db(2,3)}}
5.2 cookies的设定
from flask import Flask, render_template, Markup, jsonify, make_response app = Flask(__name__) app.debug = True @app.route("/", methods=["GET"]) def index(): # text = "cookies" # text = make_response(text) # text.set_cookie("XXXX") #设置cookies html = render_template("s7.html",text="1111") response = make_response(html) response.headers["xxx"] = "xxxx" # 设置headers return response if __name__ == '__main__': app.run(port=8001)
6.请求相应相关参数
from flask import Flask from flask import request from flask import render_template from flask import redirect from flask import make_response app = Flask(__name__) @app.route('/login.html', methods=['GET', "POST"]) def login(): # 请求相关信息 # request.method # request.args # request.form # request.values # request.cookies # request.headers # request.path # 去除前面的地址 # request.full_path # 全部地址 # request.script_root # request.url # request.base_url # request.url_root # request.host_url # request.host # request.files # obj = request.files['the_file_name'] # 获取文件obj存储 # obj.save('/var/www/uploads/' + secure_filename(f.filename)) # 响应相关信息 # return "字符串" # return render_template('html模板路径',**{}) # return redirect('/index.html') # 对响应进行包装 # response = make_response(render_template('index.html')) # response是flask.wrappers.Response类型 # response.delete_cookie('key') # response.set_cookie('key', 'value') # response.headers['X-Something'] = 'A value' # return response return "string" if __name__ == '__main__': app.run()
6.1 Session相关
基本使用(lower)
from flask import Flask, render_template, request, redirect, session, url_for app = Flask(__name__) app.debug = True # app.config["SECRET_KEY"] = "1234" app.secret_key = "123" # 开头需要加盐 这样才不会报错 两种形式 USERS = { 1: {'name': '老王', 'age': 18, 'gender': '男', 'text': "当眼泪掉下来的时候,是真的累了, 其实人生就是这样: 你有你的烦,我有我的难,人人都有无声的泪,人人都有难言的苦。 忘不了的昨天,忙不完的今天,想不到的明天,走不完的人生,过不完的坎坷,越不过的无奈,听不完的谎言,看不透的人心放不下的牵挂,经历不完的酸甜苦辣,这就是人生,这就是生活。"}, 2: {'name': '老李', 'age': 28, 'gender': '男', 'text': "高中的时候有一个同学家里穷,每顿饭都是膜膜加点水,有时候吃点咸菜,我们六科老师每天下课都叫他去办公室回答问题背诵课文,然后说太晚啦一起吃个饭,后来他考上了人大,拿到通知书的时候给每个老师磕了一个头"}, 3: {'name': '老鸨', 'age': 18, 'gender': '女', 'text': "高中的时候有一个同学家里穷,每顿饭都是膜膜加点水,有时候吃点咸菜,我们六科老师每天下课都叫他去办公室回答问题背诵课文,然后说太晚啦一起吃个饭,后来他考上了人大,拿到通知书的时候给每个老师磕了一个头"}, }
import functools
# 装饰器的使用,每次来验证session
# 因为有一点需要明确 就是 endpoint 不指定默认 用函数名
# 导致我们加装饰器的时候 每次返回的inners函数的名字不能一样 需要改成 传入的 func函数的名字
def verfriy(func): @functools.wraps(func) # 函数属性转移 def inners(*args, **kwargs): user = session.get("user_info") if not user: url = url_for("login") return redirect(url) return func(*args, **kwargs) # inners.__name__ = func.__name__ # 也可以这样做 return inners @app.route("/", methods=["GET", "POST"], endpoint="login") def login(): if request.method == "GET": return render_template("login.html", ) else: user = request.form.get("user") pwd = request.form.get("pwd") if user == "alex" and pwd == "123": session["user_info"] = user return redirect("/index") else: return render_template("login.html", **{"msg": "erro"}) @app.route("/index", methods=["GET"]) @verfriy def index(): return render_template("index.html", user_dict=USERS) @app.route("/detail/<int:nid>", methods=["GET"]) @verfriy def detail(nid): user_info = USERS.get(nid) return render_template("detail.html", user_info=user_info) if __name__ == '__main__': app.run(port=5001)
后续会添加自定义seesion
7. Flask 中的 蓝图
小型的目录结构:
from flask import Blueprint, render_template # 各个蓝图 view 里面的code account = Blueprint("account", __name__, template_folder="templates",url_prefix="/account") # 可重新 定义templates的文件夹,并且可以设置url的分组前缀(重要) @account.route("/") def index(): return render_template("actest.html")
大型的目录结构:
各个文件夹下的__init__方法的code
# -*- coding:utf-8 -*- from flask import Blueprint admin = Blueprint( 'admin', __name__, template_folder='templates', static_folder='static' ) from . import views # 视图函数
各个文件夹下的view 函数文件
# -*- coding:utf-8 -*- from . import admin @admin.route('/index') def index(): return 'Admin.Index'
8. flash (message) 闪现
message是一个基于Session实现的用于保存数据的集合,其特点是:使用一次就删除。
from flask import Flask, flash, get_flashed_messages, request, redirect app = Flask(__name__) app.debug = True app.secret_key = "1111" @app.route("/") def index(): val = request.get_data("page") if val == 'page': return "page" else: flash("page not get !", category="erro") # 向flash中添加 messgae ,可以指定 category的类型,以便获取 return redirect("/erro") @app.route("/erro") def erro(): eval = get_flashed_messages(category_filter="erro") # 是一个 列表的形式 取category 放进去的值 print(eval) return "错误信息:" + eval[0] if __name__ == '__main__': app.__call__() app.run()
9.中间件
9.1 基于flask 装饰器的中间件
before_request after_request
from flask import Flask, render_template, request, redirect, session, url_for app = Flask(__name__) app.debug = True app.secret_key = "123"
# 请求到来之前做session验证,不需要使用装饰器
# 请求扩展
USERS = { 1: {'name': '老张', 'age': 18, 'gender': '男', 'text': "当眼泪掉下来的时候,是真的累了, 其实人生就是这样: 你有你的烦,我有我的难,人人都有无声的泪,人人都有难言的苦。 忘不了的昨天,忙不完的今天,想不到的明天,走不完的人生,过不完的坎坷,越不过的无奈,听不完的谎言,看不透的人心放不下的牵挂,经历不完的酸甜苦辣,这就是人生,这就是生活。"}, 2: {'name': '狼王', 'age': 28, 'gender': '男', 'text': "高中的时候有一个同学家里穷,每顿饭都是膜膜加点水,有时候吃点咸菜,我们六科老师每天下课都叫他去办公室回答问题背诵课文,然后说太晚啦一起吃个饭,后来他考上了人大,拿到通知书的时候给每个老师磕了一个头"}, 3: {'name': '老鸨', 'age': 18, 'gender': '女', 'text': "高中的时候有一个同学家里穷,每顿饭都是膜膜加点水,有时候吃点咸菜,我们六科老师每天下课都叫他去办公室回答问题背诵课文,然后说太晚啦一起吃个饭,后来他考上了人大,拿到通知书的时候给每个老师磕了一个头"}, }
# 每次请求到来之前 执行before_request 装饰器函数,实现session的验证
@app.before_request def process_request(*args,**kwargs): print(request.path) if request.path == '/': return # 返回none 证明 可以执行 下面的request user = session.get("user_info") print(user) if user: return return redirect("/") # 返回相对应的地址 @app.before_request def process_request1(*args, **kwargs): print("process_request1") @app.before_request def process_request2(*args, **kwargs): print("process_request2") @app.route("/", methods=["GET", "POST"], endpoint="login") def login(): if request.method == "GET": return render_template("login.html", ) else: user = request.form.get("user") pwd = request.form.get("pwd") if user == "alex" and pwd == "123": session["user_info"] = user return redirect("/index") else: return render_template("login.html", **{"msg": "erro"}) @app.route("/index", methods=["GET"]) def index(): return render_template("index.html", user_dict=USERS) @app.route("/detail/<int:nid>", methods=["GET"]) def detail(nid): user_info = USERS.get(nid) return render_template("detail.html", user_info=user_info) @app.after_request def process_response1(response, *args, **kwargs): print("process_response1") return response @app.after_request def process_response2(response, *args, **kwargs): print("process_response2") return response # before_request返回none时 执行下一个request 否则拦截 但是还是会执行response函数 # 执行顺序 request1 -> request2 -> response2 -> response1 @app.errorhandler(404) # 定制错误信息 def erro_404(arg): return "404" if __name__ == '__main__': app.run(port=5002)
9.2中间件的 自定制
from flask import Flask
app = Flask(__name__)
app.debug = True
# 自定义中间件
@app.route("/") def sayhello(): return "hello word" class Md(object): def __init__(self, old_wsgi_app): self.old_wsgi_app = old_wsgi_app def __call__(self, environ, start_response): # 实例化的时候运行call 方法 print("before") ret = self.old_wsgi_app(environ, start_response) print("after") return ret if __name__ == '__main__': # 这一步 是进行实例化把 Flask——>app.wsgi_app 传递进来,接着就执行call方法 其实就是执行app.wsgi_app(参数) app.wsgi_app = Md(app.wsgi_app) # 执行 Md __call__ 方法 app.run(port=9000)
app 的 call 方法源码
def wsgi_app(self, environ, start_response): ctx = self.request_context(environ) # requestCountext ........... def __call__(self, environ, start_response): return self.wsgi_app(environ, start_response)