2012-05-09 6 views
9

ich eine Reihe von Tabellen, die wie folgt aussehen:SQLAlchemy: Beziehung Tabelle mit zusammengesetzten Primärschlüssel

workflows = Table('workflows', Base.metadata, 
        Column('id', Integer, primary_key=True), 
       ) 

actions = Table('actions', Base.metadata, 
       Column('name', String, primary_key=True), 
       Column('workflow_id', Integer, ForeignKey(workflows.c.id), primary_key=True), 
       ) 

action_dependencies = Table('action_dependencies', Base.metadata, 
          Column('workflow_id', Integer, ForeignKey(workflows.c.id), primary_key=True), 
          Column('parent_action', String, ForeignKey(actions.c.name), primary_key=True), 
          Column('child_action', String, ForeignKey(actions.c.name), primary_key=True), 
          ) 

Meine ORM-Klassen wie folgt aussehen:

class Workflow(Base): 
    __table__ = workflows 

    actions = relationship("Action", order_by="Action.name", backref="workflow") 


class Action(Base): 
    __table__ = actions 

    children = relationship("Action", 
          secondary=action_dependencies, 
          primaryjoin=actions.c.name == action_dependencies.c.parent_action, 
          secondaryjoin=actions.c.name == action_dependencies.c.child_action, 
          backref="parents" 
          ) 

Also in meinem System, jede Aktion eindeutig ist identifiziert durch eine Kombination aus einer Workflow-ID und ihrem Namen. Ich möchte, dass jede Aktion das Attribut parents und children hat, das auf ihre übergeordneten und untergeordneten Aktionen verweist. Jede Aktion kann mehrere Eltern und Kinder haben.

Das Problem tritt auf, wenn ich eine Funktion haben, wie zum Beispiel:

def set_parents(session, workflow_id, action_name, parents): 
    action = session.query(db.Action).filter(db.Action.workflow_id == workflow.id).filter(db.Action.name == action_name).one() 

    for parent_name in parents: 
     parent = session.query(db.Action).filter(db.Action.workflow_id == workflow.id).filter(db.Action.name == parent_name).one() 
     action.parents.append(parent) 

    session.commit() 

ich einen Fehler wie:

IntegrityError: (IntegrityError) action_dependencies.workflow_id may not be NULL u'INSERT INTO action_dependencies (parent_action, child_action) VALUES (?, ?)' (u'directory_creator', u'packing') 

Wie erhalte ich die Beziehung der workflow_id richtig eingestellt?

+0

Warum haben Sie müssen tun, um 'workflow_id' in der Tabelle 'action_dependencies'? – van

+1

Da der Primärschlüssel für eine Aktion eine Zusammensetzung aus Name und Workflow-ID ist. Wenn die workflow_id nicht in action_dependencies enthalten ist, gibt es keine Möglichkeit zu ermitteln, auf welche Workflowaktionen sich die Abhängigkeit bezieht. –

+0

Guter Punkt, guter Punkt. Lass mich nachdenken ... – van

Antwort

11

Siehe unten Code arbeiten. Die wichtigsten Punkte sind die, die ich in den Kommentaren erwähnt:

  • richtige Verbund ForeignKey s
  • korrekte relationship Konfiguration FKS mit

Code:

workflows = Table('workflows', Base.metadata, 
        Column('id', Integer, primary_key=True), 
       ) 

actions = Table('actions', Base.metadata, 
       Column('workflow_id', Integer, ForeignKey(workflows.c.id), primary_key=True), 
       Column('name', String, primary_key=True), 
       ) 

action_dependencies = Table('action_dependencies', Base.metadata, 
          Column('workflow_id', Integer, ForeignKey(workflows.c.id), primary_key=True), 
          Column('parent_action', String, ForeignKey(actions.c.name), primary_key=True), 
          Column('child_action', String, ForeignKey(actions.c.name), primary_key=True), 
          ForeignKeyConstraint(['workflow_id', 'parent_action'], ['actions.workflow_id', 'actions.name']), 
          ForeignKeyConstraint(['workflow_id', 'child_action'], ['actions.workflow_id', 'actions.name']), 
          ) 
class Workflow(Base): 
    __table__ = workflows 
    actions = relationship("Action", order_by="Action.name", backref="workflow") 

class Action(Base): 
    __table__ = actions 
    children = relationship("Action", 
          secondary=action_dependencies, 
          primaryjoin=and_(actions.c.name == action_dependencies.c.parent_action, 
           actions.c.workflow_id == action_dependencies.c.workflow_id), 
          secondaryjoin=and_(actions.c.name == action_dependencies.c.child_action, 
           actions.c.workflow_id == action_dependencies.c.workflow_id), 
          backref="parents" 
          ) 

# create db schema 
Base.metadata.create_all(engine) 

# create entities 
w_1 = Workflow() 
w_2 = Workflow() 
a_11 = Action(name="ac-11", workflow=w_1) 
a_12 = Action(name="ac-12", workflow=w_1) 
a_21 = Action(name="ac-21", workflow=w_2) 
a_22 = Action(name="ac-22", workflow=w_2) 
session.add(w_1) 
session.add(w_2) 
a_22.parents.append(a_21) 
session.commit() 
session.expunge_all() 
print '-'*80 

# helper functions 
def get_workflow(id): 
    return session.query(Workflow).get(id) 
def get_action(name): 
    return session.query(Action).filter_by(name=name).one() 

# test another OK 
a_11 = get_action("ac-11") 
a_12 = get_action("ac-12") 
a_11.children.append(a_12) 
session.commit() 
session.expunge_all() 
print '-'*80 

# test KO (THIS SHOULD FAIL VIOLATING FK-constraint) 
a_11 = get_action("ac-11") 
a_22 = get_action("ac-22") 
a_11.children.append(a_22) 
session.commit() 
session.expunge_all() 
print '-'*80 
0

Ich glaube nicht, dass es richtig ist, den Primärschlüssel einen Fremdschlüssel zu haben. Wie funktioniert das?

Aber Composite-Constraint zu machen, einen Schlüssel, der „einzigartig zusammen“, verwenden Sie diese in der Tabellendefinition ist:

UniqueConstraint(u"name", u"workflow_id"), 

Aber wenn Sie es wirklich der Primärschlüssel sein wollen können Sie auch diese verwenden:

PrimaryKeyConstraint(*columns, **kw) 

http://docs.sqlalchemy.org/en/latest/core/schema.html#sqlalchemy.schema.PrimaryKeyConstraint

+5

Es gibt nichts Falsches an Fremdschlüsseleinschränkungen für Primärschlüssel; Dies ist ein typischer Weg, um eine Eins-zu-Eins-Beziehung zu erhalten, Unterklassen der Datenbank zuzuordnen oder Attribute zu haben, die alle "null zusammen" sein können. – SingleNegationElimination

Verwandte Themen