Ho un'applicazione Flask in esecuzione che è impostata in base a una combinazione delle migliori pratiche che abbiamo trovato online e nel libro "Flask Web Development" di Miguel Grinberg.Condividere i modelli sqlalchemy tra il pallone e altre app
Ora abbiamo bisogno di una seconda applicazione Python, che NON sia un'app Web, e che abbia bisogno di accedere agli stessi modelli dell'applicazione Flask. Volevamo riutilizzare gli stessi modelli di corso, in modo che entrambe le app possano beneficiare del codice condiviso.
Abbiamo rimosso le dipendenze dall'estensione di flask-sqlalchemy (che abbiamo usato prima, quando avevamo solo l'applicazione Flask). E lo ha sostituito con lo SQLalchemy Declarative extension described here, che è un po 'più semplice (Flask-SQLalchemy adds a few specific things to standard SQLAlchemy)
In linea con l'esempio, abbiamo creato un file database.py nella radice. Nel nostro caso ci sono due cose diverse dall'esempio di estensione dichiarativa: metto il motore e la sessione in una classe, perché tutti i nostri modelli usano db.session, invece di db_session, e io passo un dizionario con i valori di configurazione all'iniziativa (), in modo da poter riutilizzare questo database.py sia da Flask che da un'altra applicazione, utilizzando una configurazione diversa. assomiglia a questo:
from sqlalchemy import create_engine
from sqlalchemy.orm import scoped_session, sessionmaker
from sqlalchemy.ext.declarative import declarative_base
class Database(object):
def __init__(self, cfg):
self.engine = create_engine(cfg['SQLALCHEMY_DATABASE_URI'], convert_unicode=True)
self.session = scoped_session(sessionmaker(autocommit=False, autoflush=False, bind=self.engine))
class Model(object):
pass
Base = declarative_base()
Così ora veniamo al problema reale. Flask crea un oggetto simile al dizionario che contiene le opzioni di configurazione e le aggiunge come proprietà all'istanza dell'app. Li carica da un instance folder, un config.py nella radice del sito e dalle variabili di ambiente. Ho bisogno di passare il dizionario di configurazione da Flask, quindi ho bisogno di Flask su FIRST caricare e assemblare la configurazione, e dopo che inizializzare il database, e avere un oggetto db (configurato) nella root del file app. Tuttavia, seguiamo lo Application factory pattern, quindi possiamo utilizzare diverse configurazioni per situazioni diverse (test, produzione, sviluppo).
Questo significa che il nostro app/__init__.py
sembra qualcosa di simile (semplificato):
from flask import Flask
from database import Database
from flask.ext.mail import Mail
from flask_bcrypt import Bcrypt
from config import config
mail = Mail()
bcrypt = Bcrypt()
def create_app(config_name):
app = Flask(__name__, instance_relative_config=True)
if not config_name:
config_name = 'default'
app.config.from_object(config[config_name])
app.config.from_pyfile('config.py')
config[config_name].init_app(app)
db = Database(app.config)
mail.init_app(app)
bcrypt.init_app(app)
@app.teardown_appcontext
def shutdown_session(exception=None):
db.session.remove()
from main import main as main_blueprint
app.register_blueprint(main_blueprint)
return app
Ma il db (che i modelli importazione da ..), ora ha bisogno di essere all'interno della funzione create_app(), perché è lì che Flask carica la configurazione. Se istanzassi l'oggetto db al di fuori della funzione create_app(), sarà importabile dai modelli, ma non è configurato!
un modello di esempio si presenta, e come si può vedere, si aspetta un "db" nella root del app:
from . base_models import areas
from sqlalchemy.orm import relationship, backref
from ..utils.helper_functions import newid
from .. import db
class Areas(db.Model, areas):
"""Area model class.
"""
country = relationship("Countries", backref=backref('areas'))
def __init__(self, *args, **kwargs):
self.area_id = newid()
super(Areas, self).__init__(*args, **kwargs)
def __str__(self):
return u"{}".format(self.area_name).encode('utf8')
def __repr__(self):
return u"<Area: '{}'>".format(self.area_name).encode('utf8')
Quindi la mia domanda è: come posso avere un DB di istanza che può essere configurato esternamente (da Flask o da un'altra app), e utilizzare ancora il Pattern di fabbrica dell'applicazione?
modifica: L'esempio di codice non era corretto, aveva un'importazione per Flask-SQLalchemy che è stata sostituita da from database import Database
. Ci scusiamo per qualsiasi confusione.
La funzione teardown che chiama 'db.session.remove' non è necessaria e può effettivamente causare problemi. – davidism
La funzione di teardown è di tipo nessario usando il metodo dichiarativo, secondo la documentazione di Flask: http://flask.pocoo.org/docs/0.10/patterns/sqlalchemy/ –
Poiché si tratta di una sessione con ambito, è ridondante. Grazie per averlo sottolineato, ho bisogno di correggere quei documenti. – davidism