Estoy tratando de averiguar cómo hacer que las clases de SQLAlchemy se distribuyan en varios archivos y, por mi vida, no puedo averiguar cómo hacerlo. Soy bastante nuevo en SQLAlchemy, así que perdóname si esta pregunta es trivial.
Considere estas 3 clases en cada uno su propio archivo :
A.py:
from sqlalchemy import *
from main import Base
class A(Base):
__tablename__ = "A"
id = Column(Integer, primary_key=True)
Bs = relationship("B", backref="A.id")
Cs = relationship("C", backref="A.id")
B.py:
from sqlalchemy import *
from main import Base
class B(Base):
__tablename__ = "B"
id = Column(Integer, primary_key=True)
A_id = Column(Integer, ForeignKey("A.id"))
C.py:
from sqlalchemy import *
from main import Base
class C(Base):
__tablename__ = "C"
id = Column(Integer, primary_key=True)
A_id = Column(Integer, ForeignKey("A.id"))
Y luego digamos que tenemos un main.py algo como esto:
from sqlalchemy import create_engine
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.orm import relationship, backref, sessionmaker
Base = declarative_base()
import A
import B
import C
engine = create_engine("sqlite:///test.db")
Base.metadata.create_all(engine, checkfirst=True)
Session = sessionmaker(bind=engine)
session = Session()
a = A.A()
b1 = B.B()
b2 = B.B()
c1 = C.C()
c2 = C.C()
a.Bs.append(b1)
a.Bs.append(b2)
a.Cs.append(c1)
a.Cs.append(c2)
session.add(a)
session.commit()
Lo anterior da el error:
sqlalchemy.exc.NoReferencedTableError: Foreign key assocated with column 'C.A_id' could not find table 'A' with which to generate a foreign key to target column 'id'
¿Cómo comparto la base declarativa entre estos archivos?
¿Cuál es la forma "correcta" de lograr esto, considerando que podría arrojar algo como Pylons o Turbogears encima de esto?
editar 10-03-2011
Encontré esta descripción del marco de Pyramids que describe el problema y, lo que es más importante, verifica que este es un problema real y no (solo) mi yo confundido, ese es el problema. Espero que pueda ayudar a otros que se atreven por este camino peligroso :)
fuente
Respuestas:
La solución más sencilla a su problema será llevar
Base
a cabo del módulo que las importacionesA
,B
yC
; Rompe la importación cíclica.base.py
from sqlalchemy.ext.declarative import declarative_base Base = declarative_base()
a.py
from sqlalchemy import * from base import Base from sqlalchemy.orm import relationship class A(Base): __tablename__ = "A" id = Column(Integer, primary_key=True) Bs = relationship("B", backref="A.id") Cs = relationship("C", backref="A.id")
b.py
from sqlalchemy import * from base import Base class B(Base): __tablename__ = "B" id = Column(Integer, primary_key=True) A_id = Column(Integer, ForeignKey("A.id"))
c.py
from sqlalchemy import * from base import Base class C(Base): __tablename__ = "C" id = Column(Integer, primary_key=True) A_id = Column(Integer, ForeignKey("A.id"))
main.py
from sqlalchemy import create_engine from sqlalchemy.orm import relationship, backref, sessionmaker import base import a import b import c engine = create_engine("sqlite:///:memory:") base.Base.metadata.create_all(engine, checkfirst=True) Session = sessionmaker(bind=engine) session = Session() a1 = a.A() b1 = b.B() b2 = b.B() c1 = c.C() c2 = c.C() a1.Bs.append(b1) a1.Bs.append(b2) a1.Cs.append(c1) a1.Cs.append(c2) session.add(a1) session.commit()
Funciona en mi máquina:
$ python main.py ; echo $? 0
fuente
scoped_session
.scoped_session
, a menos que sepa por qué necesita almacenamiento local de subprocesos; El problema con el usoscoped_session
es que hace que todo sea demasiado fácil para terminar con transacciones filtradas y datos obsoletos, sin un vínculo explícito al punto en su código cuando eso podría haber sucedido.Si puedo agregar mi sentido común también, ya que tuve el mismo problema. Necesita importar las clases en el archivo donde crea el
Base = declarative_base()
DESPUÉS de que creó elBase
y elTables
. Breve ejemplo de cómo se configura mi proyecto:model / user.py
from sqlalchemy import * from sqlalchemy.orm import relationship from model import Base class User(Base): __tablename__ = 'user' id = Column(Integer, primary_key=True) budgets = relationship('Budget')
model / budget.py
from sqlalchemy import * from model import Base class Budget(Base): __tablename__ = 'budget' id = Column(Integer, primary_key=True) user_id = Column(Integer, ForeignKey('user.id'))
modelo / __ init__.py
from sqlalchemy import create_engine from sqlalchemy.ext.declarative import declarative_base from sqlalchemy.orm import sessionmaker _DB_URI = 'sqlite:///:memory:' engine = create_engine(_DB_URI) Base = declarative_base() Base.metadata.create_all(engine) DBSession = sessionmaker(bind=engine) session = DBSession() from .user import User from .budget import Budget
fuente
Estoy usando Python 2.7 + Flask 0.10 + SQLAlchemy 1.0.8 + Postgres 9.4.4.1
Esta plantilla viene configurada con un usuario y modelos UserDetail almacenados en el mismo archivo "modelos.py" en el módulo "usuario". Ambas clases heredan de una clase base SQLAlchemy.
Todas las clases adicionales que agregué a mi proyecto también se derivaron de esta clase base, y a medida que el archivo models.py creció, decidí dividir el archivo models.py en un archivo por clase y encontré el problema descrito aquí.
La solución que encontré, en la misma línea que la publicación de @ computermacgyver del 23 de octubre de 2013, fue incluir todas mis clases en el archivo init .py del nuevo módulo que creé para contener todos los archivos de clases recién creados. Se ve como esto:
/project/models/ __init__.py contains from project.models.a import A from project.models.b import B etc...
fuente
Para mi, agregando por
import app.tool.tool_entity
dentroapp.py
y porfrom app.tool.tool_entity import Tool
dentrotool/__init__.py
fue suficiente para crear la tabla. Sin embargo, todavía no he intentado agregar una relación.Estructura de carpetas:
# app/tool/tool_entity.py from app.base import Base from sqlalchemy import Column, Integer, String class Tool(Base): __tablename__ = 'tool' id = Column(Integer, primary_key=True) name = Column(String, nullable=False) fullname = Column(String) fullname2 = Column(String) nickname = Column(String) def __repr__(self): return "<User(name='%s', fullname='%s', nickname='%s')>" % ( self.name, self.fullname, self.nickname)
# app/tool/__init__.py from app.tool.tool_entity import Tool
# app/app.py from flask import Flask from sqlalchemy import create_engine from app.tool.tool_routes import tool_blueprint from app.base import Base db_dialect = 'postgresql' db_user = 'postgres' db_pwd = 'postgrespwd' db_host = 'db' db_name = 'db_name' engine = create_engine(f'{db_dialect}://{db_user}:{db_pwd}@{db_host}/{db_name}', echo=True) Base.metadata.create_all(engine) app = Flask(__name__) @app.route('/') def hello_world(): return 'hello world' app.register_blueprint(tool_blueprint, url_prefix='/tool') if __name__ == '__main__': # you can add this import here, or anywhere else in the file, as debug (watch mode) is on, # the table should be created as soon as you save this file. import app.tool.tool_entity app.run(host='0.0.0.0', port=5000, debug=True)
fuente