Flask SQLite
在 Flask 中,通过 g 对象可以使用 before_request() 和 teardown_request() 在请求开始前打开数据库连接,在请求结束后关闭连接。
以下是一个在 Flask 中使用 SQLite 3 的例子:
import sqlite3 from flask import g DATABASE = '/path/to/database.db' def connect_db(): return sqlite3.connect(DATABASE) @app.before_request def before_request(): g.db = connect_db() @app.teardown_request def teardown_request(exception): if hasattr(g, 'db'): g.db.close()
请记住,销毁函数是一定会被执行的。即使请求前处理器执行失败或根本没有执行, 销毁函数也会被执行。因此,我们必须保证在关闭数据库连接之前数据库连接是存在 的。
1. 按需连接
上述方式的缺点是只有在 Flask 执行了请求前处理器时才有效。如果你尝试在脚本或者 Python 解释器中使用数据库,那么你必须这样来执行数据库连接代码:
with app.test_request_context(): app.preprocess_request() # now you can use the g.db object
这样虽然不能排除对请求环境的依赖,但是可以按需连接数据库:
def get_connection(): db = getattr(g, '_db', None) if db is None: db = g._db = connect_db() return db
这样做的缺点是必须使用 db = get_connection() 来代替直接使用 g.db。
2. 简化查询
现在,在每个请求处理函数中可以通过访问 g.db 来得到当前打开的数据库连接。为了 简化 SQLite 的使用,这里有一个有用的辅助函数:
def query_db(query, args=(), one=False): cur = g.db.execute(query, args) rv = [dict((cur.description[idx][0], value) for idx, value in enumerate(row)) for row in cur.fetchall()] return (rv[0] if rv else None) if one else rv
使用这个实用的小函数比直接使用原始指针和转接对象要舒服一点。
可以这样使用上述函数:
for user in query_db('select * from users'): print user['username'], 'has the id', user['user_id']
只需要得到单一结果的用法:
user = query_db('select * from users where username = ?', [the_username], one=True) if user is None: print 'No such user' else: print the_username, 'has the id', user['user_id']
如果要给 SQL 语句传递参数,请在语句中使用问号来代替参数,并把参数放在一个列表中 一起传递。不要用字符串格式化的方式直接把参数加入 SQL 语句中,这样会给应用带来 SQL 注入 的风险。
3. 初始化模式
关系数据库是需要模式的,因此一个应用常常需要一个 schema.sql 文件来创建 数据库。因此我们需要使用一个函数来其于模式创建数据库。下面这个函数可以完成这个 任务:
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 解释器中创建数据库:
>>> from yourapplication import init_db >>> init_db()
4. 以下是一个实例:
创建一个SQLite数据库 'database.db' 并在其中创建学生表。
import sqlite3 conn = sqlite3.connect('database.db') print "Opened database successfully"; conn.execute('CREATE TABLE students (name TEXT, addr TEXT, city TEXT, pin TEXT)') print "Table created successfully"; conn.close()
我们的Flask应用程序有三个 View 功能。
第一个 new_student() 函数已绑定到URL规则 ('/addnew')。它呈现包含学生信息表单的HTML文件。
@app.route('/enternew') def new_student(): return render_template('student.html')
'student.html' 的HTML脚本如下:
<html> <body> <form action = "{{ url_for('addrec') }}" method = "POST"> <h3>Student Information</h3> Name<br> <input type = "text" name = "nm" /></br> Address<br> <textarea name = "add" ></textarea><br> City<br> <input type = "text" name = "city" /><br> PINCODE<br> <input type = "text" name = "pin" /><br> <input type = "submit" value = "submit" /><br> </form> </body> </html>
可以看出,表单数据被发布到绑定 addrec() 函数的 '/addrec' URL。
这个 addrec() 函数通过 POST 方法检索表单的数据,并插入学生表中。与insert操作中的成功或错误相对应的消息将呈现为 'result.html' 。
@app.route('/addrec',methods = ['POST', 'GET']) def addrec(): if request.method == 'POST': try: nm = request.form['nm'] addr = request.form['add'] city = request.form['city'] pin = request.form['pin'] with sql.connect("database.db") as con: cur = con.cursor() cur.execute("INSERT INTO students (name,addr,city,pin) VALUES (?,?,?,?)",(nm,addr,city,pin) ) con.commit() msg = "Record successfully added" except: con.rollback() msg = "error in insert operation" finally: return render_template("result.html",msg = msg) con.close()
result.html 的HTML脚本包含一个转义语句 {{msg}} ,它显示 Insert 操作的结果。
<!doctype html> <html> <body> result of addition : {{ msg }} <h2><a href = "\">go back to home page</a></h2> </body> </html>
该应用程序包含由 '/list' URL表示的另一个 list() 函数。
它将 'rows' 填充为包含学生表中所有记录的 MultiDict 对象。此对象将传递给 list.html 模板。
@app.route('/list') def list(): con = sql.connect("database.db") con.row_factory = sql.Row cur = con.cursor() cur.execute("select * from students") rows = cur.fetchall() return render_template("list.html",rows = rows)
此 list.html 是一个模板,它遍历行集并在HTML表中呈现数据。
<!doctype html> <html> <body> <table border = 1> <thead> <td>Name</td> <td>Address>/td< <td>city</td> <td>Pincode</td> </thead> {% for row in rows %} <tr> <td>{{row["name"]}}</td> <td>{{row["addr"]}}</td> <td> {{ row["city"]}}</td> <td>{{row['pin']}}</td> </tr> {% endfor %} </table> <a href = "/">Go back to home page</a> </body> </html>
最后, '/' URL 规则呈现 'home.html' ,它作为应用程序的入口点。
@app.route('/') def home(): return render_template('home.html')
以下是 Flask-SQLite 应用程序的完整代码。
from flask import Flask, render_template, request import sqlite3 as sql app = Flask(__name__) @app.route('/') def home(): return render_template('home.html') @app.route('/enternew') def new_student(): return render_template('student.html') @app.route('/addrec',methods = ['POST', 'GET']) def addrec(): if request.method == 'POST': try: nm = request.form['nm'] addr = request.form['add'] city = request.form['city'] pin = request.form['pin'] with sql.connect("database.db") as con: cur = con.cursor() cur.execute("INSERT INTO students (name,addr,city,pin) VALUES (?,?,?,?)",(nm,addr,city,pin) ) con.commit() msg = "Record successfully added" except: con.rollback() msg = "error in insert operation" finally: return render_template("result.html",msg = msg) con.close() @app.route('/list') def list(): con = sql.connect("database.db") con.row_factory = sql.Row cur = con.cursor() cur.execute("select * from students") rows = cur.fetchall(); return render_template("list.html",rows = rows) if __name__ == '__main__': app.run(debug = True)
在开发服务器开始运行时,从Python shell运行此脚本。在浏览器中访问 http://localhost:5000/ ,显示一个简单的菜单:
点击 “添加新记录” 链接以打开 学生信息 表单。
填写表单字段并提交。底层函数在学生表中插入记录。
返回首页,然后点击 '显示列表' 链接。将显示一个显示样品数据的表。
下一章:Flask SQLAlchemy
在 Flask Web 应用程序中使用原始 SQL 对数据库执行 CRUD 操作可能很繁琐。相反,SQLAlchemy 工具包是一个强大的 OR Mapper,它为应用程序开发人员提供了 SQL 的全部功能和灵活性。Flask-SQLAlchemy 是 Flask 扩展,它将对 SQLAlchemy 的支持添加到Flask应用程序中。