2012-03-12 83 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) 

或理想

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術語),並且因此表示owns關係類型,而不是根據動詞的uses。因此,每個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中現有的對象)。 – 2012-03-13 16:09:05

+0

@DerekLitz:夠公平的......會根據你的問題鏈接到的文檔更改答案...... – van 2012-03-14 09:19:59

+0

是的,該方法看起來會起作用,並且優於定義一個'__init__'做同樣的事情,因爲這樣做會導致所有的對象都必須以這種方式實例化,或者爲方法增加一些複雜性。我注意到了一些錯誤,這是我不確定的問題之一。爲了在插入之前執行查詢,獲取會話的最佳方式是什麼?你的代碼在查詢上存在一個錯誤,因爲它需要在對象上進行會話,比如'session.query(Keyword).filter(Keyword.keyword = kw)'。 – 2012-03-15 03:50:15

2

我最近遇到了同樣的問題。 SQLAlchemy的創建者Mike Bayer提到了我的「unique object」 recipe,但也向我展示了一個使用事件偵聽器的變體。後一種方法修改關聯代理,以便UserKeyword.keyword臨時指向一個純字符串,並且只在關鍵字不存在時才創建新的關鍵字對象。

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