2012-05-09 29 views
9

Tôi có một tập hợp các bảng trông giống như:SQLAlchemy: bảng Relation với hỗn tiểu học trọng điểm

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

lớp ORM của tôi trông giống như:

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

Vì vậy, trong hệ thống của tôi, mỗi hành động là duy nhất được xác định bởi sự kết hợp của id luồng công việc và tên của nó. Tôi muốn mỗi hành động có thuộc tính parentschildren đề cập đến hành động của cha mẹ và con của nó. Mỗi hành động có thể có nhiều phụ huynh và trẻ em.

Vấn đề xảy ra khi tôi có một chức năng như:

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

tôi nhận được một lỗi như:

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

Làm thế nào để tôi có được mối quan hệ để thiết lập workflow_id chính xác?

+0

Tại sao bạn cần phải có 'workflow_id' trong bảng' action_dependencies'? – van

+1

Bởi vì khóa chính cho một hành động là một kết hợp của tên và workflow_id của nó. Nếu workflow_id không có trong action_dependencies, sẽ không có cách nào để cho biết các hành động của luồng công việc mà phụ thuộc đang đề cập đến. –

+0

Điểm tốt, điểm tốt. Hãy để tôi suy nghĩ ... – van

Trả lời

11

Xem dưới đây đang làm việc. Những điểm mấu chốt là những tôi đã đề cập trong các ý kiến:

  • thích hợp ForeignKey s
  • đúng relationship cấu hình bằng cách sử dụng FKS

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

Tôi không nghĩ chính xác là khóa chính có khóa ngoại. Nó hoạt động như thế nào?

Tuy nhiên, để thực hiện chế composite, một chìa khóa đó là "độc đáo với nhau", sử dụng này trong định nghĩa bảng:

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

Nhưng nếu bạn thực sự muốn nó được khóa chính bạn cũng có thể sử dụng này:

PrimaryKeyConstraint(*columns, **kw) 

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

+5

không có gì sai với các ràng buộc khoá ngoại trên các khóa chính; đây là một cách điển hình để có được mối quan hệ 'một đối một', để ánh xạ các lớp con vào cơ sở dữ liệu, hoặc để có các thuộc tính mà tất cả có thể là "rỗng với nhau" – SingleNegationElimination

Các vấn đề liên quan