it-swarm.com.de

Stellen Sie eine Verbindung zu einer Datenbank in Flask her. Welcher Ansatz ist besser?

Methode 1: Verwenden eines speziellen g-Objekts aus http://flask.pocoo.org/docs/tutorial/dbcon/ und 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()

Methode 2: Verwenden von Mysterious _app_ctx_stack aus 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()

Welche Methode ist besser? Was ist der Unterschied?

56
Gaby Solis

Der Unterschied zwischen den beiden besteht darin, dass Methode 1 eine Verbindung auf g.db Erstellt, unabhängig davon, ob Sie sie benötigen oder nicht, während Methode 2 die Verbindung nur dann erstellt, wenn Sie get_db Zum ersten Mal in diesem Anwendungskontext aufrufen.

Wenn Sie die beiden vergleichen, verwenden Sie dieses 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)

Sie werden sehen, dass Sie, wenn Sie / Mit dem Setup @app.before_request (g.db) Drücken, eine Verbindung erhalten , unabhängig davon, ob Sie sie verwenden oder nicht , bei Verwendung der Route _app_context erhalten Sie nur dann eine Verbindung , wenn Sie get_db aufrufen .

Um fair zu sein, können Sie g auch einen Deskriptor hinzufügen, der die gleiche langsame Verbindung herstellt (oder im wirklichen Leben eine Verbindung aus einem Verbindungspool abruft). Und in beiden Fällen können Sie etwas mehr Magie (um genau zu sein werkzeug.local.LocalProxy) Verwenden, um Ihren eigenen benutzerdefinierten Thread zu erstellen local das verhält sich wie g, current_app und request ( nter anderem ).

34
Sean Vieira

Der erste hat das Problem, Verbindungen zu erhalten, auch wenn sie nicht benötigt werden. Der zweite hat den Nachteil, mit Interna eines Drittanbieter-Frameworks zu spielen, außerdem ist er ziemlich unlesbar.

Von den beiden allein ist wahrscheinlich die zweite die bessere Wahl. Es wird nicht nur keine Verbindung für Routen hergestellt, die keine benötigen, sondern es wird auch keine Verbindung hergestellt, wenn Sie einen Codepfad abfahren, für den keine benötigt werden, auch wenn für andere Codepfade in der Route eine erforderlich ist. (Wenn Sie beispielsweise über eine Formularvalidierung verfügen, benötigen Sie die Verbindung nur, wenn die Validierung bestanden wurde. Wenn die Validierung fehlschlägt, wird die Verbindung nicht geöffnet.) Sie erwerben erst Verbindungen, bevor Sie sie mit dieser Einrichtung verwenden.

Sie können jedoch vermeiden, sich mit den Einbauten herumzuschlagen, und trotzdem alle diese Vorteile nutzen. Persönlich habe ich meine eigenen kleinen globalen Methoden entwickelt:

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

Erhalte dann während der gesamten App immer deine Verbindung über get_request_connection, genau wie du dein get_db Funktion. Einfache und hohe Effizienz. Grundsätzlich das Beste aus beiden Welten.

Bearbeiten:

Rückblickend mag ich die Tatsache nicht, dass es sich um globale Methoden handelt, aber ich denke, der Grund dafür ist, dass Flask) so funktioniert: Sie erhalten "Globals", die tatsächlich auf Thread-Locals verweisen.

12
jpmc26

Ich empfehle Flask-SQLAlchemy , das SQLAlchemy für die Verwendung in Flask erweitert, sodass es viele verschiedene Datenbanken unterstützt. (Beispiel aus der Flask-SQLAlchemy-Dokumentation)

Installieren:

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

Jetzt können Sie einfach die Klasse User importieren/verwenden, um auf die Benutzertabelle in Ihrer Datenbank zuzugreifen.

Erstellen Sie neue Benutzer:

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

Fügen Sie die Benutzer zur Datenbank hinzu:

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

Abfrage für Benutzer, die sich bereits in der Datenbank befinden:

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

Ich würde mit Methode eins gehen - lesbarer und weniger "hackisch".

Die Methode 2 ist wahrscheinlich für die flask Erweiterungsintegration ( Beispiel und Erklärung von app-ctx-stack ) vorgesehen In ähnlicher Weise sollte Methode 1 für den Normalfall angewendet werden.

4
Robert Lujo