関連する SQLAlchemy マップされたクラスを異なるモジュールに分散させる方法の問題は、ここで何度も出てきました。ただし、提案された解決策はどれもうまくいきません。これがどのように適切に処理されるか知りたいです (つまり、コードを実行可能にするだけではなく、正しく実行したいのです)。
私はこのソリューションが本当に好きです:
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()
# snip
しかし、a.py で A クラスを定義するだけでなく、そのクラスで何かを実行できるようにしたい場合はどうすればよいでしょうか? 例えば
a.py
from sqlalchemy import *
from base import Base
from sqlalchemy.orm import relationship
import main
class A(Base):
__tablename__ = "A"
id = Column(Integer, primary_key=True)
Bs = relationship("B", backref="A.id")
Cs = relationship("C", backref="A.id")
def populate_A(session):
for i in range(10):
new_A = A(id=i)
session.add(new_A)
session.commit()
if __name__ == '__main__':
session = main.Session()
populate_A(session)
これは私のコードをどのように構築したいかです - 意味的には、 A と populate_A は同じモジュールに存在する必要があるようです。しかし、循環 a -> main -> a import のため、上記の例では明らかにこれは機能しません。
したがって、通常、 A をどこか (a.py) で定義してから、メイン (a_functions.py ?) をインポートする別のモジュールで処理を行います。または、セッションの作成にはマップされたクラスのインポートは必要ありませんが、テーブルの作成にはインポートが必要なので、これら 2 つの機能を別のモジュールに配置しますか? または、main.pyのような何かひどいことをしますか
def create_tables():
import a, b, c
engine = create_engine("sqlite:///:memory:")
base.metadata.create_all(engine)
a、b、および c は、テーブルを作成する必要がある場合にのみ条件付きでインポートされ、循環インポートを回避できますか?
要するに、SQLAlchemy を使用したパッケージの慣用的な高レベルの編成はどのようなものでしょうか?