python - query - select sqlalchemy
Usar Flask-SQLAlchemy en modelos Blueprint sin referencia a la aplicaciĆ³n (3)
Creo que la respuesta más verdadera es que los planos modulares no deberían preocuparse directamente por el acceso a los datos, sino que deben confiar en que la aplicación proporcione una implementación compatible.
Entonces, dado su modelo de ejemplo.
from flask import current_app, Blueprint, render_template
flat_pages = Blueprint(''flat_pages'', __name__, template_folder=''templates'')
@flat_pages.record
def record(state):
db = state.app.config.get("flat_pages.db")
if db is None:
raise Exception("This blueprint expects you to provide "
"database access through flat_pages.db")
@flat_pages.route(''/<page>'')
def show(page):
db = current_app.config["flat_pages.db"]
page_object = db.find_page_by_name(page)
return render_template(''pages/{}.html''.format(page), page=page_object)
A partir de esto, no hay nada que le impida proporcionar una implementación predeterminada.
def setup_default_flat_pages_db(db):
class Page(db.Model):
name = db.Column(db.String(255), primary_key=True)
title = db.Column(db.String(255))
content = db.Column(db.String(255))
def __init__(self, name, title, content):
self.name = name
self.title = title
self.content = content
class FlatPagesDBO(object):
def find_page_by_name(self, name):
return Page.query.filter_by(name=name).first()
return FlatPagesDBO()
Y en tu configuración
app.config["flat_pages.db"] = setup_default_flat_pages_db(db)
Lo anterior se puede hacer más limpio al no confiar en la herencia directa de db.Model y en su lugar solo usar una base declarativa de vainilla de sqlalchemy, pero esto debería representar lo esencial.
Intento crear una "aplicación modular" en Flask usando Blueprints.
Al crear modelos, sin embargo, me encuentro con el problema de tener que hacer referencia a la aplicación para obtener el objeto db
proporcionado por Flask-SQLAlchemy. Me gustaría poder usar algunos planos con más de una aplicación (similar a cómo se pueden usar las aplicaciones de Django), así que esta no es una buena solución. *
- Es posible realizar un cambio y hacer que el Blueprint cree la instancia de
db
, que la aplicación luego importa junto con el resto del blueprint. Pero luego, cualquier otro modelo que desee crear modelos necesita importar desde ese plano en lugar de la aplicación.
Mis preguntas son así:
- ¿Hay alguna manera de dejar que Blueprints defina los modelos sin que se tenga conocimiento de la aplicación en la que se están utilizando más adelante y que se unan varios Blueprints? Con esto, me refiero a tener que importar el módulo / paquete de la aplicación desde su Blueprint.
- ¿Estoy equivocado desde el principio? ¿Los Blueprints no están diseñados para ser independientes de la aplicación y ser redistribuibles (a la manera de las aplicaciones de Django)?
- Si no, ¿qué patrón debería usar para crear algo así? Extensiones de matraz ¿Debería simplemente no hacerlo, y tal vez centralizar todos los modelos / esquemas a la Ruby on Rails?
Editar : He estado pensando sobre esto yo mismo ahora, y esto podría estar más relacionado con SQLAlchemy que con Flask porque tienes que tener
declarative_base()
al declarar modelos. ¡Y eso tiene que venir de algún lado, de todos modos!Quizás la mejor solución es tener el esquema de su proyecto definido en un lugar y difundirlo, como lo hace Ruby on Rails. Las definiciones declarativas de las clases de SQLAlchemy son más parecidas a schema.rb que las de Django''s models.py. Imagino que esto también facilitaría el uso de las migraciones (desde alembic o sqlalchemy-migrate ).
Me pidieron que diera un ejemplo, así que hagamos algo simple: digamos que tengo un plan que describe las "páginas planas": contenido simple y "estático" almacenado en la base de datos. Utiliza una tabla con solo shortname (para URL), un título y un cuerpo. Esto es simple_pages/__init__.py
:
from flask import Blueprint, render_template
from .models import Page
flat_pages = Blueprint(''flat_pages'', __name__, template_folder=''templates'')
@flat_pages.route(''/<page>'')
def show(page):
page_object = Page.query.filter_by(name=page).first()
return render_template(''pages/{}.html''.format(page), page=page_object)
Entonces, sería bueno dejar que este plan defina su propio modelo (esto en simple_page/models.py
):
# TODO Somehow get ahold of a `db` instance without referencing the app
# I might get used in!
class Page(db.Model):
name = db.Column(db.String(255), primary_key=True)
title = db.Column(db.String(255))
content = db.Column(db.String(255))
def __init__(self, name, title, content):
self.name = name
self.title = title
self.content = content
Esta pregunta está relacionada con:
- Flask-SQLAlchemy problema de importación / contexto
- ¿Cuál es el diseño de tu carpeta para una aplicación Flask dividida en módulos?
Y varios otros, pero todas las respuestas parecen depender de importar la instancia de db
la aplicación, o hacer lo contrario. La página wiki " Aplicación grande" también utiliza el patrón "importar su aplicación en su plano".
* Dado que la documentación oficial muestra cómo crear rutas, vistas, plantillas y activos en un Blueprint sin importar en qué aplicación está "incorporada", he supuesto que Blueprints debería, en general, ser reutilizable en todas las aplicaciones. Sin embargo, esta modularidad no parece tan útil sin tener también modelos independientes.
Dado que Blueprints se puede conectar a una aplicación más de una vez, ¿podría ser simplemente el enfoque incorrecto tener modelos en Blueprints?
Tengo necesidades similares de hacer Blueprints completamente modular y no tener ninguna referencia a la aplicación. Se me ocurrió una solución posiblemente limpia, pero no estoy seguro de qué tan correcta es y cuáles son sus limitaciones.
La idea es crear un objeto db
separado ( db = SQLAlchemy()
) dentro del blueprint y llamar a los init_app()
y create_all()
desde donde se crea la aplicación raíz.
Aquí hay algunos ejemplos de código para mostrar cómo se estructura el proyecto: La aplicación se llama jobs
y el plano se llama status
y se almacena dentro de la carpeta de planos.
blueprints.status.models.py
from flask_sqlalchemy import SQLAlchemy
db = SQLAlchemy() # <--- The db object belonging to the blueprint
class Status(db.Model):
__tablename__ = ''status''
id = db.Column(db.Integer, primary_key=True)
job_id = db.Column(db.Integer)
status = db.Column(db.String(120))
models.py
from flask_sqlalchemy import SQLAlchemy
db = SQLAlchemy() # <--- The db object belonging to the root app
class Job(db.Model):
__tablename__ = ''job''
id = db.Column(db.Integer, primary_key=True)
state = db.Column(db.String(120)
factory.py
from .blueprints.status.models import db as status_db # blueprint db
from .blueprints.status.routes import status_handler # blueprint handler
from .models import db as root_db # root db
from flask import Flask
def create_app():
app = Flask(__name__)
# Create database resources.
app.config[''SQLALCHEMY_DATABASE_URI''] = ''sqlite:////path/to/app.db''
root_db.init_app(app)
status_db.init_app(app) # <--- Init blueprint db object.
with app.app_context():
root_db.create_all()
status_db.create_all() # <--- Create blueprint db.
# Register blueprint routes.
app.register_blueprint(status_handler, url_prefix="/status")
return app
Lo probé con gunicorn
con gevent
worker y funciona. Hice una pregunta por separado sobre la solidez de la solución aquí: crea una instancia de SQLAlchemy por plano y llama a create_all varias veces
Usted preguntó "¿Los Blueprints no están diseñados para ser independientes de la aplicación y ser redistribuibles (a la manera de las aplicaciones de Django)?"
La respuesta es sí. Los planos no son similares a la aplicación Django.
Si desea utilizar diferentes aplicaciones / configuraciones, entonces necesita usar "Despacho de aplicaciones" y no planos. Lea esto [1]: http://flask.pocoo.org/docs/patterns/appdispatch/#app-dispatch [1]
Además, el enlace aquí [1] http://flask.pocoo.org/docs/blueprints/#the-concept-of-blueprints [1]
Dice claramente y cito "Un modelo en Flask no es una aplicación conectable porque no es realmente una aplicación, es un conjunto de operaciones que se pueden registrar en una aplicación, incluso varias veces. ¿Por qué no tener múltiples objetos de aplicación? hazlo (ver Despacho de la aplicación), pero tus aplicaciones tendrán configuraciones separadas y se administrarán en la capa de WSGI ".