Flask是目前最流行的Python Web框架之一
初识Flask
搭建开发环境
安装pip和Pipenv
1 2 3
| $ pip --version //检查pip是否安装 $ pip install pipenv //使用pip安装Pipenv $ pipenv install //为当前项目创建虚拟环境
|
如果pip install过程中下载很慢,则修改Pifile中的源为url = "https://pypi.tuna.tsinghua.edu.cn/simple/"
安装Flask
1 2
| $ pipenv install flask //安装flask $ pipenv update flask //更新flask
|
Hello,Flask!
切换到当前目录
创建程序实例
1 2
| from flask import Flask app = Flask(__name__)
|
注册路由
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| @app.route('/') def index(): return '<h1>Hello, World!</h1>'
@app.route('/hi') @app.route('/hello') def say_hello(): return '<h1>Hello, Flask!</h1>'
@app.route('/greet', defaults={'name': 'Programmer'}) # 设置默认值 @app.route('/greet/<name>') def greet(name): return '<h1>Hello, %s!</h1>' % name
|
启动开发服务器
- 通过命令行
- PyCharm配置
- Target type:Module name(必选)
- Target:(必填)
- Additional options:–host=127.0.0.1 –port=5000(可填)
- FLASK_ENV:development(必填)
- Working directory:<工作目录>(必填)
URL与端点
- 使用
http://127.0.0.1:1234/test/addi
进行访问
- 如果动态路由没有设置默认值
1 2 3 4 5
| @app.route('/test/<additional>') def test(additional): part_url = url_for('test', additional=additional) all_url = url_for('test', additional=additional, _external=True)
|
- 如果动态路由设置了默认值
1 2 3 4 5
| @app.route('/test', defaults={'additional': 'default'}) @app.route('/test/<additional>') def test(additional): part_url = url_for('test') all_url = url_for('test', _external=True)
|
url_for
是用来获取要使用某一方法的最简洁URL
模板
模板基本用法
三种定界符
- 语句
- 表达式
- 注释
语句结束标签
- if-else
1 2 3 4 5
| {% if ... %} //if 语句 {% else ... %} //else 语句 {% endif %}
|
- for
1 2 3
| {% for i in ... %} //for语句 {% endfor %}
|
模板语法
变量名 |
说明 |
loop.index |
当前迭代数(从1开始计数) |
loop.index0 |
当前迭代数(从0开始计数) |
loop.revindex |
当前反向迭代数(从1开始计数) |
loop.revindex0 |
当前反向迭代数(从0开始计数) |
loop.first |
如果是第一个元素,则为True |
loop.last |
如果是最后一个元素,则为True |
loop.previtem |
上一个迭代的条目 |
loop.nextitem |
下一个迭代的条目 |
loop.length |
序列包含的元素数量 |
渲染模板
使用Flask提供的渲染函数render_template()
1 2 3 4
| from flask import Flask, render_template @app.route('/watchlist') def watchlist(): return render_template('watchlist.html', user=user, movies=movies)
|
模板辅助工具
上下文
自定义上下文变量
1 2 3 4 5 6
| {% set navigation = [{'/', 'Home'}, {'/about', 'About'}] %}
{% set navigation %} <li><a href="/">Home</a></li> <li><a href="/about">About</a></li> {% endset %}
|
内置上下文变量
变量 |
说明 |
config |
当前的配置对象 |
request |
当前的请求对象,在已激活的请求环境下可用 |
session |
当前的会话对象,在已激活的请求环境下可用 |
g |
与请求绑定的全局变量,在已激活的请求环境下可用 |
自定义上下文
设置模板全局变量
1 2 3 4 5 6 7 8 9 10 11
| @app.context_processor def inject_foo(): foo = 'I am foo.' return dict(foo=foo)
app.context_processor(inject_foo)
app.context_processor(lambda: dict(foo='I am foo.'))
|
全局对象
- 内置全局函数
函数 |
说明 |
range([start, ]stop[, step]) |
和Python中的range()用法相同 |
lipsum(n = 5, html = True, min = 20, max = 100 |
生成随机文本 |
dict(**item) |
和Python中的dict()用法相同 |
url_for() |
用于生成URL的函数 |
get_flashed_message() |
用于获取flash消息的函数 |
- 自定义全局函数
1 2 3 4 5 6 7
| @app.template_global() def bar(): return 'I am bar.'
@app.add_template_global(bar)
|
过滤器
- 用法
1 2 3 4 5 6 7
| {{ name|title }}
{% filter upper %} This text becomes uppercase. {% endfilter %}
|
- 内置过滤器
过滤器 |
说明 |
default(value, default_value=u”, boolean=False) |
设置默认值,默认值作为参数传入,别名为d |
escape(s) |
转义HTML文本,别名为e |
first(seq) |
返回序列的第一个元素 |
last(seq) |
返回序列的最后一个元素 |
length(object) |
返回变量的长度 |
random(seq) |
返回序列中的随机元素 |
safe(value) |
将变量值标记为安全,避免转义 |
trim(value) |
清除变量值前后的空格 |
max(value, case_sensitive = False, attribute = None |
返回序列中的最大值 |
min(value, case_sensitive = False, attribute = None |
返回序列中的最小值 |
unique(value, case_sensitive = False, attribute = None |
返回序列中的不重复的值 |
striptags(value) |
清除变量值内的HTML标签 |
urlize(value, trim_url_limit = None, nofollow = False, target = None, rel = None |
将URL文本转换成可单击的HTML链接 |
wordcount(s) |
计算单词数量 |
tojson(value, indent = None) |
将变量值转换为JSON格式 |
truncate(s, length = 255, killwords = False, end=’…’, leeway=None |
截断字符串,常用于显示文章摘要,length参数设置截断的长度,killwords参数设置是否截断单词,end参数设置结尾的符号 |
- 自定义过滤器
1 2 3 4 5 6 7 8
| from flask import Markup @app.template_filter() def musical(s): return s + Markup(' ♫')
app.add_template_filter(musical)
|
- 在使用过滤器时,竖线符号左边的值,是过滤器函数的第一个值,其他参数可以通过添加括号传入
测试器
- 用法
1 2 3 4 5
| {% if age is number %} {{ age * 365 }} {% else %} 无效数字 {% endif %}
|
- 内置测试器
测试器 |
说明 |
callable(object) |
判断对象是否可被调用 |
defined(value) |
判断变量是否已定义 |
undefined(value) |
判断变量是否未定义 |
none(value) |
判断变量是否为None |
number(value) |
判断变量是否是数字 |
string(value) |
判断变量是否是字符串 |
sequence(value) |
判断变量是否是序列,比如字符串、列表、元组 |
iterable(value) |
判断变量是否可迭代 |
mapping(value) |
判断变量是否是匹配对象,比如字典 |
sameas(value, other) |
判断变量与other是否指向相同的内存地址 |
- 自定义测试器
1 2 3 4 5 6 7 8 9
| @app.template_test() def baz(n): if n == 'baz': return True return False
app.add_template_test(baz)
|
- 在使用测试器时,is的左侧是测试器函数的第一个参数(value),其他参数可以添加括号传入,也可以在右侧使用空格连接,以sameas为例:
1 2 3
| {% if foo is sameas(bar) %}
{% if foo is sameas bar %}
|
模板环境对象
- 添加自定义全局对象
1
| app.jinja_env.globals['bar'] = bar
|
- 添加自定义过滤器
1
| app.jinja_env.filters['smiling'] = smiling
|
- 添加自定义测试器
1
| app.jinja_env.tests['baz'] = baz
|
模板结构组织
局部模板
- 提示:为了和普通模板区分开,局部模板的命名通常以一个下划线开始
- 用法
1 2
| {% include '_xxx.html' %}
|
宏
- 简单示例
1 2 3 4 5 6 7
| {% macro qux(amount=1) %} {% if amount == 1 -%} I am qux. {%- elif amount > 1 -%} We are quxs. {%- endif %} {% endmacro %}
|
- 用法
1 2 3
| {% from 'macros.html' import qux %}
{{ qux(amount = 5) }}
|
- 默认情况下,包含(include)一个局部模板会传递当前上下文到局部模板中,但是导入(import)不会。如果想要导入(import)也传递当前上下文,则需要显式地使用
with context
。 1
| {% from 'macros.html' import qux with context %}
|
模板继承
- 编写基模板,通常命名为base.html或layout.html
1 2
| {% block head %} {% endblock %}
|
- 编写子模板
- 覆盖内容
1 2 3 4
| {% extend 'base.html' %} {% block head %} { {% endblock %}
|
- 追加内容
1 2 3 4
| {% extend 'base.html' %} {{ super() }} { {% endblock %}
|
模板进阶实践
空白控制
- 渲染时自动去除右边空格
- 渲染时自动去除左边空格
- 利用环境对象
1 2 3 4
| app.jinja_env.trim_blocks = True
app.jinja_env.lstrip_block = True
|
- 需要注意:宏内的空白控制行为不受
trim_blocks
和lstrip_blocks
属性控制,需要手动控制
加载静态文件
- 默认将静态文件存储在与主脚本同级目录的static文件夹中
- 通过
url_for('static', filename='avatar.jpg')
获取到/static/avatar.jpg
- 添加Favicon
- 添加文件
favicon.ico
到static
目录下
- 在
<head>
部分添加一个<link>
元素 1
| <link rel="icon" type="image/x-icon" href="{{ url_for('static', filename='favicon.ico') }}">
|
- 使用宏加载静态资源
1 2 3 4 5 6 7 8 9 10 11 12
| {% macro static_file(type, filename_or_url, local=True) %} {% if local -%} {% set filename_or_url = url_for('static', filename=filename_or_url) %} {%- endif %} {% if type == 'css' -%} <link rel="stylesheet" href="{{ filename_or_url }}" type="text/css"> {%- elif type == 'js' -%} <script type="text/javascript" src="{{ filename_or_url }}"></script> {%- elif type == 'icon' -%} <link rel="icon" href="{{ filename_or_url }}"> {%- endif %} {% endmacro %}
|
1 2 3 4
| static_file('css', '/css/bootstrap.min.css')
static_file('css', 'https://.../css/bootstrap.min.css', local=False)
|
消息闪现
- 使用
flash()
函数闪现
- 使用
get_flashed_messages()
在模板中获取消息 1 2 3
| {% for message in get_flashed_messages() %} <div class="alert">{{ message }}</div> {% endfor %}
|
自定义错误页面
1 2 3 4
| from flask import Flask, render_template @app.errorhandler(404) def page_not_found(e): return render_template('errors/404.html'), 404
|
JavaScript和CSS中的Jinja2
- 定义为JavaScript变量
- 定义
1
| <span data-id="{{ user.id }}" data-username="{{ user.username }}">{{ user.username }}</span>
|
- 使用
1 2 3
| element.dataset.username element.getAttribute('data-username') $element.data('username')
|
- 定义为CSS变量
- 定义
1 2 3 4 5 6
| <style> :root { --theme-color: {{ theme_color }}; --background-url: {{ url_for('static', filename='background.jpg') }} } </style>
|
- 使用
1 2 3 4 5 6 7
| #foo { color: var(--theme-color); } #bar { background: var(--background-url); }
|