2010-05-19 19 views
5

Tôi đang cố gắng tạo cấu trúc db trong đó tôi có nhiều loại thực thể nội dung, trong đó một, một chú thích, có thể được đính kèm với bất kỳ khác.Tạo các bảng tự tham chiếu có tính đa hình trong SQLALchemy

xem xét như sau:

from datetime import datetime 
from sqlalchemy import create_engine 
from sqlalchemy import Column, ForeignKey 
from sqlalchemy import Unicode, Integer, DateTime 
from sqlalchemy.orm import relation, backref 
from sqlalchemy.ext.declarative import declarative_base 

Base = declarative_base() 

class Entity(Base): 
    __tablename__ = 'entities' 
    id = Column(Integer, primary_key=True) 
    created_at = Column(DateTime, default=datetime.utcnow, nullable=False) 
    edited_at = Column(DateTime, default=datetime.utcnow, onupdate=datetime.utcnow, nullable=False) 
    type = Column(Unicode(20), nullable=False) 
    __mapper_args__ = {'polymorphic_on': type} 

# <...insert some models based on Entity...> 

class Comment(Entity): 
    __tablename__ = 'comments' 
    __mapper_args__ = {'polymorphic_identity': u'comment'} 
    id = Column(None, ForeignKey('entities.id'), primary_key=True) 
    _idref = relation(Entity, foreign_keys=id, primaryjoin=id == Entity.id) 
    attached_to_id = Column(Integer, ForeignKey('entities.id'), nullable=False) 
    #attached_to = relation(Entity, remote_side=[Entity.id]) 
    attached_to = relation(Entity, foreign_keys=attached_to_id, 
          primaryjoin=attached_to_id == Entity.id, 
          backref=backref('comments', cascade="all, delete-orphan")) 

    text = Column(Unicode(255), nullable=False) 

engine = create_engine('sqlite://', echo=True) 
Base.metadata.bind = engine 
Base.metadata.create_all(engine) 

này có vẻ đúng, ngoại trừ SQLAlchemy không thích có hai phím nước ngoài trỏ đến cùng cha mẹ. Nó nói ArgumentError: Can't determine join between 'entities' and 'comments'; tables have more than one foreign key constraint relationship between them. Please specify the 'onclause' of this join explicitly.

Làm cách nào để chỉ định onclause?

Trả lời

10

Cố gắng bổ sung của bạn Comment.__mapper_args__ tới:

__mapper_args__ = { 
    'polymorphic_identity': 'comment', 
    'inherit_condition': (id == Entity.id), 
} 

T.B. Tôi chưa hiểu bạn cần mối quan hệ _idref để làm gì? Nếu bạn muốn sử dụng một bình luận ở đâu đó, nơi Entity được mong đợi, bạn chỉ có thể vượt qua ví dụ Comment.

+0

_idref là một nỗ lực để làm rõ đối với SQLAlchemy mà mối quan hệ đề cập đến cái gì. Nó không được sử dụng trong mã của tôi. –

+0

Vì vậy, tôi chỉ cố gắng điều này và nó đã khắc phục được vấn đề, nhưng đã giới thiệu một vấn đề mới: xóa bất kỳ thực thể nào khác (mà có một mô hình backref từ mô hình này, nhưng không có trường hợp nào) thất bại với 'TypeError: id() lấy chính xác một đối số (0) ' –

+0

Bạn chưa hiểu ý kiến ​​của mình hoàn toàn nhưng có vẻ như bạn đã nhầm lẫn với hàm' id() 'của Python. Bạn đã quên thêm id với 'self.'? Hoặc bạn đã di chuyển '__mapper_args__' trong khai báo lớp * sau * khai báo của trường' id'? – nkrkv

3

Để bổ sung câu trả lời của @ nailxx: Lặp lại tất cả các bản mẫu đó là tẻ nhạt nếu bạn có nhiều bảng phụ thuộc. Giải pháp: di chuyển tất cả sang metaclass.

class EntityMeta(type(Entity)): 
    def __init__(cls, name, bases, dct): 
     ident = dct.get('_identity',None) 
     if '__abstract__' not in dct: 
      xid = Column(None, ForeignKey(Entity.id), primary_key=True) 
      setattr(cls,'id',xid) 
      setattr(cls,'__mapper_args__', { 'polymorphic_identity': dct['_identity'], 'inherit_condition': (xid == Entity.id,), 'primary_key':(xid,) }) 
      setattr(cls,'__tablename__',name.lower()) 
     super(EntityMeta, cls).__init__(name, bases, dct) 

class EntityRef(Entity): 
    __abstract__ = True 
    __metaclass__ = EntityMeta 

class Comment(EntityRef): 
    _identity = 'comment' 
    [...] 

Variations, như một bài tập cho người đọc: Bạn có thể bỏ qua các tuyên bố _identity=… và sử dụng tên lớp, như trong dòng setattr(cls,'__tablename__',…). Hoặc không ghi đè thuộc tính __tablename__ hoặc __mapper_args__ hiện có.

Hãy chắc chắn để kiểm tra dct và không cls cho sự tồn tại: sau này sẽ tìm các thuộc tính trong lớp cha mẹ, mà bạn rõ ràng là không muốn vào thời điểm này.

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