61 votes

Se connecter à une base de données en Flask, quelle approche est la meilleure ?

Première méthode : utiliser l'objet spécial g de http://flask.pocoo.org/docs/tutorial/dbcon/ y 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()

Deuxième méthode : utiliser la mystérieuse _app_ctx_stack de 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()

Quelle méthode est la meilleure ? Quelle est la différence ?

1 votes

Relié, mais sans explication non plus : flask.pocoo.org/docs/extensiondev

41voto

Sean Vieira Points 47080

La différence entre les deux est que la première méthode crée une connexion sur g.db que vous en ayez besoin ou non, tandis que la deuxième méthode ne crée la connexion que lorsque vous appelez get_db pour la première fois dans ce contexte d'application.

Si vous comparez les deux, en utilisant cette configuration :

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)

Vous verrez que lorsque vous cliquez sur / en utilisant le @app.before_request installation ( g.db ) vous obtenez une connexion que vous l'utilisiez ou non tout en utilisant le _app_context route vous obtenez seulement une connexion _quand vous appelez get_db_ .

Pour être juste, vous pouvez aussi ajouter un descripteur à g qui fera la même chose en se connectant paresseusement (ou dans la vraie vie, en acquérant une connexion à partir d'un pool de connexion). Et dans ambos vous pouvez utiliser un peu plus de magie ( werkzeug.local.LocalProxy pour être précis) pour créer votre propre fil conducteur local qui agit comme g , current_app y request ( entre autres ).

15voto

jpmc26 Points 3364

Le premier a le problème de l'acquisition de connexions même lorsqu'elles ne sont pas nécessaires. La seconde a l'inconvénient de jouer avec les internes d'un framework tiers, et elle est assez illisible.

Des deux seuls, le second est probablement le meilleur choix. Non seulement il n'acquiert pas de connexion pour les routes qui n'en ont pas besoin, mais il n'en acquiert pas non plus si vous empruntez un chemin de code qui n'en a pas besoin, même si d'autres chemins de code dans la route en requièrent un. (Par exemple, si vous avez une validation de formulaire, vous n'avez besoin de la connexion que si la validation est réussie ; cela n'en ouvrira pas une si la validation échoue). Avec cette configuration, vous n'acquérez les connexions que juste avant de les utiliser.

Cependant, vous pouvez éviter de toucher à l'interne et bénéficier de tous ces avantages. Personnellement, j'ai créé mes propres petites méthodes globales :

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

Ensuite, tout au long de l'application, obtenez toujours votre connexion via get_request_connection comme vous le feriez pour votre get_db fonction. Simplicité et haute efficacité. En fait, le meilleur des deux mondes.

Edita:

Rétrospectivement, je n'aime vraiment pas le fait qu'il s'agisse de méthodes globales, mais je pense que la raison en est que c'est ainsi que fonctionne Flask : il vous donne des "globales" qui pointent en fait vers des thread-locals.

7voto

arboc7 Points 1816

Je recommande Flask-SQLAlchemy qui étend SQLAlchemy pour l'utiliser dans Flask, et qui prend en charge de nombreuses bases de données différentes. (Exemple tiré de la documentation de Flask-SQLAlchemy)

Mise en place :

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

Maintenant, vous pouvez simplement importer/utiliser le User pour accéder à la table User de votre base de données.

Créer de nouveaux utilisateurs :

>>> from yourapplication import User
>>> admin = User('admin', 'admin@example.com')
>>> guest = User('guest', 'guest@example.com')

Ajoutez les utilisateurs à la base de données :

>>> db.session.add(admin)
>>> db.session.add(guest)
>>> db.session.commit()

Recherche des utilisateurs déjà présents dans la base de données :

>>> users = User.query.all()
[<User u'admin'>, <User u'guest'>]
>>> admin = User.query.filter_by(username='admin').first()
<User u'admin'>

1 votes

Vous avez raison. Je pense que j'ai également supposé qu'une réponse devait indiquer quand il fallait également déconnecter/fermer la connexion. Comment procéder pour mettre en œuvre une connexion/déconnexion ? Method One: Using special g object OU Method Two: Using Mysterious _app_ctx_stack .

1 votes

@jberger Excellent point ; ma réponse ne répond pas directement à la question initiale de savoir laquelle des deux approches est la meilleure, mais fournit une troisième option entièrement différente.

4voto

Robert Lujo Points 2743

Je choisirais la première méthode, plus lisible et moins "bricolée".

La méthode 2 est probablement conçue pour l'intégration des extensions flask ( exemple y explication de l'app-ctx-stack ). Bien qu'elles aient probablement un effet très similaire, la première méthode doit être utilisée pour les cas normaux.

0voto

tanveer Points 31

Pour conserver la connexion à la base de données dans la même session flask, vous pouvez utiliser le contexte d'application et assigner la connexion à la base de données. Si la connexion est rompue, le contexte essaiera de rétablir la connexion en interrogeant continuellement l'objet de connexion.

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

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X