2016-08-08 65 views
1

Ich versuche, die folgende Situation zu modellieren: Ein Programm hat viele Versionen, und eine der Versionen ist die aktuelle (nicht unbedingt die neueste).Eine zu viele + eine Beziehung in SQLAlchemy?

Dies ist, wie ich es jetzt tue:

class Program(Base): 
    __tablename__ = 'programs' 
    id = Column(Integer, primary_key=True) 
    name = Column(String) 
    current_version_id = Column(Integer, ForeignKey('program_versions.id')) 

    current_version = relationship('ProgramVersion', foreign_keys=[current_version_id]) 
    versions = relationship('ProgramVersion', order_by='ProgramVersion.id', back_populates='program') 


class ProgramVersion(Base): 
    __tablename__ = 'program_versions' 
    id = Column(Integer, primary_key=True) 
    program_id = Column(Integer, ForeignKey('programs.id')) 
    timestamp = Column(DateTime, default=datetime.datetime.utcnow) 

    program = relationship('Filter', foreign_keys=[program_id], back_populates='versions') 

Aber dann bekomme ich die Fehlermeldung: Konnte nicht ermitteln Zustand zwischen Eltern/Kind-Tabellen auf Beziehung Program.versions beitreten - es mehrere Fremdschlüsselpfade Verknüpfen der Tabellen. Geben Sie das Argument 'foreign_keys' an und geben Sie eine Liste der Spalten an, die gezählt werden sollen und die einen Fremdschlüsselbezug zur übergeordneten Tabelle enthalten.

Aber welchen Fremdschlüssel sollte ich für die "Program.versions" -Beziehung bereitstellen? Gibt es eine bessere Möglichkeit, diese Situation zu modellieren?

Antwort

0

Dieser Entwurf ist nicht ideal; Wenn zwei Tabellen aufeinander verweisen, können Sie nicht effektiv in eine der beiden Tabellen einfügen, da der für den anderen erforderliche Fremdschlüssel nicht existiert. Eine mögliche Lösung ist in der ausgewählten Antwort von this question related to microsoft sqlserver beschrieben, aber ich werde hier zusammenfassen.

Eine bessere Möglichkeit, dies zu modellieren, könnte darin bestehen, eine dritte Tabelle, VersionHistory, einzuführen und Ihre Fremdschlüsseleinschränkungen für die anderen beiden Tabellen zu beseitigen.

class VersionHistory(Base): 
    __tablename__ = 'version_history' 
    program_id = Column(Integer, ForeignKey('programs.id'), primary_key=True) 
    version_id = Column(Integer, ForeignKey('program_version.id'), primary_key=True) 
    current = Column(Boolean, default=False) 
    # I'm not too familiar with SQLAlchemy, but I suspect that relationship 
    # information goes here somewhere 

Dadurch wird die Kreisbeziehung, die Sie in Ihrer aktuellen Implementierung erstellt haben, eliminiert. Sie können dann diese Tabelle nach Programmen abfragen und alle vorhandenen Versionen für das Programm usw. empfangen. Aufgrund des zusammengesetzten Primärschlüssels in dieser Tabelle können Sie auf eine bestimmte Kombination aus Programm und Version zugreifen. Das Hinzufügen des Felds current zu dieser Tabelle nimmt die Last des Verfolgens der Währung von den anderen zwei Tabellen, obwohl das Beibehalten einer einzigen aktuellen Version pro Programm eine Trigger-Gymnastik erfordern könnte.

HTH!

0

Kreisabhängigkeit wie diese ist eine vollkommen gültige Lösung für dieses Problem.

Um das Problem mit Fremdschlüsseln zu beheben, müssen Sie das Argument foreign_keys explizit angeben.

class Program(Base): 
    ... 
    current_version = relationship('ProgramVersion', foreign_keys=current_version_id, ...) 
    versions = relationship('ProgramVersion', foreign_keys="ProgramVersion.program_id", ...) 

class ProgramVersion(Base): 
    ... 
    program = relationship('Filter', foreign_keys=program_id, ...) 

Sie werden feststellen, dass, wenn Sie einen create_all() tun, SQLAlchemy Probleme hat die Tabellen erstellen, da jede Tabelle einen Fremdschlüssel hat, die auf einer Säule in der anderen abhängt. SQLAlchemy bietet eine Möglichkeit, diese zirkuläre Abhängigkeit zu durchbrechen, indem sie eine ALTER Erklärung für eine der Tabellen:

class Program(Base): 
    ... 
    current_version_id = Column(Integer, ForeignKey('program_versions.id', use_alter=True, name="fk_program_current_version_id")) 
    ... 

Schließlich Sie werden feststellen, dass, wenn Sie ein komplettes Objekt Diagramm, um die Sitzung hinzufügen, SQLAlchemy Probleme haben INSERT Ausgabe Anweisungen, da jede Zeile einen Wert hat, der vom noch unbekannten Primärschlüssel des anderen abhängt. SQLAlchemy bietet eine Möglichkeit, diese zirkuläre Abhängigkeit aufzuheben, indem für eine der folgenden Spalten eine UPDATE ausgegeben wird: