2012-05-09 53 views
9

との関係テーブルは、私がどのように見えるテーブルのセットがあります。SQLAlchemyの:複合主キー

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), 
          ) 

を私のORMクラスは次のようになります。

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" 
          ) 

だから私のシステムでは、各アクションが一意ですワークフローIDとその名前の組み合わせによって識別されます。私は、それぞれのアクションが親と子のアクションを参照するparentschildren属性を持つようにしたいと思います。各アクションは、複数の親と子を持つことができます。

問題は、私のような機能を持っている場合に発生します。私は関係が正しくworkflow_idを設定してもらうにはどうすればよい

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') 

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() 

を私はのようなエラーが出ますか?

+0

'action_dependencies'テーブルの' workflow_id'ですか? – van

+1

アクションの主キーは、その名前とworkflow_idのコンポジットです。 workflow_idがaction_dependenciesにない場合、依存関係が参照していたワークフローのアクションを特定する方法はありません。 –

+0

良い点、良い点。私は考えてみよう... – van

答えて

11

動作するコードの下を参照してください。キーポイントは、私がコメントで述べたものです:

  • 適切な複合FKSを使用してForeignKey
  • 正しいrelationship設定

コード:あなたが持っている必要がありますなぜ

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

プライマリキーに外部キーを設定するのは間違いです。それはどのように機能するのですか?

しかし、複合制約、「一緒にユニーク」であるキーを作るテーブル定義でこれを使用する:

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

しかし、あなたは本当にそれはまた、あなたがこれを使用することができ、主キーになりたい場合は:

PrimaryKeyConstraint(*columns, **kw) 

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

+5

主キーの外部キー制約には何も問題はありません。これは、「1対1」の関係を取得したり、サブクラスをデータベースにマップしたり、属性をすべて「ヌル」にするための典型的な方法です – SingleNegationElimination

関連する問題