2017-12-19 23 views
1

관계를 사용하여 생성자에 객체를 추가하는 방법은 무엇입니까? 생성자가 평가 될 때 ID는 아직 준비되지 않았습니다. 단순한 경우 사전에 계산하여 목록을 제공하는 것이 가능합니다. 아래 예제에서 나는 블랙 박스와 비슷한 방식으로 complex_cls_method라고 말하려고했습니다.SQLAlchemy 모델의 생성자에서 관계를 통해 데이터를 저장하는 방법은 무엇입니까?

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

DB_URL = "mysql://user:[email protected]/exampledb?charset=utf8" 

engine = create_engine(DB_URL, encoding='utf-8', convert_unicode=True, pool_recycle=3600, pool_size=10) 
session = sessionmaker(autocommit=False, autoflush=False, bind=engine)() 

Model = declarative_base() 


class User(Model): 
    __tablename__ = 'user' 
    id = Column(Integer, primary_key=True) 
    simple = Column(String(255)) 
    main_address = Column(String(255)) 
    addresses = relationship("Address", 
          cascade="all, delete-orphan") 

    def __init__(self, addresses, simple): 
     self.simple = simple 
     self.main_address = addresses[0] 
     return # because the following does not work 
     self.addresses = Address.complex_cls_method(
      user_id_=self.id, # <-- this does not work of course 
      key_="address", 
      value_=addresses 
     ) 


class Address(Model): 
    __tablename__ = 'address' 
    id = Column(Integer, primary_key=True) 
    keyword = Column(String(255)) 
    value = Column(String(255)) 
    user_id = Column(Integer, ForeignKey('user.id'), nullable=False) 
    parent_id = Column(Integer, ForeignKey('address.id'), nullable=True) 

    @classmethod 
    def complex_cls_method(cls, user_id_, key_, value_): 
     main = Address(keyword=key_, value="", user_id=user_id_, parent_id=None) 
     session.add_all([main]) 
     session.flush() 
     addrs = [Address(keyword=key_, value=item, user_id=user_id_, parent_id=main.id) for item in value_] 
     session.add_all(addrs) 
     return [main] + addrs 


if __name__ == "__main__": 
    # Model.metadata.create_all(engine) 
    user = User([u"address1", u"address2"], "simple") 
    session.add(user) 
    session.flush() 

    # as it can't be done in constructor, these additional statements needed 

    user.addresses = Address.complex_cls_method(
     user_id_=user.id, 
     key_="address", 
     value_=[u"address1", u"address2"] 
    ) 
    session.commit() 

문제는 사용자의 생성자와 함께이 작업을 수행하는 구문 우아한 (기술적으로 타당) 방법이있다, 또는은 Session.flush가 원하는 객체를 추가 한 후에는 안전한 단지 사용자 클래스의 별도의 메소드를 호출하는 것입니다 관계 (예제 코드에서와 같이)?

생성자를 모두 포기하는 것은 가능하지만 결과로 나타나는 서명 변경으로 덜 바람직한 옵션은 중요한 리팩토링을 필요로합니다.

+0

"1 대 다 관계가있는 뭔가를 추가하는 유일한 방법은 먼저 플러시하는 것입니다. 다음으로 ..."는 다소 잘못되었습니다. SQLA는 지속적인 복잡한 객체 그래프를 처리 할 수 ​​있습니다 (일반적으로) - 그것의 판매 포인트 중 하나입니다. 게시물의 정보를 너무 많이 생략했습니다. 예를 들면 :'User.main_address'는 무엇입니까? 주소에 자체 참조 외래 키를 표시했으나이를 사용하는 방식이 아닙니다. [mcve]를 입력하십시오. 여기서 설명하는 대신 실제로 수행하려는 작업을 최소한으로 표시하십시오. –

+0

이제 (비) 작동 예제와 간단한 설명이 있습니다. –

답변

1

수동으로 플러시 및 ID 설정 등 대신 SQLAlchemy가 개체 그래프를 유지할 수 있도록 할 수 있습니다. 당신은 Address에서 한 번 더 adjacency list relationship이 필요합니다 모든 설정이 끝난 것입니다 : 그래서,

class User(Model): 
    __tablename__ = 'user' 
    id = Column(Integer, primary_key=True) 
    simple = Column(String(255)) 
    main_address = Column(String(255)) 
    addresses = relationship("Address", 
          cascade="all, delete-orphan") 

    def __init__(self, addresses, simple): 
     self.simple = simple 
     self.main_address = addresses[0] 
     self.addresses = Address.complex_cls_method(
      key="address", 
      values=addresses 
     ) 


class Address(Model): 
    __tablename__ = 'address' 
    id = Column(Integer, primary_key=True) 
    keyword = Column(String(255)) 
    value = Column(String(255)) 
    user_id = Column(Integer, ForeignKey('user.id'), nullable=False) 
    parent_id = Column(Integer, ForeignKey('address.id'), nullable=True) 
    # For handling parent/child relationships in factory method 
    parent = relationship("Address", remote_side=[id]) 

    @classmethod 
    def complex_cls_method(cls, key, values): 
     main = cls(keyword=key, value="") 
     addrs = [cls(keyword=key, value=item, parent=main) for item in values] 
     return [main] + addrs  

if __name__ == "__main__": 
    user = User([u"address1", u"address2"], "simple") 
    session.add(user) 
    session.commit() 
    print(user.addresses) 

주 등 SQLAlchemy의 자동 객체 관계를 기반으로 삽입 필요한 순서를 파악 수동 플러시가없는 의존성 행간을 존중할 수 있습니다. 이 부분은 Unit of Work 패턴의 일부입니다.

+0

마법처럼 작동합니다, 감사합니다! 특히 새로운 코드는 이전 버전보다 매개 변수가 적기 때문에 기쁩니다. –