连接到Flask的数据库,哪种方法更好?
方法一:使用http://flask.pocoo.org/docs/tutorial/dbcon/和http://flask.pocoo.org/docs/patterns/sqlite3/中的特殊g对象
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()
方法二:从https://github.com/mitsuhiko/flask/blob/master/examples/flaskr/flaskr.py使用神秘的_app_ctx_stack
from sqlite3 import dbapi2 as sqlite3 from flask import Flask, request, session, g, redirect, url_for, abort, \ render_template, flash, _app_ctx_stack def get_db(): """Opens a new database connection if there is none yet for the current application context. """ top = _app_ctx_stack.top if not hasattr(top, 'sqlite_db'): top.sqlite_db = sqlite3.connect(app.config['DATABASE']) return top.sqlite_db @app.teardown_appcontext def close_db_connection(exception): """Closes the database again at the end of the request.""" top = _app_ctx_stack.top if hasattr(top, 'sqlite_db'): top.sqlite_db.close()
哪种方法更好? 有什么不同?
两者之间的区别在于,方法一是在g.db
上创build一个连接,不pipe你是否需要它,而方法二只是在该应用程序上下文中第一次调用get_db
时才创build连接。
如果你比较两个,使用这个设置:
yourapp = Flask(__name__) # setup g.db or app_context here # Add a logging statement (print will do) # to the get_db or before_request functions # that simply says "Getting the db connection ..." # Then access / and /1 @yourapp.route("/") def index(): return "No database calls here!" @yourapp.route("/<int:post_id>") def show_post(post_id): # get a post using g.db or get_db return "Went to the DB and got {!r}".format(post)
你会看到,当你使用@app.before_request
设置( g.db
)时,你会得到一个连接, 无论你使用或不使用_app_context
路由,你只有在调用get_db
时才能获得连接。
公平地说,你也可以添加一个描述符到g
,它将执行相同的懒连接(或者在现实生活中,从连接池中获取连接)。 在这两种情况下,你都可以使用更多的魔法( werkzeug.local.LocalProxy
)来创build你自己的自定义线程local ,就像g
, current_app
和request
( 等等 )一样。
第一个问题是即使在不需要的时候也可以获得连接。 第二个是玩内部的第三方框架的缺点,再加上它是不可读的。
只有两个,第二个可能是更好的select。 它不仅不需要获取不需要的路由的连接,而且即使路由中的其他代码path需要一个代码path,也不会获取连接。 (例如,如果您有一些表单validation,则只有在validation通过时才需要连接;validation失败时将不会打开。)只有在使用此设置之前,您才能获取连接。
但是,你可以避免与内部的混乱,仍然可以获得所有这些好处。 就我个人而言,我创build了自己的小全局方法:
import flask import sqlite3 def request_has_connection(): return hasattr(flask.g, 'dbconn') def get_request_connection(): if not request_has_connection(): flask.g.dbconn = sqlite3.connect(DATABASE) # Do something to make this connection transactional. # I'm not familiar enough with SQLite to know what that is. return flask.g.dbconn @app.teardown_request def close_db_connection(ex): if request_has_connection(): conn = get_request_connection() # Rollback # Alternatively, you could automatically commit if ex is None # and rollback otherwise, but I question the wisdom # of automatically committing. conn.close()
然后,在整个应用程序中,总是通过get_request_connection
获得连接,就像你的get_db
函数一样。 直截了当,高效率。 基本上,两全其美。
编辑:
回想起来,我真的不喜欢这些是全局的方法,但我认为这是因为Flask是这样工作的:它给了你实际指向线程本地的“全局variables”。
我推荐Flask-SQLAlchemy ,它扩展了Flask中使用的SQLAlchemy,所以它支持许多不同的数据库。 (来自Flask-SQLAlchemy文档的例子)
build立:
from flask import Flask from flask.ext.sqlalchemy import SQLAlchemy app = Flask(__name__) app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:////tmp/test.db' db = SQLAlchemy(app) class User(db.Model): id = db.Column(db.Integer, primary_key=True) username = db.Column(db.String(80), unique=True) email = db.Column(db.String(120), unique=True) def __init__(self, username, email): self.username = username self.email = email def __repr__(self): return '<User %r>' % self.username
现在,您可以导入/使用User
类来访问数据库中的用户表。
创build新用户:
>>> from yourapplication import User >>> admin = User('admin', 'admin@example.com') >>> guest = User('guest', 'guest@example.com')
将用户添加到数据库:
>>> db.session.add(admin) >>> db.session.add(guest) >>> db.session.commit()
查询已经在数据库中的用户:
>>> users = User.query.all() [<User u'admin'>, <User u'guest'>] >>> admin = User.query.filter_by(username='admin').first() <User u'admin'>
我会用方法一 – 更可读,更less“hackish”。
方法2可能是为扩展集成而devise的( app-ctx-stack的 例子和解释 )。 虽然他们可能有非常相似的效果,方法之一应该用于正常情况。