2

関連する 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 を使用したパッケージの慣用的な高レベルの編成はどのようなものでしょうか?

4

1 に答える 1

0

これは実際には、SQLAlchemy のイディオムではなく、Python のイディオムの問題だったと思います。Guido Van Rossum によると、パッケージ内でスクリプトを実行することはアンチパターンです (zzeek はコメントでこれをほのめかしました)。したがって、a.py、b.py、および c.py にはif __name__ == '__main__'ブロックを含めないでください。

于 2013-01-21T23:04:03.330 に答える