内容简介:狗书无敌,天下第一(初识flask)
为什么选择使用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基本一致。
<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() 函数则将改动删除。
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持 码农网
猜你喜欢:本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。