Ваш пример определяет отношение один-ко-многим между UserAction
и News
. Мне это кажется беспорядком, поскольку я не вижу причин, по которым News
наследуется от UserAction
. Чтобы разрешить нескольким пользовательским действиям ссылаться на одну новость, вы должны определить промежуточную таблицу с двумя внешними ключами: один ссылается на UserAction
, а другой на News
. Я вижу два разумных способа сделать его полиморфным:
- Используйте отдельную промежуточную таблицу для каждого предпочтительного класса модели и определите различные отношения в каждом
UserAction
подклассах.
- Определите отдельный внешний ключ для каждой предпочитаемой модели в промежуточной таблице и сопоставьте его с иерархией классов с наследованием одной таблицы (что-то вроде
UserActionItem
, UserActionNewsItem
и т. Д.).
Но учтите, что все вышеперечисленное предназначено для связи 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