第一章 安装
1.安装virtualenv
$ pip install virtualenv // or $ sudo easy_install virtualenv
$ virtualenv --version
2.Clone source code to local
$ cd flask-2017 //create a folder named flask-2017
$ git clone [email protected]:baby4bamboo/flasky.git
$ cd flasky
$ git checkout 1a
3.虚拟环境
$ virtualenv venv //创建虚拟环境
$ source venv/bin/activate //启动虚拟环境
$ deactivate //关闭虚拟环境
4.需要安装的Flask 相关的包:
$ pip install flask //MarkupSafe, Jinja2, click, Werkzeug, itsdangerous, flask
5.python 命令行:
$ python
>>> import flask //检查某个包是否安装好
>>> exit() //退出python命令行
第二章 程序的基本结构
Web 服务器使用一种名为 Web 服务器网关接口
(Web Server Gateway Interface,WSGI)的协议,把接收自客户端的所有请求都转交给这个对象处理。
git checkout 2b
from flask import Flask
app = Flask(__name__)
@app.route('/')
def index():
return 'Hello World!
'
@app.route('/user/')
def user(name):
return 'Hello, %s!
' % name
if __name__ == '__main__':
app.run(debug=True)
__name__=='__main__'
是 Python 的惯常用法,在这里确保直接执行这个脚本时才启动开发Web 服务器。如果这个脚本由其他脚本引入,程序假定父级脚本会启动不同的服务器,因此不会执行 app.run()
python hello.py //启动程序
Ctrl + C //退出程序
然后浏览器访问
http://127.0.0.1:5000/
请求上下文
>>> app_ctx = app.app_context()
>>> app_ctx.push()
>>> current_app.name
'hello'
>>> app_ctx.pop()
路由调度
Flask 使用 app.route 修饰器或者非修饰器形式的 app.add_url_rule() 生成映射。
$ python
>>> from hello import app
>>> app.url_map
Map([ index>,
' (HEAD, OPTIONS, GET) -> static>,
' (HEAD, OPTIONS, GET) -> user>])
Hook
• before_first_request:注册一个函数,在处理第一个请求之前运行。
• before_request:注册一个函数,在每次请求之前运行。
• after_request:注册一个函数,如果没有未处理的异常抛出,在每次请求之后运行。
• teardown_request:注册一个函数,即使有未处理的异常抛出,也在每次请求之后运行
响应
Flask将视图函数的返回值,作为响应结果,通常就是一个字符串
也可以接受第二个参数,为响应的状态码,例如200,404等
也可以接受第三个参数,是一个由首部(header)组成的字典
@app.route('/')
def index():
return 'Bad Request
', 400
当然,更好的选择是返回一个respones对象
from flask import make_response
@app.route('/')
def index():
response = make_response('This document carries a cookie!
')
response.set_cookie('answer', '42')
return response
有一种特殊的响应,叫做重定向(14章会有例子)
from flask import redirect
@app.route('/')
def index():
return redirect('http://www.example.com')
还有一种特殊的响应,由abort()函数生成,用来处理错误
abort 不会把控制权交还给调用它的函数,而是抛出异常把控制权交给 Web 服务器
from flask import abort
@app.route('/user/')
def get_user(id):
user = load_user(id)
if not user:
abort(404)
return 'Hello, %s
' % user.name
Flask扩展
$ pip install flask-script
文件的改动:
from flask.ext.script import Manager
manager = Manager(app)
# ...
if __name__ == '__main__':
manager.run()
这个扩展的初始化方法也适用于其他很多扩展:把程序实例作为参数传给构造函数,初始化主类的实例。创建的对象可以在各个扩展中使用。在这里,服务器由 manager.run() 启动,启动后就能解析命令行了。
python hello.py runserver --host 0.0.0.0 //监听所有窗口
第三章 模板
面对一个请求,其实server做了两件事情,分成‘业务逻辑’和‘表现逻辑’
例如用户输入了账号密码,点击登录
业务逻辑:在检查账号密码是否匹配,如果匹配成功则登录
表现逻辑:跳转到已经登录用户的首页
Jinja2模板引擎
Jinja有两种定界符。{% ... %}和{{ ... }}。前者用于执行像for循环或赋值等语句,后者向模板输出一个表达式的结果。
参见: http://jinja.pocoo.org/docs/templates/#synopsis
if
{% if user %}
Hello, {{ user }}!
{% else %}
Hello, Stranger!
{% endif %}
for
{% for comment in comments %}
- {{ comment }}
{% endfor %}
macro
{% macro render_comment(comment) %}
{{ comment }}
{% endmacro %}
{% for comment in comments %}
{{ render_comment(comment) }}
{% endfor %}
单独文件导入
{% import 'macros.html' as macros %}
{% for comment in comments %}
{{ macros.render_comment(comment) }}
{% endfor %}
模板继承
base.html
{% block head %}
{% block title %}{% endblock %} - My Application
{% endblock %}
{% block body %}
{% endblock %}
extend.html
{% extends "base.html" %}
{% block title %}Index{% endblock %}
{% block head %}
{{ super() }}
{% endblock %}
{% block body %}
Hello, World!
{% endblock %}
模板渲染
Hello, {{ name }}!
@app.route('/user/')
def user(name):
return render_template('user.html', name=name)
name=name,左边的name表示模板中的占位符,右边的name表示url中传进来的参数
使用Flask-Bootstrap集成Twitter Bootstrap
$ pip install flask-bootstrap
hello.py:初始化 Flask-Bootstrap
from flask.ext.bootstrap import Bootstrap
# ...
bootstrap = Bootstrap(app)
doc 整个 HTML 文档
标签中的内容
html_attribs 标签的属性
html 标签中的内容
head
title标签中的内容 标签的属性
metas 一组 标签
styles 层叠样式表定义
body_attribs
body 标签中的内容
navbar 用户定义的导航条
content 用户定义的页面内容
scripts 文档底部的 JavaScript 声明
链接
url_for('index') //得到相对路径,这里会得到'/'
url_for('index', _external=True) //得到绝对路径,这里是'http://localhost:5000/'。(外部使用的时候要用,例如邮件)
url_for('user', name='john', _external=True) //返回结果是 http://localhost:5000/user/john。
url_for('index', page=2) // 返回结果是 /?page=2
使用Flask-Moment本地化日期和时间
1.安装
pip install flask-moment
2.hello.py 中初始化Moment:
from flask.ext.moment import Moment
moment = Moment(app)
3.基模板(templates/base.html)中引入moment.js
{% block scripts %}
{{ super() }}
{{ moment.include_moment() }}
{% endblock %}
4.把变量current_time 传入模板进行渲染
from datetime import datetime
@app.route('/')
def index():
return render_template('index.html',current_time=datetime.utcnow())
5.在模板中渲染 current_time
templates/index.html
{% block page_content %} //page_content 表示显示的内容,之前错误的放在了{% block scripts %}中,发现不能在页面显示
The local date and time is {{ moment(current_time).format('LLL') }}
That was {{ moment(current_time).fromNow(refresh=True) }}
{% endblock %}
第四章 web表单
Flask-WTF 能保护所有表单免受跨站请求伪造(Cross-Site Request Forgery,CSRF)的攻击
hello.py
app = Flask(__name__)
app.config['SECRET_KEY'] = 'hard to guess string'
$ pip install flask-wtf
M
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')
T
templates/index.html
{% extends "base.html" %}
{% import "bootstrap/wtf.html" as wtf %}
{% block title %}Flasky{% endblock %}
{% block page_content %}
Hello, {% if name %}{{ name }}{% else %}Stranger{% endif %}!
{{ wtf.quick_form(form) }}
{% endblock %}
V
hello.py:
@app.route('/', methods=['GET', 'POST'])
def index():
name = None
form = NameForm() //建一个M
if form.validate_on_submit():
name = form.name.data
form.name.data = ''
return render_template('index.html', form=form, name=name) //把得到的数据传给T
MTV:
M是Model,数据结构,
T是template,模板,最后渲染出来的页面的样子
V是view,先建一个M,然后通过一系列的逻辑和运算,得到需要的数据,传给T
注:这里我写的时候出现了三个错误
app.config['SECRET_KEY'] = 'hard to guess string' //这句忘记加了
{{ wtf.quick_form(form) }} //写成了{% wtf.quick_form(form) %}
name = form.name.data //写成了 name = form.name.data()
session and 重定向
from flask import Flask, render_template, session, redirect, url_for
@app.route('/', methods=['GET', 'POST'])
def index():
form = NameForm()
if form.validate_on_submit():
session['name'] = form.name.data
return redirect(url_for('index'))
return render_template('index.html', form=form, name=session.get('name'))
hello.py flash消息
from flask import Flask, render_template, session, redirect, url_for, flash
@app.route('/', methods=['GET', 'POST'])
def index():
form = NameForm()
if form.validate_on_submit():
old_name = session.get('name')
if old_name is not None and old_name != form.name.data:
flash('Looks like you have changed your name!')
session['name'] = form.name.data
return redirect(url_for('index'))
return render_template('index.html',form = form, name = session.get('name'))
base.html
{% block content %}
{% for message in get_flashed_messages() %}
{{ message }}
{% endfor %}
{% block page_content %}{% endblock %}
{% endblock %}
第七章 web表单
项目结构
工厂函数
app/__init__.py
:
bootstrap = Bootstrap()
mail = Mail()
moment = Moment()
db = SQLAlchemy()
def create_app(config_name):
app = Flask(__name__) //实例化一个Flask对象,命名为app
app.config.from_object(config[config_name]) //把我们配置的config,设置给这个app
config[config_name].init_app(app) //config中定义的初始化函数
bootstrap.init_app(app)
mail.init_app(app)
moment.init_app(app)
db.init_app(app)
from .main import main as main_blueprint //在app中,注册blueprint
app.register_blueprint(main_blueprint)
return app
blueprint
app/main/__init__.py
:创建蓝本:
from flask import Blueprint
main = Blueprint('main', __name__)
from . import views, errors
实例化一个蓝本,命名为main,接受两个参数,第一个是蓝本的名字,第二个是蓝本所在的包或模块,一般都是__name__
views(路由设定),errors(错误处理)必须在创建蓝本之后导入,以防止循环依赖。
app/main/view.py:
# from ...
@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))
1)不再像单文件@route('/'),有了蓝本 以后,路由都是由蓝本提供,所以前面要加上蓝本的名字
@main.route('/', methods=['GET', 'POST'])。
2)url_for 不再像单文件的url_for('index'),现在需要指明相应的蓝本url_for('main.index')
url_for实际上就是根据后面的参数来生成url的,以便程序跳转,所以一般都这样用redirect(url_for('.index'))
3)蓝本的端点都是有命名空间的,其实就是蓝本的名字,这样可以避免冲突。例如main.index和auth.index就是两个页面,可以同时存在。
4)url_for('main.index')可以简写成url_for('.index'),这里省略的命名空间就是当前请求所在的蓝本,例如现在的main,跨蓝本的重定向必须带有命名空间的端点名。
7.5 需求文件
(venv) $ pip freeze >requirements.txt //生成需求文件
(venv) $ pip install -r requirements.txt //使用需求文件去安装各种包