2013-01-23 7 views
8

Update:SQLAlchemy Bidirektionale Beziehung Verein Proxy

Für alle, dieses Problem hat, mit dem very latest SQLAlchemy dieses Verhalten wurde behoben.

Originalausgabe:

Ich habe ein Problem mit Verband Proxies immer korrekt zu aktualisieren.

hier die Beispielmodelle verwenden: http://docs.sqlalchemy.org/en/rel_0_7/orm/extensions/associationproxy.html#simplifying-association-objects

Aber Ändern UserKeyword mit dieser Zeile:

keyword = relationship("Keyword", backref=backref("user_keywords", cascade="all, delete-orphan")) 

und das Hinzufügen dieser zu Stichwort:

users = association_proxy('user_keywords', 'user') 

So ein Stichwort Instanz hat eine Liste von Benutzer.

Die folgenden Arbeiten wie erwartet:

>>> rory = User("rory") 
>>> session.add(rory) 
>>> chicken = Keyword('chicken') 
>>> session.add(chicken) 
>>> rory.keywords.append(chicken) 
>>> chicken.users 
[<__main__.User object at 0x1f1c0d0>] 
>>> chicken.user_keywords 
[<__main__.UserKeyword object at 0x1f1c450>] 

Aber Umzüge seltsame Dinge tun. Entfernen aus den Verein Proxy-Listen wie folgt:

>>> rory.keywords.remove(chicken) 

verursacht einen Integritätsfehler als SA eine der Fremdschlüsselspalten zu setzen versucht, auf null.

dies zu tun:

>>> rory.user_keywords.remove(rory.user_keywords[0]) 

Ergebnisse in dieser:

>>> chicken.users 
[None] 

Ich habe offensichtlich etwas verpasst haben, nicht wahr?

Antwort

7

UserKeyword erfordert, dass es gleichzeitig mit Keyword und User verbunden ist, um dauerhaft zu sein. Wenn Sie es mit einer User und Keyword verknüpfen, aber dann aus der User.user_keywords Sammlung entfernen, ist es immer noch mit der Keyword verbunden.

>>> rory.keywords.remove(chicken) 

# empty as we expect 
>>> rory.user_keywords 
[] 

# but the other side, still populated. UserKeyword 
# has no User, but still has Keyword 
>>> chicken.user_keywords 
[<__main__.UserKeyword object at 0x101748d10>] 

# but the User on that UserKeyword is None 
>>> chicken.user_keywords[0].user is None 
True 

# hence accessing the "association" gives us None 
# as well 
>>> chicken.users 
[None] 

Also, wenn wir() dieses Recht jetzt spülen sind, haben Sie ein UserKeyword Objekt bereit bekommen zu gehen, aber es hat keine User auf sich, so dass Sie NULL Fehler. Bei der INSERT-Zeit wird das Objekt nicht als "verwaist" betrachtet, es sei denn, es ist nicht mit einer Sammlung verbunden. Nur wenn Sie sagen würden, del chicken.user_keywords[0] oder gleichwertig, würden Sie sehen, dass kein INSERT generiert wird und das UserKeyword Objekt vergessen wird.

Wenn Sie das Objekt in die Datenbank leeren, bevor Sie es aus "Rory" entfernen, dann ändern sich die Dinge. Die UserKeyword ist jetzt persistent, und wenn Sie "Huhn" aus "Rory entfernen.keywords“, ein "delete-orphan Brände" Ereignis aus der die UserKeyword nicht löschen, obwohl es nach wie vor mit dem Keyword Objekt zugeordnet ist:

rory.keywords.append(chicken) 

session.flush() 

rory.keywords.remove(chicken) 

session.flush() 

Sie die SQL sehen:

INSERT INTO "user" (name) VALUES (%(name)s) RETURNING "user".id 
{'name': 'rory'} 

INSERT INTO keyword (keyword) VALUES (%(keyword)s) RETURNING keyword.id 
{'keyword': 'chicken'} 

INSERT INTO user_keyword (user_id, keyword_id, special_key) VALUES (%(user_id)s, %(keyword_id)s, %(special_key)s) 
{'keyword_id': 1, 'special_key': None, 'user_id': 1} 

DELETE FROM user_keyword WHERE user_keyword.user_id = %(user_id)s AND user_keyword.keyword_id = %(keyword_id)s 
{'keyword_id': 1, 'user_id': 1} 

Nun würde eine vernünftige Person fragen: "Ist das nicht inkonsequent?" Und im Moment würde ich "absolut" sagen. Ich muss in die Testfälle schauen, um zu sehen, was die Gründe für diesen Unterschied im Verhalten sind. Ich habe in dem Code festgestellt, warum es auf diese Weise auftritt, und ich bin mir ziemlich sicher, dass dieser Unterschied darin besteht, wie ein "Waisenkind" betrachtet wird "Ausstehende" gegenüber "persistenten" Objekten ist beabsichtigt, aber in dieser speziellen Permutation ergibt sich offensichtlich ein seltsames Ergebnis. Ich könnte eine Änderung in 0.8 dafür machen, wenn ich eine finden kann, die machbar ist.

bearbeiten: http://www.sqlalchemy.org/trac/ticket/2655 fasst das Problem zusammen, über das ich nachdenken muss. Es gibt einen Test speziell für dieses Verhalten, den man auf seinen Ursprung zurückverfolgen muss.

+0

Danke Haufen zzzeek. –

Verwandte Themen