python - query - Comparte modelos sqlalchemy entre matraz y otras aplicaciones.
sqlalchemy flask oracle (4)
Tengo una aplicación Flask en ejecución que se configura de acuerdo con una combinación de las mejores prácticas que encontramos en línea y en el libro "Flask Web Development" de Miguel Grinberg.
Ahora necesitamos una segunda aplicación de Python, que NO es una aplicación web, y que necesita acceso a los mismos modelos que la aplicación Flask. Queríamos reutilizar los mismos modelos de curso, por lo que ambas aplicaciones pueden beneficiarse del código compartido.
Hemos eliminado las dependencias de la extensión flask-sqlalchemy (que utilizamos antes, cuando solo teníamos la aplicación Flask). Y lo reemplazó con la extensión declarativa SQLalchemy descrita aquí , que es un poco más simple ( Flask-SQLalchemy agrega algunas cosas específicas a SQLAlchemy estándar )
En línea con el ejemplo, hemos creado un archivo database.py en la raíz. En nuestro caso, hay dos cosas diferentes del ejemplo de extensión declarativa: coloco el motor y la sesión en una clase, porque todos nuestros modelos usan db.session, en lugar de db_session, y le paso un diccionario con valores de configuración al inicio ( ) , de modo que puedo reutilizar este database.py tanto de Flask como de otra aplicación, usando una configuración diferente. se parece a esto:
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()
Así que ahora llegamos al problema real. Flask crea un objeto similar a un diccionario que contiene opciones de configuración y los agrega como una propiedad a la instancia de la aplicación. Los carga desde una carpeta de instancia , un config.py en la raíz del sitio y desde las variables de entorno. Necesito pasar el diccionario de configuración de Flask, así que necesito que Flask PRIMERO cargue y arme la configuración, y luego inicie la base de datos y tenga un objeto db (configurado) en la raíz del archivo de la aplicación. Sin embargo, seguimos el patrón de fábrica de aplicaciones , por lo que podemos usar diferentes configuraciones para diferentes situaciones (prueba, producción, desarrollo).
Esto significa que nuestra app/__init__.py
parece a esto (simplificado):
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
Pero la base de datos (que importan los modelos desde ..), ahora debe estar dentro de la función create_app (), porque ahí es donde Flask carga la configuración. Si quisiera crear una instancia del objeto db fuera de la función create_app (), será importable desde los modelos, ¡pero no está configurado!
un modelo de ejemplo se ve así, y como puede ver, espera una "db" en la raíz de la aplicación:
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'')
Entonces, mi pregunta es, ¿cómo puedo tener una instancia de db que pueda configurarse externamente (ya sea por Flask u otra aplicación), y seguir utilizando el Patrón de Fábrica de Aplicaciones?
edición: el código del ejemplo era incorrecto, tenía una importación para Flask-SQLalchemy que fue reemplazado por la from database import Database
Lo siento por cualquier confusión.
La extensión Flask-SQLAlchemy, como la mayoría de las extensiones Flask, debe crearse fuera de la fábrica y luego inicializarse en la fábrica utilizando init_app
. Esto es para que pueda usar el objeto db
antes de crear una aplicación.
db = SQLAlchemy()
def create_app():
app = Flask(__name__)
db.init_app(app)
return app
Su aplicación Flask, como cualquier proyecto de Python correctamente diseñado, debe ser un paquete instalable. Esto es simple de hacer: asegúrese de que el diseño de su proyecto tenga sentido, luego agregue un archivo setup.py
básico.
project/
my_flask_package/
__init__.py # at the most basic, this contains create_app and db
setup.py
from setuptools import setup, find_packages
setup(
name=''my_flask_package'',
version=''1.0'',
packages=find_packages(),
install_requires=[''flask'', ''flask-sqlalchemy''],
)
$ python setup.py sdist
Ahora puede instalar su aplicación Flask, junto con su base de datos, para usar en otros proyectos. Instálela e impórtela en el virtualenv de su segundo proyecto, luego cree y presione una aplicación para inicializarla.
$ pip install my_flask_package-1.0.tar.gz
from my_flask_package import db, create_app
create_app().app_context().push()
db.session.query(...)
Si le preocupa la sobrecarga que implica la creación de su aplicación, puede agregar argumentos a la función create_app
para controlar qué se inicializa. Sin embargo, para la mayoría de los casos esto no debería ser un problema.
Para otras personas aventurándose en esta dirección. Hay una buena publicación en el blog y un enlace a una biblioteca que ofrece ventajas similares a Flask-SQLAlchemy, sin vincular SQLAlchemy a Flask directamente.
Una advertencia sin embargo; He intentado usar Alchy, pero aún no lograba comprender cómo integrarlo en Flask y en una aplicación que no sea de web, así que decidí responder con la respuesta aceptada del davidismo. Su experiencia puede ser diferente.
Tuve el mismo problema.
Si activa "SQLALCHEMY_ECHO" probablemente verá que se inicia una nueva transacción pero falta el COMMIT / ROLLBACK correspondiente.
Por lo que descubrí, tiene algo que ver con dos instancias de SQLAlchemy que también creas, una vez en tu archivo modelo y una vez en tu web.py. Lo más probable es que se deba a que interactúa con la sesión de su web.py y si consulta sus modelos, hay un cambio de contexto que recibirá el COMPROMISO.
Solucioné el problema importando "db" de los modelos y luego lo inicié llamando a db.init_app (aplicación). De acuerdo con los registros, cometer ahora funciona bien.
El @app.teardown_appcontext
no debería ser necesario ya que está configurado en la clase SQLAlchemy de Flask-SQLAlchemy ( https://github.com/mitsuhiko/flask-sqlalchemy/blob/master/flask_sqlalchemy/init.py )
Usted puede compartir fácilmente. Te mostraré cómo. Teniendo en cuenta esta aplicación matraz:
.
├── config.py
├── db
│ └── test.db
├── do_somenthing2.py ============> Here is run some script 2
├── do_something.py ============> Here is run some script
├── machinelearning
│ ├── models
│ │ ├── restore.py
│ │ ├── train.py
│ │ └── utils.py
│ └── save
│ └── test.ckpt
├── runserver.py ============> Here is run your app
├── test.py
└── web
├── __init__.py
├── api
│ ├── __init__.py
│ ├── app.py ============> Here is app = Flask(__name__)
│ ├── client.py
│ ├── models.py ==========> Here is db = SQLAlchemy(app)
│ ├── sample.json
│ └── utils.py
└── frontend
├── __init__.py
└── routes.py
runserver.py
import os
from config import DEBUG
from web.api.app import app
from web.api.client import *
if __name__ == "__main__":
app.run(debug=DEBUG)
DE ACUERDO. Ahora quieres usar los mismos modelos para hacer otra cosa. Por ejemplo: entrene una máquina, sirva y guarde en la base de datos (ORM) utilizando los mismos modelos.
Puede importar la aplicación y usar app.test_request_context (). Como eso:
do_something.py
desde web.api.app importar aplicación desde web.api.models importar db, usuario
def do_something():
q = db.session.query(User)/
.filter(User.Name.ilike(''Andre''))
for i in q.all():
print (i.Name)
with app.test_request_context():
do_something()
do_something2.py (ejemplo real)
from web.api.app import app
from web.api.models import *
def save(df):
passengers = []
records = df.to_dict(''records'')
for row in records:
p = Passenger(row)
passengers.append(p)
for p in passengers:
db.session.add(p)
db.session.commit()
from ml.models import train, restore
with app.test_request_context():
print (''Trainning model. It will take a while... (~ 5 minutos)'')
train.run()
print (''Saving model...'')
save(restore.run())
print (''Saved!'')
Muchas respuestas recomiendan su uso (Importar archivos desde una carpeta diferente):
import sys
sys.path.append(''../'')
Pero no estoy de acuerdo cuando tienes una aplicación Flask y otros scripts, porque te volverás loco resolviendo las referencias relativas.
El enfoque para instalar su aplicación Flask, junto con su base de datos, para usar en otros proyectos, es otra opción.
Aquí puede encontrar una documentación sobre los paquetes y módulos .
Los paquetes son una forma de estructurar el espacio de nombres de los módulos de Python mediante el uso de "nombres de módulos punteados". Por ejemplo, el nombre de módulo AB designa un submódulo llamado B en un paquete llamado A. Así como el uso de módulos evita que los autores de diferentes módulos tengan que preocuparse por los nombres de las variables globales de cada uno, el uso de nombres de módulos de puntos ahorra a los autores de paquetes de múltiples módulos como NumPy o Pillow por tener que preocuparse por los nombres de los módulos de los demás.