web-dev-qa-db-fra.com

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

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

Méthode deux: utilisation de Mysterious _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?

56
Gaby Solis

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 alors que la méthode deux crée uniquement la connexion 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 appuyez sur / en utilisant le @app.before_request installer (g.db) vous obtenez une connexion que vous l'utilisiez ou non , tout en utilisant le _app_context route vous obtenez uniquement une connexion lorsque vous appelez get_db.

Pour être honnête, vous pouvez également ajouter un descripteur à g qui fera la même connexion paresseuse (ou dans la vraie vie, acquérir une connexion à partir d'un pool de connexions). Et dans les deux cas, vous pouvez utiliser un peu plus de magie (werkzeug.local.LocalProxy pour être précis) pour créer votre propre --- thread local qui agit comme g, current_app et request ( entre autres ).

34
Sean Vieira

Le premier a le problème d'acquérir des connexions même lorsqu'elles ne sont pas nécessaires. Le second a l'inconvénient de jouer avec les composants internes d'un cadre tiers, en plus c'est assez illisible.

Des deux seuls, le second est probablement le meilleur choix. Non seulement il n'acquiert pas de connexion pour les itinéraires qui n'en ont pas besoin, mais il n'acquiert pas de connexion si vous descendez un chemin de code qui n'en a pas besoin, même si d'autres chemins de code de l'itinéraire en nécessitent un. (Par exemple, si vous avez une validation de formulaire, vous n'avez besoin de la connexion que si la validation réussit; cela ne l'ouvrira pas lorsque la validation échoue.) Vous acquérez uniquement des connexions juste avant de les utiliser avec cette configuration.

Cependant, vous pouvez éviter de jouer avec les internes 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, dans toute l'application, obtenez toujours votre connexion via get_request_connection, comme vous le feriez pour votre get_db une fonction. Efficacité simple et élevée. Fondamentalement, le meilleur des deux mondes.

Modifier:

Rétrospectivement, je n'aime vraiment pas le fait que ce soient des méthodes globales, mais je pense que la raison en est que c'est ainsi que fonctionne Flask: cela vous donne des "globaux" qui pointent en fait vers des locals de threads.

12
jpmc26

Je recommande Flask-SQLAlchemy , qui étend SQLAlchemy pour une utilisation dans Flask, donc il prend en charge de nombreuses bases de données différentes. (Exemple tiré de la documentation Flask-SQLAlchemy)

Installer:

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 la classe User pour accéder à la table User dans votre base de données.

Créez de nouveaux utilisateurs:

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

Ajoutez les utilisateurs à la base de données:

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

Requête pour les utilisateurs déjà 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'>
7
arboc7

J'irais avec la première méthode - plus lisible et moins "hackish".

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

4
Robert Lujo