2012-06-05 6 views
7

다른 테이블에서 채워진 객체의 매핑 된 속성을 만들고 싶습니다.SQLAlchemy join의 선언적 속성 (전체 속성이 아닌 단일 속성)

SQLAlchemy의 문서의 예제를 사용하여, 나는 _ 이름 필드가 될 수 있도록 주소 클래스에 존재하게하려면 모두 쉽게 조회하고 쉽게 예를 들어

(데이터베이스에 두 번째 왕복없이) 액세스, 나는 user_nameAddress.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 
에 속성의 예상되는대로 제대로 기록 유지해야 할 0

어떻게해야합니까?

대부분의 예제, 특히 Flask-SQLAlchemy 예제와 일치하지 않는 것으로 보아 설명서를 비교적 이해하기 어렵습니다.

+0

은 무엇 정확하게 잘못 문서와 함께? [sqlalchemy tutorial] (http://docs.sqlalchemy.org/en/rel_0_7/orm/tutorial.html)에서는 일반적인 작업에 필요한 모든 것을 다룹니다. 그리고 고급 개념은 나머지 문서에서 찾을 수 있습니다. – schlamar

답변

6

쿼리 개체에서 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> 

편집가 : 직접 주소 오브젝트에서 사용자가 액세스 할 수 있듯이,이 Address 클래스에 대한 속성을 직접 참조 할 필요가 없습니다.

>>> a = session.query(Address).join(User).filter(User.name == 'test').first() 
>>> a.user.name 
'test' 
+0

다소 정확하지만, 내 질문에 설명 된대로 'Address'의 user_name 속성에 액세스 할 수 없기 때문에 이것은 내 의도가 아닙니다. 사용자 지정 PHP/손으로 작성된 ORM에 이것을 쓰면 User .name 테이블에서 User.name을 선택하고 특성을 저장하는 SQL을 올바르게 생성하는 간단한 경우입니다. SQLAlchemy는이 시나리오를 지원해야합니까? –

+0

@wcdolphin 답변을 업데이트했습니다. – schlamar

+0

Brilliant, 여기서 핵심은 역 참조를 만들 때 발생하는 마법입니다. 실제로 id를 쿼리 할 사용자 개체의 Address를 제공한다는 것은 '알고 있습니다'. –

1

주소에 SQL 지원 버전이 필요하면 명시 적으로 조인 할 필요없이 "User.name"을 사용하려면 상관 하위 쿼리를 사용해야합니다. 이것은 모든 경우에 사용할 수 있지만 데이터베이스 측 (특히 MySQL의 경우)에서는 비효율적 인 경향이 있으므로 일반적인 JOIN을 사용할 때보 다 SQL 측에서 성능이 저하 될 수 있습니다. 일부 EXPLAIN 테스트를 실행하면 영향의 정도를 분석하는 데 도움이 될 수 있습니다.

상관 관계가있는 column_property()의 다른 예는 http://docs.sqlalchemy.org/en/latest/orm/mapped_sql_expr.html#using-column-property입니다.

"set"이벤트의 경우 상관 하위 쿼리는 읽기 전용 속성을 나타내지 만 이벤트를 사용하여 변경 사항을 가로 채어 상위 사용자 행에 적용 할 수 있습니다. 이 두 가지 접근 방식이 아니라면 이미 사용자 행의 부하를 발생합니다 일반 Identity지도 역학을 이용하여, 하나 다음과 같다, 행에 직접 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" 
+0

이 방법을 사용하면 어떤 이점이 있습니까?표준 파이썬 속성이 더 직설적 인 것 같습니다 (제 답변 참조). 또는 나는 무엇인가 놓치고있다? ('address.user.name'로 액세스하는 것은 IMO가 가장 파이썬 버전입니다.) – schlamar

+0

그는 join()을 호출하지 않고도 쿼리 할 수 ​​있기를 원합니다. 이 패턴은 초기 SQLA에서 많은 부분을 강조한 패턴이었으며, "집계 계산"과 같은 것이 필요할 경우 여전히 관련성이 있습니다. 그러나 관련 속성을 얻으려면별로 좋아하지 않습니다. SQLA는 실제로 당신이 질의의 기하학을 설명하기를 원한다. – zzzeek

관련 문제