61

方法一:使用来自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()

哪种方法更好?有什么区别?

4

5 回答 5

40

The difference between the two is that method one creates a connection on g.db whether you need it or not while method two only creates the connection when you call get_db for the first time in that application context.

If you compare the two, using this setup:

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)

You'll see that when you hit / using the @app.before_request setup (g.db) you get a connection whether you use it or not, while using the _app_context route you only get a connection when you call get_db.

To be fair, you can also add a descriptor to g that will do the same lazy connecting (or in real life, acquiring a connection from a connection pool). And in both cases you can use a bit more magic (werkzeug.local.LocalProxy to be precise) to create your own custom thread local that acts like g, current_app and request (among others).

于 2013-05-14T03:40:46.607 回答
15

第一个问题是即使在不需要时也要获取连接。第二个有使用第三方框架内部的缺点,而且它非常不可读。

仅在两者中,第二个可能是更好的选择。它不仅不会为不需要的路由获取连接,而且如果您沿着不需要的任何代码路径走下去,即使路由中的其他代码路径需要连接,它也不会获取连接。(例如,如果您有一些表单验证,则仅在验证通过时才需要连接;验证失败时不会打开连接。)您仅在使用此设置之前获取连接。

但是,您可以避免弄乱内部结构并仍然获得所有这些好处。就个人而言,我创建了自己的小全局方法:

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 就是这样工作的:它为您提供了实际上指向线程局部变量的“全局变量”。

于 2013-05-17T04:42:14.440 回答
7

我推荐Flask-SQLAlchemy,它扩展了 SQLAlchemy 以在 Flask 中使用,因此它支持许多不同的数据库。(来自 Flask-SQLAlchemy 文档的示例)

设置:

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该类来访问数据库中的用户表。

创建新用户:

>>> 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'>
于 2013-05-15T03:14:10.337 回答
4

我会使用方法一 - 更具可读性和更少的“hackish”。

方法 2 可能是为烧瓶扩展集成而设计的( app-ctx-stack 的示例解释)。虽然它们可能具有非常相似的效果,但方法一应该用于正常情况。

于 2013-05-13T13:25:36.160 回答
0

要在同一个烧瓶会话中保留数据库连接,您可以使用应用程序上下文并分配数据库连接。如果连接中断,上下文将在不断轮询连接对象时尝试重新建立连接。

from flask import Flask
application = Flask(__name__)


def connect_to_database():
  db_handler = SqliteDBConnect("uid={0};""pwd={1}".format(UID, PWD),
                                table_prefix="{}".format(TBL_PRFX))
  return db_handler

fd = {'_database': None}
def get_db():
  db = fd['_database']
  if not isinstance(db, SqliteDBConnect):
    fd['_database'] = connect_to_database()
    db = fd['_database']
return db

with application.app_context():
  #Get DB connection from application's context
  db = LocalProxy(lambda: get_db())
于 2020-02-20T15:28:40.907 回答