簡體   English   中英

如何使用int而不是實例查詢sqlalchemy關系?

[英]How can I query a sqlalchemy relationship with an int instead of an instance?

我有兩個模型,Foo和Bar。 Bar與Foo具有一對多關系,具有“ foo_id”列和“ foo”關系。 我想查詢具有foo_id = 1的Bar行。

據我所知,有兩種工作方法可以做到這一點:

  1. 訪問基礎外鍵列對象: query.filter(Bar.foo_id == 1)
  2. 獲取Foo的實例,並執行query.filter(Bar.foo == instance)

我想要這兩種方法的替代方法,更像是query.filter(Bar.foo == 1) -也就是說,使用關系列,並使用普通整數而不是實例。 當前這會失敗,並出現AttributeError: 'int' object has no attribute '_sa_instance_state'

我想避免上述兩種方法的原因:

  1. 第一種方法在實際應用程序中不可行,因為我不知道該foo_id列的名稱-所有表元數據都是通過反射生成的,並且列名是我不希望依賴的實現細節(它們目前非常不一致,並且正在使用Alembic遷移進行重命名,因此不希望依賴這些名稱)

  2. 第二種方法添加了一個SQL查詢的額外往返行程,以獲取Foo的實例,然后我才能執行實際的查詢,該查詢不使用除我已經擁有的整數ID之外的任何東西(並且身份映射不使用用我使用它的方式緩存它)。 我承認這部分很危險地接近過早的優化,但是仍然感到浪費,應該有更好的方法。

解決這個問題的另一種方法是如何從RelationshipProperty開始到達外鍵列,可能使用自省

我也可以用某種方式來獲取某種延遲加載的Foo實例,該實例足以通過ID查詢,但實際上並不發送SQL查詢

這是一些自包含的測試代碼來說明問題:

from sqlalchemy import create_engine, Column, Integer, ForeignKey
from sqlalchemy.orm import relationship, Session
from sqlalchemy.ext.declarative import declarative_base

def method_1(session, some_foo_id):
    """Works, but i don't actually know foo_id"""

    session.query(Bar).filter(Bar.foo_id == some_foo_id).first()

def method_2(session, some_foo_id):
    """Works, but adds a pointless roundtrip"""

    some_foo = session.query(Foo).get(some_foo_id)
    session.query(Bar).filter(Bar.foo == some_foo).first()

def method_3(session, some_foo_id):
    """Throws an exception, passing int instead of instance"""

    session.query(Bar).filter(Bar.foo == some_foo_id).first()


# database setup follows

Base = declarative_base()

class Foo(Base):
    __tablename__ = 'foo'
    id = Column(Integer, primary_key=True)
    value = Column(Integer)

class Bar(Base):
    __tablename__ = 'bar'
    id = Column(Integer, primary_key=True)
    foo_id = Column(Integer, ForeignKey('foo.id'))
    foo = relationship(Foo)

if __name__ == '__main__':
    engine = create_engine('sqlite://')
    Base.metadata.create_all(engine)
    session = Session(bind=engine)

    foo1, foo2 = Foo(value=1), Foo(value=2)
    bar1, bar2 = Bar(foo=foo1), Bar(foo=foo2)
    session.add_all([foo1, foo2, bar1, bar2])
    session.commit()
    engine.echo = True

    for fun in [method_1, method_2, method_3]:
        print("\n---> %s (%s)\n" % (fun.__name__, fun.__doc__))

        fun(session, 1)
        session.rollback()

輸出:

---> method_1 (Works, but i don't actually know foo_id

BEGIN (implicit)
SELECT bar.id AS bar_id, bar.foo_id AS bar_foo_id
    FROM bar
    WHERE bar.foo_id = ?
     LIMIT ? OFFSET ?
(1, 1, 0)
ROLLBACK

---> method_2 (Works, but adds a pointless roundtrip)

BEGIN (implicit)
SELECT foo.id AS foo_id, foo.value AS foo_value
    FROM foo
    WHERE foo.id = ?
(1,)
SELECT bar.id AS bar_id, bar.foo_id AS bar_foo_id
    FROM bar
    WHERE ? = bar.foo_id
     LIMIT ? OFFSET ?
(1, 1, 0)
ROLLBACK

---> method_3 (Throws an exception, passing int instead of instance)

Traceback (most recent call last):
  File "asd.py", line 51, in <module>
    fun(session, 1)
  File "asd.py", line 19, in method_3
    session.query(Bar).filter(Bar.foo == some_foo_id).first()
  File "/usr/lib/python2.7/site-packages/sqlalchemy/sql/operators.py", line 304, in __eq__
    return self.operate(eq, other)
  File "/usr/lib/python2.7/site-packages/sqlalchemy/orm/attributes.py", line 175, in operate
    return op(self.comparator, *other, **kwargs)
  File "/usr/lib/python2.7/site-packages/sqlalchemy/orm/relationships.py", line 1042, in __eq__
    other, adapt_source=self.adapter))
  File "/usr/lib/python2.7/site-packages/sqlalchemy/orm/relationships.py", line 1369, in _optimized_compare
    state = attributes.instance_state(state)
AttributeError: 'int' object has no attribute '_sa_instance_state'

找到了一種方法, Bar.foo.prop.local_columns是RelationshipProperty的屬性,該屬性返回一個通常包含一個項目的集合(對於像這樣的簡單關系)。 集合是無序的,您不能只獲得第一項,所以list(...)[0] 完整代碼:

def method_4(session, some_foo_id):
    foo_col = list(Bar.foo.prop.local_columns)[0]
    session.query(Bar).filter(foo_col == some_foo_id).first()

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM