2010-02-17 5 views
8

Ich habe viele Modellklassen mit Relationen zwischen ihnen mit einer CRUD-Schnittstelle zu bearbeiten. Das Problem besteht darin, dass einige Objekte nicht gelöscht werden können, da andere Objekte auf sie verweisen. Manchmal kann ich eine ON DELETE-Regel einrichten, um diesen Fall zu behandeln, aber in den meisten Fällen möchte ich nicht automatisch verwandte Objekte löschen, bis sie manuell gelöst werden. Wie auch immer, ich möchte dem Redakteur eine Liste von Objekten vorstellen, die sich auf das aktuell angezeigte beziehen, und diejenigen hervorheben, die das Löschen aufgrund der FOREIGN KEY-Einschränkung verhindern. Gibt es eine fertige Lösung, um Referer automatisch zu entdecken?Erkennen von References zu SQLAlchemy-Objekt

aktualisieren

Die Aufgabe scheint recht verbreitet zu sein (z django ORM zeigt alle Abhängigkeiten), so frage ich mich, dass es, um es noch keine Lösung ist.

Es gibt zwei Richtungen vorgeschlagen:

  1. Aufzählen alle Beziehungen des aktuellen Objekts und gehen durch ihre backref. Aber es gibt keine Garantie, dass alle Relationen backref definiert haben. Darüber hinaus gibt es einige Fälle, in denen backref bedeutungslos ist. Obwohl ich es überall definieren kann, mag ich es nicht so und es ist nicht zuverlässig.
  2. (Vorgeschlagen von van und stephan) Überprüfen Sie alle Tabellen MetaData Objekt und sammeln Abhängigkeiten von ihrer foreign_keys Eigenschaft (der Code sqlalchemy_schemadisplay kann als Beispiel verwendet werden, dank stephans Kommentare). Dies ermöglicht es, alle Abhängigkeiten zwischen Tabellen zu fangen, aber was ich brauche, ist Abhängigkeiten zwischen Modellklassen. Einige Fremdschlüssel sind in Zwischentabellen definiert und haben keine ihnen entsprechenden Modelle (in Relationen als secondary verwendet). Sicher, ich kann weiter gehen und verwandtes Modell finden (muss noch einen Weg finden, es zu tun), aber es sieht zu kompliziert aus.

Lösung

Hier ist eine Methode der Modellklasse Base (deklarative extention entworfen), die ich als Lösung verwenden. Es ist nicht perfekt und erfüllt nicht alle meine Anforderungen, aber es funktioniert für den aktuellen Stand meines Projekts. Das Ergebnis wird als Wörterbuch von Wörterbüchern gesammelt, sodass ich sie nach Objekten und ihren Eigenschaften gruppiert darstellen kann. Ich habe noch nicht entschieden, ob es eine gute Idee ist, da die Liste der Referer manchmal riesig ist und ich gezwungen bin, sie auf eine vernünftige Anzahl zu beschränken.

def _get_referers(self): 
    db = object_session(self) 
    cls, ident = identity_key(instance=self) 
    medatada = cls.__table__.metadata 
    result = {} 
    # _mapped_models is my extension. It is collected by metaclass, so I didn't 
    # look for other ways to find all model classes. 
    for other_class in medatada._mapped_models: 
     queries = {} 
     for prop in class_mapper(other_class).iterate_properties: 
      if not (isinstance(prop, PropertyLoader) and \ 
        issubclass(cls, prop.mapper.class_)): 
       continue 
      query = db.query(prop.parent) 
      comp = prop.comparator 
      if prop.uselist: 
       query = query.filter(comp.contains(self)) 
      else: 
       query = query.filter(comp==self) 
      count = query.count() 
      if count: 
       queries[prop] = (count, query) 
     if queries: 
      result[other_class] = queries 
    return result 

Dank an alle, die mir geholfen haben, besonders stephan und van.

+0

Ich bin mir nicht sicher, ob ich die Frage verstehe. Tabellenobjekte haben eine 'foreign_keys' -Eigenschaft, die korrekt gesetzt werden sollte, wenn Sie die Metadaten durch Reflektion erhalten haben. ORM-Mapped-Klassen haben Relationen und Backrefs als Teil des Mappers. Kannst du es ausarbeiten? – stephan

+0

@stephan: Ich habe die Frage aktualisiert, hoffentlich ist es jetzt klar. –

+0

@Denis: Ja, jetzt viel klarer, und ich denke, van hat das meiste schon geschrieben. Bei einer gegebenen Modellklasse können Sie den Mapper mit der Funktion 'class_mapper()' oder 'object_mapper()' aufrufen. Dies gibt Ihnen die zugeordnete Tabelle. Dann können Sie die 'foreign_keys' dieser Tabellen aufrufen, um alle zugehörigen Tabellen zu erhalten. Für diese überprüfen Sie, welchen Klassen sie zugeordnet sind. Siehe das zweite Beispiel in http://www.sqlalchemy.org/trac/wiki/UsageRecipes/SchemaDisplay für etwas Ähnliches. Ich kenne keinen besseren Weg. – stephan

Antwort

6

SQL: Ich stimme absolut nicht mit S.Lott 'answer überein. Ich bin nicht bewusst Out-of-the-Box-Lösung, aber es ist definitiv möglich, alle Tabellen zu entdecken, die ForeignKey-Einschränkungen für eine bestimmte Tabelle haben. Man muss die INFORMATION_SCHEMA Ansichten wie REFERENTIAL_CONSTRAINTS, KEY_COLUMN_USAGE, TABLE_CONSTRAINTS usw. richtig verwenden. Siehe SQL Server example. Mit einigen Einschränkungen und Erweiterungen unterstützen die meisten Versionen neuer relationaler Datenbanken den Standard INFORMATION_SCHEMA. Wenn Sie alle FK-Informationen und das Objekt (Zeile) in der Tabelle haben, müssen Sie nur wenige SELECT-Anweisungen ausführen, um alle anderen Zeilen in anderen Tabellen abzurufen, die sich auf die angegebene Zeile beziehen, und verhindern, dass sie gelöscht wird.

sqlalchemy: Wie von stephan in seinem Kommentar, wenn Sie orm mit backref für die Beziehungen zu verwenden, dann sollte es ganz einfach für Sie die Liste der Eltern Objekte zu erhalten, den Bezug zu halten auf das Objekt Sie versuchen zu löschen, da diese Objekte im Grunde Eigenschaften Ihres Objekts zugeordnet sind (child1.Parent).

Wenn Sie mit Table Objekte von Sqlalchemy arbeiten (oder auch nicht immer backref für die Beziehungen verwenden), dann würden Sie Werte von foreign_keys für alle Tabellen bekommen müssen, und dann für alle ForeignKey Ruf references(...) Methode, die Bereitstellung Ihrer Tabelle als Parameter. Auf diese Weise finden Sie alle FKs (und Tabellen), die sich auf die Tabelle beziehen, der Ihr Objekt zugeordnet ist. Dann können Sie alle Objekte abfragen, die Bezug auf Ihr Objekt haben, indem Sie die Abfrage für jede dieser FKs erstellen.

+1

Danke (+1) für die Idee, 'foreign_keys' für alle Tabellen zu überprüfen, bisher scheint es die vielversprechendste/zuverlässigste Methode zu sein. Ich benutze fast nie "backref", da es die Beziehung außerhalb der Modelldefinition definiert: Sie können nicht alle verfügbaren Eigenschaften aus der Klassendefinition sehen, der Code ist nicht selbst dokumentiert. –

+1

Sie können alle FK-Abhängigkeiten ermitteln. Ich habe nie gesagt, dass du es nicht kannst. Wir sind uns einig, FK-Einschränkungen zu finden. Sie können jedoch nicht alle Abfragen ermitteln, die abgebrochen werden, da sie von einer * nicht deklarierten * FK-Beziehung abhängen. –

1

Im Allgemeinen gibt es keine Möglichkeit, alle der Referenzen in einer relationalen Datenbank zu "entdecken".

In einigen Datenbanken können sie deklarative referenzielle Integrität in Form von expliziten Foreign Key- oder Check-Einschränkungen verwenden.

Aber es gibt keine Anforderung, dies zu tun. Es kann unvollständig oder inkonsistent sein.

Jede Abfrage kann eine FK-Beziehung enthalten, die nicht deklariert ist. Ohne das Universum aller Abfragen können Sie nicht wissen, welche Beziehungen verwendet, aber nicht deklariert werden.

Um "Referer" im Allgemeinen zu finden, müssen Sie das Datenbankdesign kennen und alle Abfragen haben.

+2

information_schema liefert die Informationen, die zum Erkennen der Referenzen benötigt werden. und Denis interessiert nur diejenigen, die tatsächlich verhindern, dass die Zeile gelöscht wird, was bedeutet, dass es sich um eine echte FOREIGN KEY-Einschränkung handelt. – van

+0

Die Frage war über SQLAlchemy, die bereits Schemadefinition als eine Struktur im Speicher (im Code definiert oder automatisch aus der Datenbank geladen) hat. –

+0

@van: Das Problem ist, dass es noch informelle Regeln gibt. Wenn eine Abfrage eine nicht deklarierte FK-Beziehung erwartet, bricht ein zulässiges Löschen die Anwendung noch immer ab. Daher der "allgemeine" Teil der Antwort. Deklarierte Beziehungen sind nicht das ganze Geschäft im Allgemeinen. Die Frage spiegelt dies nicht wider, was im Allgemeinen zu Problemen führen wird. Das spezifische Schema kann wirklich, wirklich vollständig sein und kann alle FKs deklariert haben. Aber im Allgemeinen gibt es keine Möglichkeit, das zu wissen, wenn Sie nicht jede Abfrage rückentwickeln. –

0

Für jede Modellklasse können Sie leicht sehen, ob alle ihre Eins-zu-Viele-Beziehungen leer sind, indem Sie einfach nach der Liste fragen und sehen, wie viele Einträge darin enthalten sind. (Es gibt wahrscheinlich auch einen effizienteren Weg, der in Bezug auf COUNT implementiert wird.) Wenn es Fremdschlüssel für das Objekt gibt und Sie Ihre Objektbeziehungen korrekt eingerichtet haben, dann wird mindestens eine dieser Listen ungleich Null sein in der Länge.

+0

Dies ist nicht immer richtig. Das aktuelle Objekt kann keine Relationen haben, während es ein Objekt gibt, das über einen Fremdschlüssel auf es verweist und eine Relation ohne Backref hat. Beachten Sie auch, dass das Überprüfen aller Objekte in allen Objektbeziehungen keine Option ist, da es sich um eine riesige Sammlung handeln kann (faul = "dynamisch"). –

+0

Riesig oder nicht, ohne mindestens 1 Zeile zu lesen, können Sie nicht wissen, ob es Referenzen gibt. Was fehlende Backrefs angeht, so meinte ich mit "richtig eingerichtet", obwohl ich anerkenne, dass es nicht falsch ist, die backref zu verpassen, nur unbequem. – Kylotan