Recientemente comencé a trabajar en Flask y Flask-SQLAlchemy. Viniendo de los antecedentes de Django, encontré que Flask-SQLAlchmey es bastante complejo. He leído que SQLAlchemy implementa el patrón Data Mapper mientras que Django ORM se basa en Active Record Pattern.
Aquí hay un código de muestra escrito que implementa un patrón de repositorio para acceder a la base de datos.
Aquí hay otro enlace de un comentario de S. Lott (reputación de 271k) que dice que ORM es la capa de acceso a datos y está separada del modelo.
Mis preguntas son estas:
Question.query.filter_by(text = text).all() que db.session.query(Question).filter(Question.text == text).all() ?Este no es un duplicado del patrón DataMapper vs ActiveRecord porque esto solo dice la definición, estoy más interesado en los ejemplos prácticos.
Punto por punto.
Tengo una base de datos heredada para la que tengo que escribir algunas utilidades de manejo de datos. El uso del patrón Mapper, sin el estilo ORM/ActiveRecord, me facilitó las cosas al escribir consultas como lo haría ActiveRecord. Está operando en agradables objetos componibles que se asemejan a las cláusulas de SQL, protegidos de las inyecciones de SQL.
Los objetos que son "pasivos" permitieron una mayor flexibilidad / uniformidad: el resultado de una combinación compleja es una tupla con nombre, como resultado de una selección simple. No hay identidad de la que preocuparse, no hay objetos almacenados en caché con la misma identidad.
Todas las actualizaciones son explícitas; no es un "guardado" de algún estado alterado en otro lugar, no hay ganchos ejecutándose en .save() , etc. Esto hizo que las actualizaciones por lotes eficientes fueran triviales, sin problemas si se envían los datos correctos a la base de datos. Ambos fueron beneficios en mi caso. En general, 'depende'. Por ejemplo, tuve que buscar manualmente los ID generados por la base de datos después de las inserciones. Ejecutar esta consulta explícitamente es un poco de trabajo extra. Poder hacer eso en una consulta en lugar de una por registro fue una gran ayuda en mi caso.
SQLAlchemy tiene un diseño en capas que le permite acceder al nivel inferior de "mapeador" incluso si declara cosas en el nivel superior de ORM y normalmente opera en él. En Django, por ejemplo, no es tan sencillo si/cuando todavía es posible.
En el ejemplo, el 'repositorio' parece un nivel construido sobre el 'mapeador'. El repositorio podría haberse construido sobre DBAPI simple, pero el mapeador simplifica algunas cosas, como un enlace de parámetros más agradable, tuplas con nombre para los conjuntos de resultados y un contenedor sobre SQL simple con partes componibles y reutilizables.
El mapeador también proporciona un cierto grado de independencia de la base de datos. Por ejemplo, SQL Server y Postgres tienen diferentes formas de concatenar cadenas; el mapeador proporciona una interfaz unificada.
Escribes tu select donde la usas. Si tiene una selección que reutiliza constantemente en diferentes contextos, puede ponerla en un método o función. La mayoría de los selectos tienen un solo uso y se construyen en el lugar.
Una buena característica del diseño de SQLAlchemy es que puede almacenar fácilmente condiciones y cláusulas where completas y reutilizarlas en declaraciones de selección/actualización/eliminación.
Question.query.filter_by(text = text).all() usa una transacción implícita. db.session.query(Question).filter(Question.text == text).all() usa una transacción explícita.
Las transacciones explícitas le brindan tranquilidad con DML. También son importantes con los select s, cuando está consultando una base de datos que cambia rápidamente y desea que sus varios select s relacionados vean el mismo estado consistente.
Por lo general, escribo un envoltorio trivial alrededor sessionmaker y escribo cosas como esta:
with my_database.transaction() as trans: records = trans.query(...) ... updated = trans.execute(...).rowcount # Here the transaction commits if all went well. Cuando definitivamente sé que no se debe ejecutar DML en este bloque, uso .readonly_transaction() que siempre retrocede.
En muchos casos, la transacción implícita está bien. Django te permite decorar un método con @transaction.atomic y tener un control de transacción semiexplícito, suficiente en el 99% de los casos. Pero a veces necesita una granularidad aún más fina.
Completamente de acuerdo con la respuesta anterior: sí, el patrón Data Mapper de SQLAlchemy es realmente más flexible y para consultas complejas es realmente más poderoso, menos mágico y más controlado.
Pero, en tareas simples como CRUD SQLAlchemy, el código se vuelve demasiado pesado/excesivo/redundante.
Por ejemplo, para simplemente crear algún objeto en el controlador "crear" más simple, necesita algo como esto:
user = User(name='Nick', surname='Nickson') session.add(user) session.flush()Mientras esté en Active Record ORM, solo necesitará una sola cadena.
Bueno, para tareas simples , algunos de nosotros podemos querer algo más simple. Quiero decir que será genial tener Active Record para SQLAlchemy.
Buenas noticias: recientemente creé un paquete para esto (también contiene otras cosas útiles).
Échale un vistazo: https://github.com/absent1706/sqlalchemy-mixins
La única razón por la que usaría Data Mapper sobre Active Record es si tiene serios problemas de escalabilidad. Data Mapper fomenta la separación de los objetos de dominio y la lógica de acceso a la base de datos, mientras que Active Records coloca la lógica de acceso a la base de datos en el objeto de dominio. Por ejemplo, cuando levante la instancia de Flask, se conectará a la base de datos solo a pedido, mientras que en Django siempre estará conectado a la base de datos.
Data Mapper aísla los objetos de dominio de la lógica de acceso a la base de datos, mientras que el patrón de repositorio es una capa entre los objetos de dominio y Data Mapper. Es un nivel más alto que Data Mapper. Por ejemplo, en el patrón de Data Mapper tendrá captadores y definidores directos, en el patrón de repositorio tendrá captadores y definidores que también pueden contener una lógica empresarial compleja.
El asignador de datos está separado de la clase modelo. Solo el patrón Active Record une a los getters y setters en la misma clase.
He trabajado con SQLAlchemy y Django por un tiempo y definitivamente prefiero las consultas tipo Django. Para mis propios proyectos, la probabilidad de que use Flask + SQLAlchemy sobre Django es casi nula. La productividad y la comunidad son los dos factores más decisivos al considerar estos dos marcos.