为什么选择使用flask?
和其他框架相比, Flask 之所以能脱颖而出,原因在于它让开发者做主,使其能对程序具有全面的创意控制。
在 Flask 中,你可以自主选择程序的组件,如果找不到合适的,还可以自己开发。
Flask 提供了一个强健的核心, 其中包含每个 Web 程序都需要的基本功能,而其他功能则交给行业系统中的众多第三方扩展。
一句话概括就是flask不是一个高度定制化的web框架,你可以做到随心所欲,使用任何可能的扩展来完成你的项目。
狗书的代码已上传GitHub:Companion code to my O‘Reilly book "Flask Web Development。
Flask 有两个主要依赖:路由、调试和 Web 服务器网关接口(Web Server Gateway Interface,WSGI)子系统由 Werkzeug(http://werkzeug.pocoo.org/)提供;模板系统由 Jinja2(http://jinja.pocoo.org/)提供。 Werkzeug 和 Jinjia2 都是由 Flask 的核心开发者开发而成。
Flask 并不原生支持数据库访问、 Web 表单验证和用户认证等高级功能。这些功能以及其他大多数 Web 程序中需要的核心服务都以扩展的形式实现, 然后再与核心包集成。
安装
pip install flask
初始化
所有 Flask 程序都必须创建一个程序实例。 Web 服务器使用一种名为 Web 服务器网关接口(Web Server Gateway Interface, WSGI)的协议,把接收自客户端的所有请求都转交给这个对象处理。
from flask import Flask app = Flask(__name__)
也就是说,此时web框架接收的请求都会通过flask实例化的对象进行处理。
这里的初始化方式是最简单的初始化方式,后面会使用到更为复杂的初始化方式。
路由和视图函数
程序实例需要知道对每个 URL 请求运行哪些代码,所以保存了一个 URL 到Python 函数的映射关系。处理 URL 和函数之间关系的程序称为路由。
在 Flask 程序中定义路由的最简便方式,是使用程序实例提供的 app.route 修饰器,把修饰的函数注册为路由。
@app.route(‘/‘) def index(): return ‘<h1>Hello World!</h1>‘
这个函数的返回值称为响应,是客户端接收到的内容。如果客户端是 Web 浏览器, 响应就是显示给用户查看的文档(一般就是html页面)。
程序实例用 run 方法启动 Flask 集成的开发 Web 服务器:
if __name__ == ‘__main__‘: app.run(debug=True)
有一些选项参数可被 app.run() 函数接受用于设置 Web 服务器的操作模式。在开发过程中启用调试模式会带来一些便利, 比如说激活调试器和重载程序。要想启用调试模式, 我们可以把 debug 参数设为 True。
第一个程序:
from flask import Flask app = Flask(__name__) @app.route(‘/‘) def index(): return ‘<h1>Hello World!</h1>‘ @app.route(‘/user/<name>‘) def user(name): return ‘<h1>Hello, {}!</h1>‘.format(name) if __name__ == ‘__main__‘: app.run()
为了避免大量可有可无的参数把视图函数弄得一团糟, Flask 使用上下文临时把某些对象变为全局可访问。有了上下文,就可以写出下面的视图函数:
from flask import request @app.route(‘/‘) def index(): user_agent = request.headers.get(‘User-Agent‘) return ‘<p>Your browser is %s</p>‘ % user_agent
这里我们把request当作全局变量使用,实际生产中每个线程都处理不同的请求,那么他们的request必然是不同的。Falsk 使用上下文让特定的变量在一个线程中全局可访问,与此同时却不会干扰其他线程。
flask上下文全局变量
没激活程序上下文之前就调用 current_app.name 会导致错误,但推送完上下文之后就可以调用了。 注意,在程序实例上调用 app.app_context() 可获得一个程序上下文。
from hello import app from flask import current_app app_ctx = app.app_context() app_ctx.push() current_app.name ‘hello‘
URL 映射是 URL 和视图函数之间的对应关系。Flask 使用 app.route 修饰器或者非修饰器形式的 pp.add_url_rule() 生成映射。
app.url_map Map([<Rule ‘/‘ (GET, HEAD, OPTIONS) -> index>, <Rule ‘/static/<filename>‘ (GET, HEAD, OPTIONS) -> static>, <Rule ‘/user/<name>‘ (GET, HEAD, OPTIONS) -> user>])
/ 和 /user/<name> 路由在程序中使用 app.route 修饰器定义。 /static/<filename> 路由是Flask 添加的特殊路由,用于访问静态文件。
请求钩子
Flask 支持以下 4 种钩子。
before_first_request:注册一个函数,在处理第一个请求之前运行。
before_request:注册一个函数,在每次请求之前运行。
after_request:注册一个函数,如果没有未处理的异常抛出,在每次请求之后运行。
teardown_request:注册一个函数,即使有未处理的异常抛出,也在每次请求之后运行。
注:在请求钩子函数和视图函数之间共享数据一般使用上下文全局变量 g。例如, before_request 处理程序可以从数据库中加载已登录用户,并将其保存到 g.user 中。随后调用视
图函数时,视图函数再使用 g.user 获取用户。
响应
flask的响应包括模板(本质是字符串)和状态码。
@app.route(‘/‘) def index(): return ‘<h1>Bad Request</h1>‘, 400
如果不想返回由 1 个、 2 个或 3 个值组成的元组, Flask 视图函数还可以返回 Response 对象。 make_response() 函数可接受 1 个、 2 个或 3 个参数(和视图函数的返回值一样),并
返回一个 Response 对象。
from flask import make_response @app.route(‘/‘) def index(): response = make_response(‘<h1>This document carries a cookie!</h1>‘) response.set_cookie(‘answer‘, ‘42‘)#创建对象,给对象加cookie return response
flask重定向
from flask import redirect @app.route(‘/‘) def index(): return redirect(‘http://www.example.com‘)
abort生成响应404,abort 不会把控制权交还给调用它的函数,而是抛出异常把控制权交给 Web 服务器。
from flask import abort @app.route(‘/user/<id>‘) def get_user(id): user = load_user(id) if not user: abort(404) return ‘<h1>Hello, %s</h1>‘ % user.name
使用Flask-Script支持命令行选项
Flask 的开发 Web 服务器支持很多启动设置选项,但只能在脚本中作为参数传给 app.run()函数。这种方式并不十分方便,传递设置选项的理想方式是使用命令行参数。
Flask-Script 是一个 Flask 扩展,为 Flask 程序添加了一个命令行解析器。 Flask-Script 自带了一组常用选项,而且还支持自定义命令。
pip install flask-script
from flask.ext.script import Manager manager = Manager(app) if __name__ == ‘__main__‘: manager.run()
专为 Flask 开发的扩展都暴漏在 flask.ext 命名空间下。 Flask-Script 输出了一个名为Manager 的类,可从 flask.ext.script 中引入。
这个扩展的初始化方法也适用于其他很多扩展: 把程序实例作为参数传给构造函数,初始化主类的实例。 创建的对象可以在各个扩展中使用。在这里,服务器由 manager.run() 启
动,启动后就能解析命令行了。
from flask import Flask from flask_script import Manager app = Flask(__name__) manager = Manager(app) @app.route(‘/‘) def index(): return ‘<h1>Hello World!</h1>‘ @app.route(‘/user/<name>‘) def user(name): return ‘<h1>Hello, %s!</h1>‘ % name if __name__ == ‘__main__‘: manager.run()
Jinja2模板引擎
Jinja2模板引擎是flask默认的模板引擎。
Flask 提供的 render_template 函数把 Jinja2 模板引擎集成到了程序中 。使用方式与django的render基本一致。
Jinja2 能识别所有类型的变量, 甚至是一些复杂的类型,例如列表、字典和对象。
<p>A value from a dictionary: {{ mydict[‘key‘] }}.</p> <p>A value from a list: {{ mylist[3] }}.</p> <p>A value from a list, with a variable index: {{ mylist[myintvar] }}.</p> <p>A value from an object‘s method: {{ myobj.somemethod() }}.</p>
jinja2很大程度上和django的模板语言很类似,包括过滤器的使用,判断,循环,模板继承等。
来说点特别的,jinja2支持宏(类似于函数,使用也和函数很像)。
定义及使用宏:
{% macro render_comment(comment) %} <li>{{ comment }}</li> {% endmacro %} <ul> {% for comment in comments %} {{ render_comment(comment) }} {% endfor %} </ul>
重复使用宏需要将其保存在单独的文件中,然后在需要使用的模板中导入:
{% import ‘macros.html‘ as macros %} <ul> {% for comment in comments %} {{ macros.render_comment(comment) }} {% endfor %} </ul>
要想在flask程序中集成 Bootstrap, 显然要对模板做所有必要的改动。不过,更简单的方法是使用一个名为 Flask-Bootstrap 的 Flask 扩展(这也是我喜欢flask的原因之一吧,扩展性强,插件还很多),简化集成的过程。
pip install flask-bootstrap
初始化 Flask-Bootstrap
from flask.ext.bootstrap import Bootstrap bootstrap = Bootstrap(app)
初始化 Flask-Bootstrap 之后,就可以在程序中使用一个包含所有 Bootstrap 文件的基模板。
{% extends "bootstrap/base.html" %} {% block title %}Flasky{% endblock %}
{% extends "bootstrap/base.html" %} {% block title %}Flasky{% endblock %} {% block navbar %} <div class="navbar navbar-inverse" role="navigation"> <div class="container"> <div class="navbar-header"> <button type="button" class="navbar-toggle" data-toggle="collapse" data-target=".navbar-collapse"> <span class="sr-only">Toggle navigation</span> <span class="icon-bar"></span> <span class="icon-bar"></span> <span class="icon-bar"></span> </button> <a class="navbar-brand" href="/">Flasky</a> </div> <div class="navbar-collapse collapse"> <ul class="nav navbar-nav"> <li><a href="/">Home</a></li> </ul> </div> </div> </div> {% endblock %} {% block content %} <div class="container"> <div class="page-header"> <h1>Hello, {{ name }}!</h1> </div> </div> {% endblock %}
自定制错误页面
@app.errorhandler(404) def page_not_found(e): return render_template(‘404.html‘), 404 @app.errorhandler(500) def internal_server_error(e): return render_template(‘500.html‘), 500
url_for() 函数最简单的用法是以视图函数名(或者 app.add_url_route() 定义路由时使用的端点名)作为参数, 返回对应的 URL。例如,在当前版本的 hello.py 程序中调用 url_for(‘index‘) 得到的结果是 /。调用 url_for(‘index‘, _external=True) 返回的则是绝对地址,在这个示例中是 http://localhost:5000/。
使用 url_for() 生成动态地址时,将动态部分作为关键字参数传入。例如, url_for(‘user‘, name=‘john‘, _external=True) 的返回结果是 http://localhost:5000/user/john。
如果 Web 程序的用户来自世界各地,那么处理日期和时间可不是一个简单的任务。
有一个使用 JavaScript 开发的优秀客户端开源代码库,名为 moment.js(http://momentjs.com/),它可以在浏览器中渲染日期和时间。 Flask-Moment 是一个 Flask 程序扩展,能把moment.js 集成到 Jinja2 模板中。
pip install flask-moment
此模块依赖于moment.js 和jquery.js
from datetime import datetime @app.route(‘/‘) def index(): return render_template(‘index.html‘,current_time=datetime.utcnow())
渲染当前时间:
{% block scripts %} {{ super() }} {{ moment.include_moment() }} {% endblock %} <p>The local date and time is {{ moment(current_time).format(‘LLL‘) }}.</p> <p>That was {{ moment(current_time).fromNow(refresh=True) }}</p>
flask表单处理
pip install flask-wtf
为了实现 CSRF 保护, Flask-WTF 需要程序设置一个密钥。 Flask-WTF 使用这个密钥生成加密令牌,再用令牌验证请求中表单数据的真伪。设置密钥的方法
app = Flask(__name__) app.config[‘SECRET_KEY‘] = ‘hard to guess string‘
定义表单类
from flask.ext.wtf import Form from wtforms import StringField, SubmitField from wtforms.validators import Required class NameForm(Form): name = StringField(‘What is your name?‘, validators=[Required()]) submit = SubmitField(‘Submit‘)
这点flask-wtf要比django的form强大,类似于model form的功能。
使用 Flask-Bootstrap方式渲染
{% import "bootstrap/wtf.html" as wtf %} {{ wtf.quick_form(form) }}
wtf.quick_form() 函数的参数为 Flask-WTF 表单对象,使用 Bootstrap 的默认样式渲染传入的表单。
用户提交表单后, 服务器收到一个包含数据的 POST 请求。 validate_on_submit() 会调用name 字段上附属的 Required() 验证函数。如果名字不为空,就能通过验证, validate_on_submit() 返回 True。现在,用户输入的名字可通过字段的 data 属性获取。
Flask-SQLAlchemy 是一个 Flask 扩展,简化了在 Flask 程序中使用 SQLAlchemy 的操作。SQLAlchemy 是一个很强大的关系型数据库框架, 支持多种数据库后台。 SQLAlchemy 提供了高层 ORM,也提供了使用数据库原生 SQL 的低层功能。
pip install flask-sqlalchemy
配置数据库
from flask.ext.sqlalchemy import SQLAlchemy basedir = os.path.abspath(os.path.dirname(__file__)) app = Flask(__name__) app.config[‘SQLALCHEMY_DATABASE_URI‘] =‘sqlite:///‘ + os.path.join(basedir, ‘data.sqlite‘) app.config[‘SQLALCHEMY_COMMIT_ON_TEARDOWN‘] = True db = SQLAlchemy(app)
db 对象是 SQLAlchemy 类的实例,表示程序使用的数据库,同时还获得了 Flask-SQLAlchemy提供的所有功能。
定义数据库模型
class Role(db.Model): __tablename__ = ‘roles‘ id = db.Column(db.Integer, primary_key=True) name = db.Column(db.String(64), unique=True) def __repr__(self): return ‘<Role %r>‘ % self.name
创建数据库
(venv) $ python hello.py shell >>> from hello import db >>> db.create_all()
删除数据库
db.drop_all()
增加行数据
>>> from hello import Role, User >>> admin_role = Role(name=‘Admin‘) >>> mod_role = Role(name=‘Moderator‘) >>> user_role = Role(name=‘User‘) >>> user_john = User(username=‘john‘, role=admin_role) >>> user_susan = User(username=‘susan‘, role=user_role) >>> user_david = User(username=‘david‘, role=user_role)
此时,我们的对象还只是自己意淫出来的,并没有往数据库中提交,我们需要通过数据库会话管理对数据库做改动 。
第一步,将要提交对象加入会话中:
>>> db.session.add(admin_role) >>> db.session.add(mod_role) >>> db.session.add(user_role) >>> db.session.add(user_john) >>> db.session.add(user_susan) >>> db.session.add(user_david)
或者:
db.session.add_all([admin_role, mod_role, user_role,user_john, user_susan, user_david])
为了把对象写入数据库,我们要调用 commit() 方法提交会话:
db.session.commit()
此时,我们的对象已经真正的存在数据库中,可以查询到了。
数据库会话能保证数据库的一致性。提交操作使用原子方式把会话中的对象全部写入数据库。如果在写入会话的过程中发生了错误, 整个会话都会失效。
修改数据行
>>> admin_role.name = ‘Administrator‘ >>> db.session.add(admin_role) >>> db.session.commit()
删除数据库
>>> db.session.delete(mod_role) >>> db.session.commit()
查询行数据
Flask-SQLAlchemy 为每个模型类都提供了 query 对象。最基本的模型查询是取回对应表中的所有记录:
>>> Role.query.all() [<Role u‘Administrator‘>, <Role u‘User‘>] >>> User.query.all() [<User u‘john‘>, <User u‘susan‘>, <User u‘david‘>]
过滤查询
>>> User.query.filter_by(role=user_role).all() [<User u‘susan‘>, <User u‘david‘>]
如果你想看他在底层到底执行的sql语句是什么。
>>> str(User.query.filter_by(role=user_role)) ‘SELECT users.id AS users_id, users.username AS users_username, users.role_id AS users_role_id FROM users WHERE :param_1 = users.role_id‘
为了避免一直重复导入,我们可以做些配置,让 Flask-Script 的 shell 命令自动导入特定的对象。
from flask.ext.script import Shell def make_shell_context(): return dict(app=app, db=db, User=User, Role=Role) manager.add_command("shell", Shell(make_context=make_shell_context))
使用Flask-Migrate实现数据库迁移
配置 Flask-Migrate
from flask.ext.migrate import Migrate, MigrateCommand migrate = Migrate(app, db) manager.add_command(‘db‘, MigrateCommand)
在维护数据库迁移之前,要使用 init 子命令创建迁移仓库:
python hello.py db init
upgrade() 函数把迁移中的改动应用到数据库中, downgrade() 函数则将改动删除。
原文地址:https://www.cnblogs.com/Jeffding/p/8313263.html