9

別のテーブルから取り込まれたオブジェクトのマップされた属性を作成したいと考えています。

SQLAlchemy のドキュメントの例を使用して、Address クラスに user_name フィールドを作成し、簡単に照会して簡単にアクセスできるようにしたいと考えています (データベースへの 2 回目の往復なしで)。

たとえば、パフォーマンスの低下なしに、すべての Address オブジェクトの属性にuser_name Address.query.filter(Address.user_name == 'wcdolphin').first() アクセスしてクエリとフィルタリングを実行できるようにしたいと考えています。user_name__tablename__

class User(Base):
    __tablename__ = 'users'

    id = Column(Integer, primary_key=True)
    name = Column(String(50))
    addresses = relation("Address", backref="user")

class Address(Base):
    __tablename__ = 'addresses'

    id = Column(Integer, primary_key=True)
    email = Column(String(50))
    user_name = Column(Integer, ForeignKey('users.name'))#This line is wrong

どうすればいいですか?

ほとんどの例、特に Flask-SQLAlchemy の例に準拠していないように見えたため、ドキュメントを理解するのは比較的難しいと感じました。

4

2 に答える 2

7

これは、クエリオブジェクトので行うことができjoinます。この属性を直接指定する必要はありません。したがって、モデルは次のようになります。

from sqlalchemy import create_engine, Column, Integer, String, ForeignKey
from sqlalchemy.orm import sessionmaker, relation
from sqlalchemy.ext.declarative import declarative_base

Base = declarative_base()
engine = create_engine('sqlite:///')
Session = sessionmaker(bind=engine)

class User(Base):
    __tablename__ = 'users'
    id = Column(Integer, primary_key=True)
    name = Column(String(50))
    addresses = relation("Address", backref="user")

class Address(Base):
    __tablename__ = 'addresses'
    id = Column(Integer, primary_key=True)
    email = Column(String(50))
    user_id = Column(Integer, ForeignKey("users.id"))


Base.metadata.create_all(engine)

ユーザー名をフィルタリングしたアドレスの後のクエリは、次のようになります。

>>> session = Session()
>>> session.add(Address(user=User(name='test')))
>>> session.query(Address).join(User).filter(User.name == 'test').first()
<__main__.Address object at 0x02DB3730>

編集:アドレスオブジェクトからユーザーに直接アクセスできるため、属性をアドレスクラスに直接参照する必要はありません。

>>> a = session.query(Address).join(User).filter(User.name == 'test').first()
>>> a.user.name
'test'
于 2012-06-05T06:07:04.213 に答える
3

明示的に結合する必要なく、Address に "User.name" の SQL 対応バージョンを本当に持たせたい場合は、相関サブクエリを使用する必要があります。これはすべての場合に機能しますが、データベース側 (特に MySQL の場合) では非効率になる傾向があるため、通常の JOIN を使用する場合と比較して、SQL 側でパフォーマンスが低下する可能性があります。いくつかの EXPLAIN テストを実行すると、影響がどの程度あるのかを分析するのに役立つ場合があります。

相関 column_property() の別の例はhttp://docs.sqlalchemy.org/en/latest/orm/mapped_sql_expr.html#using-column-propertyにあります。

「set」イベントの場合、相関サブクエリは読み取り専用属性を表しますが、イベントを使用して変更をインターセプトし、それらを親 User 行に適用できます。これに対する 2 つのアプローチを以下に示します。1 つは通常の ID マップ メカニズムを使用するもので、まだ存在しない場合に User 行の負荷が発生します。もう 1 つは、行に直接 UPDATE を発行します。

from sqlalchemy import *
from sqlalchemy.orm import *
from sqlalchemy.ext.declarative import declarative_base

Base= declarative_base()

class User(Base):
    __tablename__ = 'users'

    id = Column(Integer, primary_key=True)
    name = Column(String(50))
    addresses = relation("Address", backref="user")

class Address(Base):
    __tablename__ = 'addresses'

    id = Column(Integer, primary_key=True)
    user_id = Column(Integer, ForeignKey('users.id'))
    email = Column(String(50))

Address.user_name = column_property(select([User.name]).where(User.id==Address.id))

from sqlalchemy import event
@event.listens_for(Address.user_name, "set")
def _set_address_user_name(target, value, oldvalue, initiator):
    # use ORM identity map + flush
    target.user.name = value

    # use direct UPDATE
    #object_session(target).query(User).with_parent(target).update({'name':value})

e = create_engine("sqlite://", echo=True)
Base.metadata.create_all(e)

s = Session(e)
s.add_all([
    User(name='u1', addresses=[Address(email='e1'), Address(email='e2')])
])
s.commit()

a1 = s.query(Address).filter(Address.user_name=="u1").first()
assert a1.user_name == "u1"

a1.user_name = 'u2'
s.commit()

a1 = s.query(Address).filter(Address.user_name=="u2").first()
assert a1.user_name == "u2"
于 2012-06-12T22:30:01.413 に答える