首先声明,不知是否个人原因,跟着[flask0.10.1文档][1] 做的完全不对(这个文档长得像官网文档,翻译过来的实际不太对)最后是跟着[官方文档][2]才做出来的,真的坑。
所需用到的:
- python3.*
- virtualenv
- flask
- sqlite3
步骤0:创建文件夹(亦可理解为创建项目)
结构如下:
/flaskr --项目名称和项目的根目录
/static --短暂的理解为存放样式的地方
/templates --页面模板
flaskr 文件夹不是一个 Python 包,只是个我们放置文件的地方。在接下来的步骤中,我们会直接把数据库模式和主模块放在这个目录中。 用户可以通过 HTTP 访问 static 文件夹中的文件,也即存放 css 和 javascript 文件的地方。Flask 会在 templates 文件夹里寻找 Jinja2 模板,之后教程中创建的模板将会放在这个文件夹里。
步骤1:数据库模式(亦可理解为数据库表)
由于应用很简单,所以简单的创一张表就行。把以下的内容放进 schema.sql
,并且放在项目根目录
drop table if exists entries;
create table entries (
id integer primary key autoincrement,
title string not null,
text string not null
);
步骤2:应用程序代码部分
有了数据库表,开始创建应用的模块了。在这里我们把它叫做flaskr.py
,并放在根目录下,我们从添加所需的导入语句和添加配置部分开始。对于小型应用,可以直接把配置放在主模块里,正如我们现在要做的一样。但更简洁的方案是创建独立的 .ini 或 .py 文件,并载入或导入里面的值。
In flaskr.py:
# all the imports
import sqlite3
from flask import Flask, request, session, g, redirect, url_for, \
abort, render_template, flash
# configuration
DATABASE = '/tmp/flaskr.db'
DEBUG = True
SECRET_KEY = 'development key'
USERNAME = 'admin'
PASSWORD = 'default'
Next we can create our actual application and initialize it with the config from the same file, in flaskr.py:
# create our little application :)创建我们的小应用程序:)--实例化
app = Flask(__name__)
app.config.from_object(__name__)
通常,加载一个单独的、环境特定的配置文件是个好主意。Flask 允许你导入多份配置,并且使用最后的导入中定义的设置。这使得配置设定过程更可靠。 from_envvar() 可用于达此目的。
app.config.from_envvar('FLASKR_SETTINGS', silent=True)
关于这里 官网还有一长串的解释,看不懂。[传送门][3]
连接数据库
我们也添加一个方法方便地连接到指定的数据库。可以用来打开一个连接请求,也从Python交互式shell或脚本。以后这将派上用场。
def connect_db():
return sqlite3.connect(app.config['DATABASE'])
最后,如果我们想要把这个文件当做独立应用来运行,我们只需在可启动服务器文件的末尾添加这一行:
if __name__ == '__main__':
app.run()
如此我们便可以开始顺利运行这个应用,使用如下命令:
python flaskr.py
步骤3:创建数据库
Flaskr之前是一个数据库驱动的应用程序中,更准确地说,一个应用程序由一个关系数据库系统。这样的系统需要一个模式,告诉他们如何存储这些信息。所以在第一次启动服务器创建该模式是很重要的。
可以通过管道把 schema.sql 作为 sqlite3 命令的输入来创建这个模式,命令为如下:
sqlite3 /tmp/flaskr.db < schema.sql
这种方法的缺点是需要安装 sqlite3 命令,而并不是每个系统都有安装。而且你必须提供数据库的路径,否则将报错。用函数来初始化数据库是个不错的想法。
如果你想这样做,你首先必须导入contextlib.closing() 因此将以下代码添加到flaskr.py:
from contextlib import closing
接下来,我们可以创建一个名为init_db的函数来初始化数据库。我们可以使用connect_db定义的函数。
def init_db():
with closing(connect_db()) as db:
with app.open_resource('schema.sql', mode='r') as f:
db.cursor().executescript(f.read())
db.commit()
关于这个方法,[官网][4]也有详细的说明,英语不好不照搬了
有了这个函数,就在python交互环境中初始化数据库
>>> from flaskr import init_db
>>> init_db()
步骤4:Request Database Connections
知道如何打开数据库连接,如何请求,
Flask allows us to do that with the before_request(), after_request() and teardown_request() decorators:
@app.before_request
def before_request():
g.db = connect_db()
@app.teardown_request
def teardown_request(exception):
db = getattr(g, 'db', None)
if db is not None:
db.close()
Functions marked with before_request() are called before a request and passed no arguments. Functions marked with after_request() are called after a request and passed the response that will be sent to the client. They have to return that response object or a different one. They are however not guaranteed to be executed if an exception is raised, this is where functions marked with teardown_request() come in. They get called after the response has been constructed. They are not allowed to modify the request, and their return values are ignored. If an exception occurred while the request was being processed, it is passed to each function; otherwise, None is passed in.
We store our current database connection on the special g object that Flask provides for us. This object stores information for one request only and is available from within each function. Never store such things on other objects because this would not work with threaded environments. That special g object does some magic behind the scenes to ensure it does the right thing.
For an even better way to handle such resources see the Using SQLite 3 with Flask documentation.
步骤5:创建视图函数
显示条目
这个视图显示数据库中存储的所有条目。它绑定在应用的根地址,并从数据库查询出文章的标题和正文。id 值最大的条目(最新的条目)会显示在最上方。从指针返回的行是按 select 语句中声明的列组织的元组。这对像我们这样的小应用已经足够了, 但是你可能会想把它转换成字典。如果你对这方面有兴趣,请参考 简化查询 的例子。
视图函数会将条目作为字典传递给 show_entries.html 模板,并返回渲染结果:
@app.route('/')
def show_entries():
cur = g.db.execute('select title, text from entries order by id desc')
entries = [dict(title=row[0], text=row[1]) for row in cur.fetchall()]
return render_template('show_entries.html', entries=entries)
添加条目
这个视图允许已登入的用户添加新条目,并只响应 POST 请求,实际的表单显示在 show_entries 页。如果一切工作正常,我们会用 flash() 向下一次请求发送提示消息,并重定向回 show_entries 页:
@app.route('/add', methods=['POST'])
def add_entry():
if not session.get('logged_in'):
abort(401)
g.db.execute('insert into entries (title, text) values (?, ?)',
[request.form['title'], request.form['text']])
g.db.commit()
flash('New entry was successfully posted')
return redirect(url_for('show_entries'))
登入和登出
这些函数用来让用户登入登出。登入通过与配置文件中的数据比较检查用户名和密码, 并设定会话中的 logged_in 键值。如果用户成功登入,那么这个键值会被设为 True ,并跳转回 show_entries 页。此外,会有消息闪现来提示用户登入成功。 如果发生一个错误,模板会通知,并提示重新登录。
@app.route('/login', methods=['GET', 'POST'])
def login():
error = None
if request.method == 'POST':
if request.form['username'] != app.config['USERNAME']:
error = 'Invalid username'
elif request.form['password'] != app.config['PASSWORD']:
error = 'Invalid password'
else:
session['logged_in'] = True
flash('You were logged in')
return redirect(url_for('show_entries'))
return render_template('login.html', error=error)
登出函数,做相反的事情,从会话中删除 logged_in 键。我们这里使用了一个简洁的方法:如果你使用字典的 pop() 方法并传入第二个参数(默认), 这个方法会从字典中删除这个键,如果这个键不存在则什么都不做。这很有用,因为我们不需要检查用户是否已经登入。
@app.route('/logout')
def logout():
session.pop('logged_in', None)
flash('You were logged out')
return redirect(url_for('show_entries'))
步骤6:模板(前端页面)
接下来我们应该创建模板了。如果我们现在请求 URL,只会得到 Flask 无法找到模板的异常。 模板使用 Jinja2 语法并默认开启自动转义。这意味着除非你使用 Markup 标记或在模板中使用 |safe 过滤器,否则 Jinja 2 会确保特殊字符,比如 < 或 > 被转义为等价的 XML 实体。
我们也会使用模板继承在网站的所有页面中重用布局。
将下面的模板放在 templates
文件夹里:
layout.html
这个模板包含 HTML 主体结构、标题和一个登入链接(用户已登入则提供登出)。 如果有,它也会显示闪现消息。 {% block body %} 块可以被子模板中相同名字的块( body )替换。
session 字典在模板中也是可用的。你可以用它来检查用户是否已登入。 注意,在 Jinja 中你可以访问不存在的对象/字典属性或成员。比如下面的代码, 即便 'logged_in' 键不存在,仍然可以正常工作:
<!doctype html>
<title>Flaskr</title>
<link rel=stylesheet type=text/css href="{{ url_for('static', filename='style.css') }}">
<div class=page>
<h1>Flaskr</h1>
<div class=metanav>
{% if not session.logged_in %}
<a href="{{ url_for('login') }}">log in</a>
{% else %}
<a href="{{ url_for('logout') }}">log out</a>
{% endif %}
</div>
{% for message in get_flashed_messages() %}
<div class=flash>{{ message }}</div>
{% endfor %}
{% block body %}{% endblock %}
</div>
show_entries.html
这个模板继承了上面的 layout.html 模板来显示消息。注意 for 循环会遍历并输出所有 render_template() 函数传入的消息。我们还告诉表单使用 HTTP 的 POST 方法提交信息到 add_entry 函数:
{% extends "layout.html" %}
{% block body %}
{% if session.logged_in %}
<form action="{{ url_for('add_entry') }}" method=post class=add-entry>
<dl>
<dt>Title:
<dd><input type=text size=30 name=title>
<dt>Text:
<dd><textarea name=text rows=5 cols=40></textarea>
<dd><input type=submit value=Share>
</dl>
</form>
{% endif %}
<ul class=entries>
{% for entry in entries %}
<li><h2>{{ entry.title }}</h2>{{ entry.text|safe }}
{% else %}
<li><em>Unbelievable. No entries here so far</em>
{% endfor %}
</ul>
login.html
最后是登入模板,只是简单地显示一个允许用户登入的表单:
{% extends "layout.html" %}
{% block body %}
<h2>Login</h2>
{% if error %}<p class=error><strong>Error:</strong> {{ error }}{% endif %}
<form action="{{ url_for('login') }}" method=post>
<dl>
<dt>Username:
<dd><input type=text name=username>
<dt>Password:
<dd><input type=password name=password>
<dd><input type=submit value=Login>
</dl>
</form>
{% endblock %}
步骤7:添加样式表
现在其它的一切都可以正常工作,是时候给应用添加样式了。只需在之前创建的 static 文件夹中创建一个名为 style.css 的样式表:
body { font-family: sans-serif; background: #eee; }
a, h1, h2 { color: #377BA8; }
h1, h2 { font-family: 'Georgia', serif; margin: 0; }
h1 { border-bottom: 2px solid #eee; }
h2 { font-size: 1.2em; }
.page { margin: 2em auto; width: 35em; border: 5px solid #ccc;
padding: 0.8em; background: white; }
.entries { list-style: none; margin: 0; padding: 0; }
.entries li { margin: 0.8em 1.2em; }
.entries li h2 { margin-left: -1em; }
.add-entry { font-size: 0.9em; border-bottom: 1px solid #ccc; }
.add-entry dl { font-weight: bold; }
.metanav { text-align: right; font-size: 0.8em; padding: 0.3em;
margin-bottom: 1em; background: #fafafa; }
.flash { background: #CEE5F5; padding: 0.5em;
border: 1px solid #AACBE2; }
.error { background: #F0D6D6; padding: 0.5em; }
如何登陆? 一开始在application code中就定义了。
USERNAME = 'admin'
PASSWORD = 'default'
[1]: http://docs.jinkan.org/docs/flask/
[2]: http://flask.pocoo.org/docs/0.10/
[3]: http://flask.pocoo.org/docs/0.10/tutorial/setup/#tutorial-setup
[4]: http://flask.pocoo.org/docs/0.10/tutorial/dbinit/#tutorial-dbinit
网友评论