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?
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 ).
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.
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.
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'>
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.