Какой подход лучше использовать для подключения к базе данных во Flask?

43

Первый метод: использование специального g-объекта из http://flask.pocoo.org/docs/tutorial/dbcon/ и http://flask.pocoo.org/docs/patterns/sqlite3/

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()

Метод второй: использование Mysterious _app_ctx_stack из https://github.com/mitsuhiko/flask/blob/master/examples/flaskr/flaskr.py

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()

Какой метод лучше? В чем разница?

  • 1
    Связано, но без объяснения причин: flask.pocoo.org/docs/extensiondev
  • 0
    Таинственный _app_ctx_stack, LOL
Теги:
flask
sqlite3

4 ответа

26

Разница между двумя заключается в том, что метод создает соединение на g.db, если вам это нужно или нет, а метод второй создает соединение только при первом вызове get_db в этом контексте приложения.

Если вы сравните их, используйте эту настройку:

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, если быть точным), чтобы создать свой собственный поток локальный, который действует как g, current_app и request (среди других).

12

Первая проблема связана с приобретением соединений, даже если они не нужны. Второй имеет недостаток в игре с внутренними компонентами сторонней структуры, плюс это довольно нечитаемо.

Из двух только один, второй, вероятно, лучший выбор. Мало того, что он не получает соединение для маршрутов, которым он не нужен, он не получает соединение, если вы идете по любому пути кода, который ему не нужен, даже если другие пути кода на маршруте требуют одного. (Например, если у вас есть какая-либо форма проверки, вам нужно только соединение, если проверка прошла, это не откроет ее, когда проверка не удалась.) Вы только получаете соединения прямо перед тем, как использовать их с этой настройкой.

Однако вы можете избежать возиться с внутренними элементами и получить все эти преимущества. Лично я создал свои собственные глобальные методы:

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. Простая и высокая эффективность. В принципе, лучшее из обоих миров.

Edit:

В ретроспективе мне действительно не нравится тот факт, что это глобальные методы, но я думаю, что причина этого в том, что так работает Flask: она дает вам "глобальные", которые фактически указывают на локаторы потоков.

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 для доступа к таблице User в вашей базе данных.

Создайте новых пользователей:

>>> from yourapplication import User
>>> admin = User('admin', '[email protected]')
>>> guest = User('guest', '[email protected]')

Добавьте пользователей в базу данных:

>>> 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'>
  • 4
    если вы не собираетесь показывать, какой подход лучше , хотя бы покажите, как подключиться к базе данных в Flask
  • 1
    @jberger Этот пример включает в себя все необходимое для подключения к базе данных, а затем запрашивает и добавляет пользователей. Я не понимаю, что вы подразумеваете под подключением? SQLAlchemy обрабатывает все соединения за кулисами.
Показать ещё 2 комментария
2

Я бы пошел с одним методом - более читаемым и менее "хакерским".

Метод 2, вероятно, предназначен для интеграции расширений фляж (пример и объяснение app-ctx-stack). Хотя они, вероятно, имеют очень похожий эффект, метод следует использовать для нормальных случаев.

Ещё вопросы

Сообщество Overcoder
Наверх
Меню