ファイル全体のSQLAlchemyクラス


82

SQLAlchemyクラスを複数のファイルに分散させる方法を理解しようとしていますが、その方法を理解することはできません。私はSQLAlchemyにかなり慣れていないので、この質問が些細なものである場合はご容赦ください。

それぞれ独自のファイルでこれらの3つのクラスを検討してください。

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"))

そして、次のようなmain.pyがあるとしましょう。

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()

上記はエラーを与えます:

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'

これらのファイル間で宣言型ベースを共有するにはどうすればよいですか?

この上にパイロンターボギアのようなものを投げる可能性があることを考えると、これを達成するための「正しい」方法は何ですか?

2011年10月3日編集

問題を説明し、さらに重要なことに、これが実際の問題であり、問​​題である私の混乱した自己だけではないことを確認するピラミッドフレームワークからこの説明を見つけました。それがこの危険な道をあえてする他の人を助けることができることを願っています:)


7
@ S.Lott上記は、すべてのクラスが1つのファイルにある場合に機能するので、教えてください:)
joveha 2011

あなたのコードはこのエラーを与えません、実際のエラーがあるコードを投稿してください。インポートを修正し、実行して、誰かが実際にエラー確認できるようにします。
knitti 2011

@ S.Lott私の混乱は、循環的なインポートを回避する方法に集中していたようです。私はこれが問題ではないCから来ました。お時間を割いていただき、誠に申し訳ございません。
joveha 2011

@joveha:なに?あなたが抱えているこれらの周期的なインポートの問題は何ですか。それらを分解してサイクルを回避する方法を説明できるように、サイクリックインポートを含むコードを投稿してください。これらのコメントには漠然とした仮説が多すぎます。どんな問題がありますか?具体的にお願いします。
S.Lott 2011

回答:


87

あなたの問題の最も簡単な解決策を取るようになりBase、その輸入のモジュールのうちABC。循環インポートを中断します。

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()

私のマシンで動作します:

$ python main.py ; echo $?
0

1
を使用しscoped_sessionます。
ユーザー

3
@user:セッション処理は、この投稿の質問とは無関係です。これは、実際には昔ながらのPythonの質問です(どのようにインポートするのですか?)しかし、私はあなたの注意を引いたので、スレッドローカルストレージが必要な理由がわからない限り、を使用しないことを強くお勧めしscoped_sessionます。使用の問題scoped_sessionは、それが発生した可能性のあるコード内のポイントへの明示的なリンクがなくても、リークされたトランザクションや古いデータが発生しやすくなることです。
singleNegationElimination 2013

このデザインパターンは、python3では機能しないようです。python3と互換性のある簡単な修正はありますか?
computermacgyver

@computermacgyver:このパターンはPythonバージョン間で正しく機能するはずです。あなたは、すべての含めることができるように、新しい質問してください、あなたのコードを、あなたが見ているエラー。
singleNegationElimination 2013年

@dequestarmappartialsetattrに感謝します。このエラーは、a.py、b.py、c.py、およびmodel.pyを別のモジュールに入れようとしたときにのみ発生することがわかりました。その場合の解決策は、代わりにモジュールの__init__。pyファイルにbase.pyコードを含めることでした。ここにコードと詳細な説明を入れまし。返信いただきありがとうございます。
computermacgyver

13

私も同じ問題を抱えていたので、私も少し感覚を加えることができれば。あなたが作成したファイル内のクラスをインポートする必要がありBase = declarative_base()、作成したAFTERBaseTables。私のプロジェクトがどのように設定されているかの短い例:

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'))

モデル/__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

8

Python 2.7 + Flask 0.10 + SQLAlchemy 1.0.8 + Postgres9.4.4.1を使用しています

このボイラープレートは、「user」モジュールの同じファイル「models.py」に保存されているUserモデルとUserDetailモデルで構成されています。これらのクラスは両方ともSQLAlchemy基本クラスから継承します。

プロジェクトに追加したすべての追加クラスもこの基本クラスから派生し、models.pyファイルが大きくなるにつれて、models.pyファイルをクラスごとに1つのファイルに分割することにし、説明されている問題に遭遇しました。ここに。

私が見つけた解決策は、@ computermacgyverの2013年10月23日のポストと同じラインに沿って、にすべての私のクラスを含めることだったのinit私はすべて新しく作成されたクラスファイルを保持するために作成された新しいモジュールのの.pyファイル。このように見えます:

/project/models/

__init__.py contains

from project.models.a import A 
from project.models.b import B
etc...

2
なぜFlaskを使用する必要があると思いますか?

0

私にとっては、テーブルを作成するには、import app.tool.tool_entity内部app.pyfrom app.tool.tool_entity import Tool内部を追加するtool/__init__.pyだけで十分でした。しかし、私はまだ関係を追加しようとはしていません。

フォルダー構造:

app/
  app.py
  tool/
    __init__.py
    tool_entity.py
    tool_routes.py
# 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)
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.