2017-04-02 108 views
0

我有兩個模型,Foo和Bar。 Bar與Foo有一個一對多的關係,有一個「foo_id」列和一個「foo」關係。我想查詢具有foo_id = 1的Bar的行。如何使用int而不是實例查詢sqlalchemy關係?

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

  1. 訪問底層外鍵字段對象:query.filter(Bar.foo_id == 1)
  2. 獲得美孚的一個實例,並做query.filter(Bar.foo == instance)

我想替代這兩種方法更像query.filter(Bar.foo == 1) - 也就是說,使用關係列,一個純整數,而不是一個實例。目前這失敗AttributeError: 'int' object has no attribute '_sa_instance_state'

原因,我想避免這兩種方法上面:

  1. 第一種方法是不是在實際應用中是可行的,因爲我不知道這個名字foo_id列 - 所有表元數據都是通過反射生成的,而列名是我不想依賴的實現細節(它們目前非常不一致,並且在使用alembic遷移進行重命名的過程中,所以對這些名稱的依賴關係是不可取的)

  2. 第二次見面hod增加了一個額外的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' 

回答

0

發現了一種方法,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() 
相關問題