sqlalchemy多态多对多关系

时间:2010-02-04 17:48:42

标签: polymorphism sqlalchemy

我正在尝试允许用户在我的网络应用中“喜欢”不同的项目。因此,例如,用户可以喜欢评论并喜欢新闻故事。然后,我想查询用户所喜爱的所有项目,并以多态方式加载相关对象(无论是新闻报道还是评论等),以向用户显示对象列表。我知道如何创建一对一的多态关系,但是我无法弄清楚多少关系。

提前致谢

修改

在我的一对一多态关系中,我在用户和用户操作之间存在一对多关系,并且用户操作与执行操作的对象之间存在一对一的多态关系。所以在这种情况下我的用户操作表是这样的:

class UserAction:  
  pass

user_actions = Table('user_action', metadata,  
    Column('id', Integer, autoincrement=True, primary_key=True),
    Column('module', String(30)),
    Column('created', DateTime, default=datetime.now),  
    Column('user_id', Integer, ForeignKey('user.id'))  
)

新闻表(可以通过用户操作访问的对象之一):

class News:  
  pass

news = Table('news', metadata,  
    Column('id', Integer, autoincrement=True, primary_key=True),  
    Column('title', String(30), nullable=False),  
    Column('action_id', Integer, ForeignKey('user_action.id'))  
)

和地图制作者:

mapper(UserAction, user_actions, polymorphic_on=user_actions.c.module, polymorphic_identity='user_action')  
mapper(News, news, inherits=UserAction, polymorphic_identity='news')

正如您所看到的,新闻对象与与之关联的user_action记录之间存在明确的一对一关系。这是因为user_action外键位于新闻表中。如果我想创建一个多对多的多态关系,其中多个不同的对象类型可以被许多用户收藏,我将如何去做呢?非常感谢。

1 个答案:

答案 0 :(得分:2)

您的示例定义了UserActionNews之间的一对多关系。这看起来很糟糕,因为我认为没有理由News继承UserAction。要允许多个用户操作引用单个新闻,您必须使用两个外键定义中间表:一个引用UserAction,另一个引用News。我认为有两种合理的方法可以使其具有多态性:

  1. 为每个受欢迎的模型类使用单独的中间表,并在每个UserAction子类中定义不同的关系。
  2. 为中间表中的每个favourited模型定义单独的外键,并将其映射到具有单表继承的类层次结构(类似UserActionItemUserActionNewsItem等。)。
  3. 但请注意,以上所有内容都是为了链接UserAction和一些具有多对多关系的条目模型。虽然用户使用条目时看起来更像是将User与条目模型联系起来。

    更新:以下是工作示例。我看到的唯一问题是它允许重复。

    from sqlalchemy import *
    from sqlalchemy.orm import mapper, relation, sessionmaker
    from sqlalchemy.ext.associationproxy import association_proxy
    
    metadata = MetaData()
    
    users = Table(
        'users', metadata,
        Column('id', Integer, nullable=False, primary_key=True),
    )
    
    news = Table(
        'news', metadata,
        Column('id', Integer, nullable=False, primary_key=True),
    )
    
    comments = Table(
        'comments', metadata,
        Column('id', Integer, nullable=False, primary_key=True),
    )
    
    favitems = Table(
        'favitems', metadata,
        Column('id', Integer, nullable=False, primary_key=True),
        Column('user_id', Integer, ForeignKey(users.c.id), nullable=False),
        Column('item_type', Integer, nullable=False),
        Column('news_id', Integer, ForeignKey(news.c.id)),
        Column('comment_id', Integer, ForeignKey(comments.c.id)),
    )
    
    class News(object): pass
    
    class Comment(object): pass
    
    class FavItem(object):
        TYPE_NEWS = 1
        TYPE_COMMENT = 2
        def __new__(cls, item=None):
            if isinstance(item, News):
                cls = FavNews
            elif isinstance(item, Comment):
                cls = FavComment
            return object.__new__(cls)
    
    class FavNews(FavItem):
        def __init__(self, item):
            self.item_type = self.TYPE_NEWS
            self.item = item
    
    class FavComment(FavItem):
        def __init__(self, item):
            self.item_type = self.TYPE_COMMENT
            self.item = item
    
    class User(object):
        favorites = association_proxy('_favitems', 'item', creator=FavItem)
    
    mapper(News, news)
    
    mapper(Comment, comments)
    
    mapper(FavItem, favitems,
           polymorphic_on=favitems.c.item_type)
    
    mapper(FavNews, favitems,
           inherits=FavItem,
           polymorphic_identity=FavItem.TYPE_NEWS,
           properties={
                'item': relation(News),
           })
    
    mapper(FavComment, favitems,
           inherits=FavItem,
           polymorphic_identity=FavItem.TYPE_COMMENT,
           properties={
                'item': relation(Comment),
           })
    
    mapper(User, users,
           properties={
                '_favitems': relation(FavItem),
           })
    
    engine = create_engine('sqlite://')
    metadata.create_all(engine)
    session = sessionmaker(engine)()
    
    user = User()
    news1 = News()
    news2 = News()
    comment1 = Comment()
    comment2 = Comment()
    user.favorites = [news1, news2, comment1, comment2]
    session.add(user)
    session.commit()
    user_id = user.id
    
    session.expunge_all()
    user = session.query(User).get(user_id)
    print user.favorites