狗书无敌,天下第一(初识flask)

栏目: Python · 发布时间: 6年前

内容简介:狗书无敌,天下第一(初识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)

为了避免大量可有可无的参数把视图函数弄得一团糟, 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上下文全局变量

狗书无敌,天下第一(初识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 %}

狗书无敌,天下第一(初识flask)

{% 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)

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()

此时,我们的对象已经真正的存在数据库中,可以查询到了。

狗书无敌,天下第一(初识flask)

数据库会话能保证数据库的一致性。提交操作使用原子方式把会话中的对象全部写入数据库。如果在写入会话的过程中发生了错误, 整个会话都会失效。

修改数据行

>>> 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() 函数则将改动删除。 


以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持 码农网

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

程序员的呐喊

程序员的呐喊

[美]Steve Yegge / 徐旭铭 / 人民邮电出版社 / 2014-5-1 / 45.00元

《程序员的呐喊》的作者是业界知名的程序员—来自google的steve yegge,他写过很多颇富争议的文章,其中有不少就收录在这本书中。本书是他的精彩文章的合集。 《程序员的呐喊》涉及编程语言文化、代码方法学、google公司文化等热点话题。 对工厂业界的各种现象、技术、趋势等,作者都在本书中表达了自己独特犀利的观点。比如java真的是一门优秀的面向对象语言吗?重构真的那么美好吗?强......一起来看看 《程序员的呐喊》 这本书的介绍吧!

图片转BASE64编码
图片转BASE64编码

在线图片转Base64编码工具

MD5 加密
MD5 加密

MD5 加密工具

SHA 加密
SHA 加密

SHA 加密工具