1、模板
模板渲染
Flask 配备了 Jinja2 模板引擎,通过使用 render_template() 方法来渲染模板,render_template函数把Jinja2模板引擎集成到了程序中。
render_template函数的第一个参数是模板的文件名。随后的参数都是键值对,表示模板中变量对应的真实值。
from flask import render_template
@app.route('/hello/')
@app.route('/hello/<name>')
def hello(name=None):
return render_template('hello.html', name=name)
hello.html
<!doctype html>
<title>Hello from Flask</title>
{% if name %}
<h1>Hello {{ name }}!</h1>
{% else %}
<h1>Hello World!</h1>
{% endif %}
模板继承
模板继承允许你创建一个基础的骨架模板, 这个模板包含您网站的通用元素,并且定义子模板可以重载的 blocks 。
基础模板
在这个叫做 layout.html 的模板中定义了一个简单的 HTML 文档骨架,你可以将这个骨架用作一个简单的双栏页面。而子模板负责填充空白的 block,在这个例子中,使用 {% block %} 标签定义了四个子模板可以重载的块。 block 标签所做的的所有事情就是告诉模板引擎: 一个子模板可能会重写父模板的这个部分。
layout.html
<!doctype html>
<html>
<head>
{% block head %}
<link rel="stylesheet" href="{{ url_for('static', filename='style.css') }}">
<title>{% block title %}{% endblock %} - My Webpage</title>
{% endblock %}
</head>
<body>
<div id="content">{% block content %}{% endblock %}</div>
<div id="footer">
{% block footer %}
© Copyright 2010 by <a href="http://domain.invalid/">you</a>.
{% endblock %}
</div>
</body>
子模板
{% extends %} 是这个例子的关键,它会告诉模板引擎这个模板继承自另一个模板的, 模板引擎分析这个模板时首先会定位其父父模板。extends 标签必须是模板的首个标签。 想要渲染父模板中的模板需要使用 {{ super() }}。
{% extends "layout.html" %}
{% block title %}Index{% endblock %}
{% block head %}
{{ super() }}
<style type="text/css">
.important { color: #336699; }
</style>
{% endblock %}
{% block content %}
<h1>Index</h1>
<p class="important">
Welcome on my awesome homepage.
{% endblock %}
2、Flask-Bootstrap
Flask-Bootstrap 把 Bootstrap 打包进一个 扩展,这个扩展主要由一个叫“bootstrap”的蓝本(blueprint)组成。它也可以创建链接从一个CDN上引用Bootstrap资源。
初始化Flask-Bootstrap
from flask.ext.bootstrap import Bootstrap
# ...
bootstrap = Bootstrap(app)
3、Flask-SQLAlchemy
Flask-SQLAlchemy是一个Flask扩展,简化了在Flask程序中使用SQLAlchemy的操作。SQLAlchemy是一个很强大的关系型数据库框架,支持多种数据库后台。SQLAlchemy提供了高层ORM,也提供了使用数据库原生SQL的低层功能。
安装
pip install flask-sqlalchemy
在Flask-SQLAlchemy中,数据库使用URL指定。最流行的数据库引擎采用的数据库URL格式如下
hostname表示MySQL服务所在的主机,可以是本地主机(localhost),也可以是远程服务器。数据库服务器上可以托管多个数据库,因此database表示要使用的数据库名。如果数据库需要进行认证,username和password表示数据库用户密令。
配置数据库
from flask.ext.sqlalchemy import SQL
Alchemybasedir = 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提供的所有功能。
定义Role和User模型
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
class User(db.Model):
__tablename__ = 'users'
id = db.Column(db.Integer, primary_key=True)
username = db.Column(db.String(64), unique=True, index=True)
def __repr__(self):
return '<User %r>' % self.username
类变量__tablename__定义在数据库中使用的表名。,其余的类变量都是该模型的属性,被定义为db.Column类的实例,db.Column类构造函数的第一个参数是数据库列和模型属性的类型。
关系
db.relationship()的第一个参数表明这个关系的另一端是哪个模型。
class Role(db.Model):
# ...
users = db.relationship('User', backref='role')
class User(db.Model):
# ...
role_id = db.Column(db.Integer, db.ForeignKey('roles.id'))
db.relationship()中的backref参数向User模型中添加一个role属性,从而定义反向关系。这一属性可替代role_id访问Role模型,此时获取的是模型对象,而不是外键的值。
创建表
首先,我们要让Flask-SQLAlchemy根据模型类创建数据库。方法是使用db.create_all()函数:
(venv) $ python hello.py shell
>>> from hello import db
>>> db.create_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)
通过数据库会话管理对数据库所做的改动,在Flask-SQLAlchemy中,会话由db.session表示。准备把对象写入数据库之前,先要将其添加到会话中:
>>> 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'>]
使用过滤器可以配置query对象进行更精确的数据库查询。下面这个例子查找角色为"User"的所有用户:
>>> User.query.filter_by(role=user_role).all()[<User u'susan'>, <User u'david'>]
若要查看SQLAlchemy为查询生成的原生SQL查询语句,只需把query对象转换成字符串:
>>> 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'
可在query对象上调用的常用过滤器
在查询上应用指定的过滤器后,通过调用all()执行查询,以列表的形式返回结果。除了all()之外,还有其他方法能触发查询执行。
4、Flask-Mail
安装
(venv) $ pip install flask-mail
使用Google Gmail账户发送电子邮件。
import os
# ...
app.config['MAIL_SERVER'] = 'smtp.googlemail.com'
app.config['MAIL_PORT'] = 587
app.config['MAIL_USE_TLS'] = True
app.config['MAIL_USERNAME'] = os.environ.get('MAIL_USERNAME')
app.config['MAIL_PASSWORD'] = os.environ.get('MAIL_PASSWORD')
初始化Flask-Mail
from flask.ext.mail import Mail
mail = Mail(app)
在Python shell中发送电子邮件
(venv) $ python hello.py shell
>>> from flask.ext.mail import Message
>>> from hello import mail
>>> msg = Message('test subject', sender='you@example.com',
... recipients=['you@example.com'])
>>> msg.body = 'text body'
>>> msg.html = '<b>HTML</b> body'
>>> with app.app_context():
... mail.send(msg)...
在程序中集成发送电子邮件功能
from flask.ext.mail import Message
app.config['FLASKY_MAIL_SUBJECT_PREFIX'] = '[Flasky]'
app.config['FLASKY_MAIL_SENDER'] = 'Flasky Admin <flasky@example.com>'
def send_email(to, subject, template, **kwargs):
msg = Message(app.config['FLASKY_MAIL_SUBJECT_PREFIX'] + subject,sender=app.config['FLASKY_MAIL_SENDER'], recipients=[to])
msg.body = render_template(template + '.txt', **kwargs)
msg.html = render_template(template + '.html', **kwargs)
mail.send(msg)
这个函数用到了两个程序特定配置项,分别定义邮件主题的前缀和发件人的地址。send_email函数的参数分别为收件人地址、主题、渲染邮件正文的模板和关键字参数列表。指定模板时不能包含扩展名,这样才能使用两个模板分别渲染纯文本正文和富文本正文。调用者将关键字参数传给render_template()函数,以便在模板中使用,进而生成电子邮件正文。
5、大型程序的结构
配置选项
config.py:程序的配置
import os
basedir = os.path.abspath(os.path.dirname(__file__))
class Config:
SECRET_KEY = os.environ.get('SECRET_KEY') or 'hard to guess string'
SQLALCHEMY_COMMIT_ON_TEARDOWN = True
FLASKY_MAIL_SUBJECT_PREFIX = '[Flasky]'
FLASKY_MAIL_SENDER = 'Flasky Admin <flasky@example.com>'
FLASKY_ADMIN = os.environ.get('FLASKY_ADMIN')
@staticmethod
def init_app(app):
pass
class DevelopmentConfig(Config):
DEBUG = True
MAIL_SERVER = 'smtp.googlemail.com'
MAIL_PORT = 587
MAIL_USE_TLS = True
MAIL_USERNAME = os.environ.get('MAIL_USERNAME')
MAIL_PASSWORD = os.environ.get('MAIL_PASSWORD')
SQLALCHEMY_DATABASE_URI = os.environ.get('DEV_DATABASE_URL') or \ 'sqlite:///' + os.path.join(basedir, 'data-dev.sqlite')
class TestingConfig(Config):
TESTING = True
SQLALCHEMY_DATABASE_URI = os.environ.get('TEST_DATABASE_URL') or \ 'sqlite:///' + os.path.join(basedir, 'data-test.sqlite')
class ProductionConfig(Config):
SQLALCHEMY_DATABASE_URI = os.environ.get('DATABASE_URL') or \ 'sqlite:///' + os.path.join(basedir, 'data.sqlite')
config = {
'development': DevelopmentConfig,
'testing': TestingConfig,
'production': ProductionConfig,
'default': DevelopmentConfig}
基类Config中包含通用配置,子类分别定义专用的配置。
配置类可以定义init_app()类方法,其参数是程序实例。在这个方法中,可以执行对当前环境的配置初始化。现在,基类Config中的init_app()方法为空。
在这个配置脚本末尾,config字典中注册了不同的配置环境,而且还注册了一个默认配置(本例的开发环境)。
程序包
程序包用来保存程序的所有代码、模板和静态文件。我们可以把这个包直接称为app(应用),如果有需求,也可使用一个程序专用名字。templates和static文件夹是程序包的一部分,因此这两个文件夹被移到了app中。数据库模型和电子邮件支持函数也被移到了这个包中,分别保存为app/models.py和app/email.py。
使用程序工厂函数
程序的工厂函数在app包的构造文件中定义。
create_app()函数就是程序的工厂函数,接受一个参数,是程序使用的配置名。
配置类在config.py文件中定义,其中保存的配置可以使用Flaskapp.config配置对象提供的from_object()方法直接导入程序。
至于配置对象,则可以通过名字从config字典中选择。程序创建并配置好后,就能初始化扩展了。在之前创建的扩展对象上调用init_app()可以完成初始化过程。
app/__init__.py:程序包的构造文件
from flask import Flask, render_template
from flask.ext.bootstrap import Bootstrap
from flask.ext.mail import Mail
from flask.ext.moment import Moment
from flask.ext.sqlalchemy import SQLAlchemy
from config import config
bootstrap = Bootstrap()
mail = Mail()
moment = Moment()
db = SQLAlchemy()
def create_app(config_name):
app = Flask(__name__)
app.config.from_object(config[config_name])
config[config_name].init_app(app)
bootstrap.init_app(app)
mail.init_app(app)
moment.init_app(app)
db.init_app(app) # 附加路由和自定义的错误页面
return app
蓝本
app/main/__init__.py:创建蓝本
from flask import Blueprint
main = Blueprint('main', __name__)
from . import views, errors
通过实例化一个Blueprint类对象可以创建蓝本。这个构造函数有两个必须指定的参数:蓝本的名字和蓝本所在的包或模块。和程序一样,大多数情况下第二个参数使用Python的__name__变量即可。
蓝本在工厂函数create_app()中注册到程序上
app/_init_.py:注册蓝本
def create_app(config_name):
# ...
from .main import main as main_blueprint
app.register_blueprint(main_blueprint)
return app
app/main/errors.py:蓝本中的错误处理程序
from flask import render_template
from . import main
@main.app_errorhandler(404)
def page_not_found(e):
return render_template('404.html'), 404
@main.app_errorhandler(500)
def internal_server_error(e):
return render_template('500.html'), 500
app/main/views.py:蓝本中定义的程序路由
from datetime import datetime
from flask import render_template, session, redirect, url_forfrom .
import mainfrom .forms
import NameFormfrom ..
import dbfrom ..models
import User
@main.route('/', methods=['GET', 'POST'])
def index():
form = NameForm()
if form.validate_on_submit():
# ...
return redirect(url_for('.index'))
return render_template('index.html',
form=form, name=session.get('name'),
known=session.get('known', False),
current_time=datetime.utcnow())
启动脚本
manage.py:启动脚本
#!/usr/bin/env python
import os
from app import create_app, db
from app.models import User, Role
from flask.ext.script import Manager, Shell
from flask.ext.migrate import Migrate, MigrateCommand
app = create_app(os.getenv('FLASK_CONFIG') or 'default')
manager = Manager(app)
migrate = Migrate(app, db)
def make_shell_context():
return dict(app=app, db=db, User=User, Role=Role)
manager.add_command("shell", Shell(make_context=make_shell_context))
manager.add_command('db', MigrateCommand)
if __name__ == '__main__':
manager.run()