链接在此:http://dormousehole.readthedocs.org/en/latest/tutorial/introduction.html
当然,直接根据上述链接一步一步来就可以实现所有的功能。但是,根据我在学习的过程中的心得,我觉得有必要记录一下。
一。环境搭建
跟前面我的记录的flask环境搭建有些不同,我在这里介绍另外一种搭建环境的方式。
首先,从github上面下载flask。可以直接下载tar文件,或者在安装来git环境的情况下直接键入如下命令:
git clone https://github.com/mitsuhiko/flask.git
其次,由于flask会依赖于两个外部库,我们也如上从github上面下载:
git clone https://github.com/mitsuhiko/jinja2.git
git clone https://github.com/mitsuhiko/werkzeug.git
二。目标
我们将要创建一个博客应用。1.可以登录和注销。2.可以添加博客条目。3.页面以一定的顺序来显示。
三。创建应用所需文件夹
/flaskr /static /templates /tmp
其中flaskr是我们应用的主文件夹,static文件夹用于存放css和js等布局文件,templates里面存放html文件,tmp文件夹存放后续生成的数据库文件。
四。生成数据库表单
由于我们使用的是sql数据库,所以新建一个schema.sql文件用于存放博客数据库表结构。
drop table if exists entries; create table entries( id integer primary key autoincrement, title string not null, text string not null )
对于sql的数据库语法虽然不是很明了,但是上述语句很容易理解。注意:上面的最后一句text后不能想当然的添加一个逗号“,”。
五。创建应用服务器的主体
创建flaskr.py文件,键入如下命令:
from flask import Flask, request, session, g, redirect, url_for, \ abort, render_template, flash #for detail app = Flask( __name__ ) if __name__ == "__main__": app.run()
此时可以在终端输入 python flaskr.py来启动服务器,然后在127.0.0.1:5000页面查看结果。由于没有视图渲染,所以会看到404 not found。
六。创建数据库
由于我们要将博客名称和内容存储进数据库,所以,我们先生成必要的数据库表单。
首先,我们需要将本地的sqlite3和我们所创建的数据库结构连接起来:
#import database import sqlite3 #define configure data DATABASE = 'tmp/flaskr.db' #connect global var to app app.config.from_object(__name__) def connect_db(): return sqlite3.connect( app.config['DATABASE'] )
其次,这部分不是很理解,暂时不解释:
from __future__ import with_statement from contextlib import closing def init_db(): with closing( connect_db() ) as db: with app.open_resource( 'schema.sql' ) as f: db.cursor().executescript( f.read() ) db.commit()
上述完成之后,可以在python的shell环境中生成数据库表。
>>> from flaskr import init_db >>> init_db()
如果没有出现错误提示,就说明表已经被创建好了。
七。请求数据库连接
后续我们所发起的http请求,均与数据库有关,所以需要在请求到达的时候,和数据库进行关联。这就要靠before_request和teardown_request装饰器。
@app.before_request def before_request(): g.db = connect_db() @app.teardown_request def teardown_request(exception): g.db.close()
这里面要注意的是g变量,这是flask的一个全局对象,它只在一个请求中起作用。所以将db保存到g中很方便。
八。视图函数
这回我们将要接近主题了,这个视图函数在MVC模型里面来说其实就是控制器C(controller)。我们首先看一下主页面的显示:
@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)
@app.route装饰器作用是将请求路由到对应的函数,即“127.0.0.1:5000/”就对应的show_entries函数。由于刚才我们保存了与数据库的连接到g.db,所以这次我们直接使用来查询数据库。entries是根据表的内容,生成一个包含title和text对的数组。
接下来我们直接添加视图,先来体验以下效果,在templates文件下添加layout.html文件和show_entries.html文件。layout.html是一个模板,通用的。
<!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>
注意:这里面使用的url_for可以查找到static目录下的style.css文件,如果style.css在css目录下,则static应更换为static.css。
{% 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> {% endblock %}
进行到这里,可以在127.0.0.1:5000页面查看效果了,但是你会发现,提示“login”等方法错误。那是因为我们还没有添加“login”方法进来。可以先预留出空间,保证我们目前的例子可以通过。添加如下代码:
@app.route('/login', methods=['GET', 'POST']) def login(): pass @app.route('/logout') def logout(): pass
ok,只要细心,到这一步,我门就算初步成功。页面没有布局(css文件未导入),而且只有login的标签(login目前还不能使用)。
九。完善视图
接下来我们完善视图。添加login和add视图控制函数,此时还需要添加全局变量,例如登陆密码等。
SECRET_KEY = 'development key' USERNAME = 'admin' PASSWORD = 'default'
@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)
@app.route('/logout') def logout(): session.pop('logged_in', None) flash('You were logged out') return redirect(url_for('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'))
十。完善视图
{% 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 %}
十一。添加样式表单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; }
ok,大功告成。