1.模板语言jinja2
Flask中默认的模板语言是Jinja2
1.0 模板传参
from flask import Flask,render_template app = Flask(__name__) @app.route("/") def index(): content = { "name":"learning", "age":"18", "sex":"男" } return render_template("index.html",**content) if __name__ == "__main__": app.run(port=5225,debug=True)
index.html
<div> {{ name }} {{ age }} {{ sex }} </div>
效果
1.1 从后端传HTML标签
# 常规做法,前端引入safe
login.html
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> {{ tags|safe }} </body> </html>
app.py
from flask import Flask,render_template app = Flask(__name__) @app.route("/") def index(): tags = "<input type='text' name='user' value='输入'>" return render_template("login.html",tags=tags) if __name__ == "__main__": app.run(port=5225,debug=True)
# 引入Markup,它的作用在HTML的标签上做一层封装,让Jinja2模板语言知道这是一个安全的HTML标签
login.html
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> {{ tags }} </body> </html>
app.py
from flask import Flask,render_template,Markup app = Flask(__name__) @app.route("/") def index(): tags = "<input type='text' name='user' value='输入'>" markup = Markup(tags) return render_template("login.html",tags=markup) if __name__ == "__main__": app.run(port=5225,debug=True)
1.2 模板内执行函数
app.py
from flask import Flask,render_template,Markup app = Flask(__name__) # 定义一个函数 def sums(a,b): return a+b @app.route("/") def index(): return render_template("login.html",tags=sums) if __name__ == "__main__": app.run(port=5225,debug=True)
login.html
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> {{ tags }} <div>{{ tags(38,37) }}</div> </body> </html>
效果:
1.3 flask中的装饰器
from flask import Flask,request,redirect,render_template,session app = Flask(__name__) app.secret_key = "123" # 开启session功能的时候必须添加该配置secret_key # 模拟数据 STUDENT_DICT = { 1: {'name': '吕洋'}, 2: {'name': '黄晓'}, 3: {'name': '余烬'}, } @app.route("/login",methods=["GET","POST"]) def login(): if request.method =="GET": return render_template("login.html") if request.method == "POST": username = request.form.get("username") password = request.form.get("password") if username =="bob" and password == "123": session["user"] = username return redirect("/index") else: return render_template("login.html", msg="用户名密码错误") @app.route("/index") def index(): if session.get("user"): return render_template("index.html",stu=STUDENT_DICT) return redirect("/login") if __name__ == "__main__": app.run(port=5225,debug=True)
index.html
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> <table border="1px"> <tr> <th>id</th> <th>name</th> <th>option</th> </tr> <tr> {% for k,v in stu.items() %} <td>{{ k }}</td> <td>{{ v.name }}</td> <td><a href="/detail?id={{ k }}">详细</a>|<a href="/delete/{{ k }}">删除</a></td> </tr> {% endfor %} </table> </body> </html>
# 用户必须先登录,才能访问index
效果:
说明:
html模板渲染一般都存放在项目主目录下的templates下,否则会出现一个jinja2的异常
开启session功能,这里必须要添加secret_key,如果在实例化的app中没有 secret_key 会抛异常
1.4 特殊装饰器
函数类:
@app.template_global()和@app.template_filter()
和django中的inclusion_tag以及filter很类似
login.html
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> {{ sums(15,15) }} {{ 22|a_b_c_sum(15,15) }} </body> </html>
在模板中执行函数,需要先定义一个函数:
app.py
from flask import Flask,render_template,Markup app = Flask(__name__) @app.template_global() def sums(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("login.html",tags="") # 注意这里也不一样了 if __name__ == "__main__": app.run(port=5225,debug=True)
非函数类:
before_request 和 after_request
@before_request注册一个函数,在每次请求之前执行,返回None则运行通过
@before_first_request,注册一个函数,在处理第一个请求之前执行。它和@before_request一样,唯一区别是它只执行一次
@after_request 注册一个函数,会在用户请求得到response响应之后,还未返回用户之前执行,它可以做统计访问量来使用
@teardown——request 注册一个函数,即使有未处理的异常抛出,也在每次请求之后运行
from flask import Flask,request,redirect,session app = Flask(__name__) app.secret_key = "fsdfs" @app.before_request def is_login(): # 判断是否登录 # 白名单设置,判断为登录页面时 if request.path == "/login": # 跳过处理 return None # 判断session是不存在时 if not session.get("user"): # 重定向到登录页面 return redirect("/login") @app.route("/login") def login(): pass if __name__ == '__main__': app.run("0.0.0.0", 5000)
1.5 模板复用block
和django中的模板使用方式一样
index.html
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> <h1>Welcome to My</h1> <h2>下面的内容是不一样的</h2> {% block content %} {% endblock %} </body> </html>
login.html
{% extends "index.html" %} {% block content %} <h4>欢迎登陆</h4> <form> 用户名:<input type="text" name="user"> 密码:<input type="text" name="pwd"> <input type="submit" value="提交"> </form> {% endblock %}
1.6 模板引用include
index.html
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> <h1>Welcome to My</h1> {% include "login.html" %} </body> </html>
login.html
<form> 用户名:<input type="text" name="user"> 密码:<input type="text" name="pwd"> <input type="submit" value="提交"> </form>
1.7 模板中的过滤器
常用的过滤器
abs # 绝对值 default # 如果当前变量没有值,则会使用参数中的值来替代 escape # 转义字符 first #返回一个序列的第一个元素 format #格式化字符串 last # 返回一个序列的最后一个元素 length # 返回一个序列的长度 join # 拼接字符串 safe # 关掉转义 int # 转为int类型 float # 转为浮点类型 lower # 转换为小写 upper # 转换为大写 replace # 替换 truncate # 截取length长度的字符串 striptags # 删除字符串中所有的html标签,如果出现多个空格,将替换成一个空格
default过滤器的使用
他比较特别,使用必须要加上boolean=True
@app.route("/") def index(): content = { "direction":None } return render_template("index.html",**content)
index.html
<div> {{ direction|default("这是对default的使用说明",boolean=True) }} </div>
效果
2. 路由相关(装饰器中的参数)
2.1 实例
from flask import Flask,request app = Flask(__name__) @app.route("/info", methods=["GET", "POST"]) def student_info(): stu_id = int(request.args["id"]) # 获取前端url中的id值,注意格式类型 return f"如家{stu_id}号房" # Python3.6的新特性 f"{变量名}" if __name__ == "__main__": app.run(port=5225,debug=True)
效果:
2.3 endpoint
反向url地址,默认为视图函数名 (url_for)
from flask import Flask,request,url_for app = Flask(__name__) @app.route("/info", methods=["GET", "POST"],endpoint="r_info") def student_info(): print(url_for("r_info")) # /info stu_id = int(request.args["id"]) # 获取前端url中的id值,注意是格式类型 return f"如家{stu_id}号房" # Python3.6的新特性 f"{变量名}" if __name__ == "__main__": app.run(port=5225,debug=True)
url栏输入http://127.0.0.1:5225/info?id=2,会打印出如上代码
2.3 url_for
用于反向生成url,也可以附带一些参数,比如想要完整的URL,可以设置_external为Ture
from flask import Flask,request,url_for app = Flask(__name__) @app.route("/info", methods=["GET", "POST"],endpoint="r_info") def student_info(): print(url_for("r_info", _external=True)) # http://127.0.0.1:5225/info stu_id = int(request.args["id"]) # 获取前端url中的id值,注意是格式类型 return f"如家{stu_id}号房" # Python3.6的新特性 f"{变量名}" if __name__ == "__main__": app.run(port=5225,debug=True)
# 这样我们获取了完整路径,但是参数还未获取,可以在后面再追加上我们的id, url_for("r_info", _external=True,id=stu_id),这样就能获取完整url
对于url_for,我们还可以通过视图函数解析出url
from flask import Flask,request,url_for app = Flask(__name__) @app.route('/') def hello_world(): return url_for('my_list',page=6) #url_for里面:第一个是视图函数,第二个是url需要的参数 @app.route('/list/<page>/') def my_list(page): return 'my_list' if __name__ == "__main__": app.run(port=5225,debug=True)
效果
url_for里面多余的参数会当做搜索字符
@app.route('/') def hello_world(): return url_for('my_list',page=2,count=2,age=18) @app.route('/list/<page>/') def my_list(page): return 'my_list'
效果
2.4 defaults
视图函数的参数默认值{"nid":100}
2.5 strict_slashes
url地址结尾符"/"的控制
False : 无论结尾 "/" 是否存在均可以访问
True : 表示开启路由严格匹配模式,结尾必须不能是 "/"
from flask import Flask app = Flask(__name__) @app.route("/info",strict_slashes=True) def student_info(): return "如家" if __name__ == "__main__": app.run(port=5225,debug=True)
# 为True,路由末尾不能再加反斜杠,不然报错
# 为False,路由末尾对反斜杠不做严格要求
2.6 redirect_to
url地址重定向
from flask import Flask app = Flask(__name__) @app.route("/info",redirect_to="/bbb") def student_info(): return "如家" @app.route("/bbb") def bbb(): return "去你的"
# 输入http://127.0.0.1:5225/info,它会自动发生跳转,到http://127.0.0.1:5225/bbb
3. 动态参数路由
from flask import Flask,url_for app = Flask(__name__) @app.route("/info/<int:nid>",endpoint="r_info") def student_info(nid): print(url_for("r_info", _external=True, nid=nid)) return f"如家{nid}" if __name__ == "__main__": app.run(port=5230,debug=True)
效果:
3.1常见 @app.route() 装饰器中的动态参数
@app.route('/user/<username>') # 不加参数的时候默认是字符串形式的 @app.route('/post/<int:post_id>') # 指定int,说明是整型的 @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, 和string类似,但是接收斜杠 'int': IntegerConverter, 'float': FloatConverter, 'uuid': UUIDConverter, }
对于any:
@app.route("/<any(blog,user):url_path>/<id>/") def index(url_path,id): if url_path == 'blog': return '博客详情%s' % id else: return '用户详情%s' % id
效果
对于path
@app.route('/article/<path:test>/') def test_article(test): return 'test_article:{}'.format(test)
效果:
3.2 路由正则
具体代码演示
from flask import Flask from werkzeug.routing import BaseConverter # 自定义正则转换器 class RegexConverter(BaseConverter): def __init__(self, url_map, *args): super(RegexConverter, self).__init__(url_map) # 将接受的第1个参数当作匹配规则进行保存 self.regex = args[0] app = Flask(__name__) # 将自定义转换器添加到转换器字典中,并指定转换器使用时名字为: re app.url_map.converters['re'] = RegexConverter @app.route('/user/<re("[0-9]{3}"):user_id>') def user_info(user_id): return "user_id 为 %s" % user_id if __name__ == "__main__": app.run(debug=True)
效果
也可以参考祥哥博客
4.实例化Flask参数
4.1 flask配置
开启debug模式
from flask import Flask app = Flask(__name__) # type:Flask app.config["DEBUG"] = True if __name__ == '__main__': app.run()
config中的所有key值
{ '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, }
4.2 对于配置的修改
我们可以创建一个setting文件
class FlaskSetting(object): DEBUG = True
需要使用,直接导入就可以
4.3 实例化配置
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 # 主模块所在的目录的绝对路径,默认项目目录
4.4 template_folder
如果设置template_folder = 'templates',这里面的templates它是相对路径!
我们在使用该模板时,应该这么设置 template_folder = '../templates'
结构图:
./
├── bin
│ └── app.py
├── static
│ └── learning.jpg
└── templates
└── login.html
案例:
from flask import Flask,render_template from flask_login import login_required # 第三方包,需要下载 app = Flask(__name__,template_folder="../templates") # 有时候会找不到,需加上template_folder @app.route("/") def login(): return render_template("login.html") @app.route("/log") @login_required # 不能直接访问该路由 def index(): return render_template("index.html") if __name__ == "__main__": app.run(port=5230,debug=True)
4.5 static_folder
静态文件目录的路径 默认当前项目中的static目录
结构图
./
├── bin
│ └── app.py
├── static
│ └── learning.jpg
└── templates
└── login.html
app.py
from flask import Flask,render_template app = Flask(__name__,template_folder="../templates",static_folder="../static") @app.route("/") def index(): return render_template("login.html") if __name__ == "__main__": app.run(port=5230,debug=True)
login.html
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> <h3>头像</h3> <img src="/static/learning.jpg" alt=""> </body> </html>
效果
4.6 static_url_path
from flask import Flask,render_template app = Flask(__name__,template_folder="../templates",static_folder="../static",static_url_path="/app") @app.route("/") def index(): print(app.static_folder) # C:UsersLearningDesktopffflaskin../static print(app.static_url_path) # /app return render_template("login.html") if __name__ == "__main__": app.run(port=5230,debug=True)
5. 内置session
上面案例已经使用过session,这里只强调一点,使用session必须配制secret_key,它是一段秘钥字符串,自己随意填写
from flask import session app = Flask(__name__) app.secret_key = "ask"
一般session我们可以做登录验证使用