2012-03-12 3 views
7

This source詳細アソシエーションプロキシを使用して、ORMオブジェクトの値を持つビューとオブジェクトを作成する方法。アソシエーションプロキシSQLAlchemy

ただし、データベース内の既存のオブジェクトと一致する値を追加すると、その値は一意のキーまたは主キーです。競合するオブジェクトが作成されるため、コミットできません。

私の場合、これはビューとしてのみ有用であり、ORMクエリを使用して追加するオブジェクトを取得する必要があります。

これは私の唯一のオプションですか、またはマージを使用できますか(プライマリキーでユニークな制約でない場合にのみこれを行うことができます)、またはコンストラクタを設定して、既存のオブジェクトを使用します新しいオブジェクトを作成するのではなく、存在する場合はデータベース?ドキュメントから例えば

user.keywords.append('cheese inspector') 

# Is translated by the association proxy into the operation: 

user.kw.append(Keyword('cheese inspector')) 

しかし、私はより多くのようなものに変換されるようにしたいと思います。(もちろん、クエリが失敗する可能性があります)。

keyword = session.query(Keyword).filter(Keyword.keyword == 'cheese inspector').one() 
user.kw.append(keyword) 

OR理想的

user.kw.append(Keyword('cheese inspector')) 
session.merge() # retrieves identical object from the database, or keeps new one 
session.commit() # success! 

私はこれも良いアイデアではないかもしれないが、それは特定のユースケースになる可能性が想定:)

答えて

9

あなたがリンクするドキュメントページに示されている例toはcompositionのタイプの関係(OOP用語)であり、usesの動詞ではなくownsタイプの関係を表します。したがって、各ownerは、同じ(値に関して)キーワードの独自のコピーを持ちます。

実際には、あなたの質問にリンクしているドキュメントの提案を正確に使用して、カスタムcreatorメソッドを作成し、新しいオブジェクトを作成するのではなく、既存のオブジェクトを再利用するようにハックすることができます。この場合、Userクラスとcreator機能のサンプルコードは以下のようになります。私は最近、同じ問題に遭遇した

def _keyword_find_or_create(kw): 
    keyword = Keyword.query.filter_by(keyword=kw).first() 
    if not(keyword): 
     keyword = Keyword(keyword=kw) 
     # if aufoflush=False used in the session, then uncomment below 
     #session.add(keyword) 
     #session.flush() 
    return keyword 

class User(Base): 
    __tablename__ = 'user' 
    id = Column(Integer, primary_key=True) 
    name = Column(String(64)) 
    kw = relationship("Keyword", secondary=lambda: userkeywords_table) 
    keywords = association_proxy('kw', 'keyword', 
      creator=_keyword_find_or_create, # @note: this is the 
      ) 
+0

チェックしていない)。私の質問は、私の特定のユースケース(私が追加するたびに新しいオブジェクトを作成するのではなく、可能であればdbの既存オブジェクトを使用する)のために、そのサンプルの動作をもっと変化させることです。 –

+0

@DerekLitz:公正な...あなたの質問にリンクされているドキュメントに基づいて答えを変えてくれるでしょう... – van

+0

ええ、その方法はうまくいくように見え、 '__init__'を定義することよりも優れていますそれはすべてのオブジェクトがそのようにインスタンス化されなければならないようにするか、またはメソッドにいくらか複雑さを加えるようにするため、同じことをします。私はいくつかの間違いに気付きました。それは私があまりよく分からなかった問題の一つです。挿入の前にクエリを実行するためにセッションを取得する最善の方法は何ですか?あなたのコードには、 'session.query(Keyword).filter(Keyword.keyword = kw)'のようなオブジェクトのセッションで行う必要があるため、クエリにバグがあります。 –

2

。 SQLAlchemyの作成者であるMike Bayerは私に“unique object” recipeと言いましたが、イベントリスナーを使用するバリアントも示しました。後者の方法では、UserKeyword.keywordが一時的にプレーンストリングを指すようにアソシエーションプロキシを変更し、キーワードが存在しない場合にのみ新しいキーワードオブジェクトを作成します。私はその答えは、ドキュメントとまったく同じことを示していると考えていますが、ドキュメントはクラスのいくつかのメソッドをオーバーライドすることなく、それを行う方法をお見せし(私はメソッドがクラスでやっていることだと仮定し、I

from sqlalchemy import event 

# Same User and Keyword classes from documentation 

class UserKeyword(Base): 
    __tablename__ = 'user_keywords' 

    # Columns 
    user_id = Column(Integer, ForeignKey(User.id), primary_key=True) 
    keyword_id = Column(Integer, ForeignKey(Keyword.id), primary_key=True) 
    special_key = Column(String(50)) 

    # Bidirectional attribute/collection of 'user'/'user_keywords' 
    user = relationship(
     User, 
     backref=backref(
      'user_keywords', 
      cascade='all, delete-orphan' 
      ) 
     ) 

    # Reference to the 'Keyword' object 
    keyword = relationship(Keyword) 

    def __init__(self, keyword=None, user=None, special_key=None): 
     self._keyword_keyword = keyword_keyword # temporary, will turn into a 
               # Keyword when we attach to a 
               # Session 
     self.special_key = special_key 

    @property 
    def keyword_keyword(self): 
     if self.keyword is not None: 
      return self.keyword.keyword 
     else: 
      return self._keyword_keyword 

    @event.listens_for(Session, "after_attach") 
    def after_attach(session, instance): 
     # when UserKeyword objects are attached to a Session, figure out what 
     # Keyword in the database it should point to, or create a new one 
     if isinstance(instance, UserKeyword): 
      with session.no_autoflush: 
       keyword = session.query(Keyword).\ 
        filter_by(keyword=instance._keyword_keyword).\ 
        first() 
       if keyword is None: 
        keyword = Keyword(keyword=instance._keyword_keyword) 
       instance.keyword = keyword