表继承映射与对象关系数据库中的组合

5 投票
1 回答
888 浏览
提问于 2025-04-18 18:05

我最近开始使用SQLAlchemy,但之前对SQL没有任何了解。我遇到的一个问题是如何引入多态行为。举个例子,想象一个类似Reddit的网站应用;我们有一个Article(文章)模型和一个Comment(评论)模型,它们都可以被投票:

class Article(Base):
    id       = Column(Integer, primary_key = True)
    data     = Column(Text)
    comments = relationship('Comment')
    #... more article-related attributes
    votes      = relationship('Vote')
    vote_ups   = Column(Integer, default = 0)
    vote_downs = Column(Integer, default = 0) 

class Comment(Base):
    id   = Column(Integer, primary_key = True)
    data = Column(Text)
    #... more comment-related attributes
    votes      = relationship('Vote')
    vote_ups   = Column(Integer, default = 0)
    vote_downs = Column(Integer, default = 0)

我想把投票的属性分离出来,因为这两个模型都有这些属性,这样我就不需要为每个可以投票的模型重复代码。

我最初的想法是创建一个新的VotesComponent(投票组件)模型,把这些属性放进去,如下所示:

class VotesComponent(Base):
    votes      = relationship('Vote')
    vote_ups   = Column(Integer)
    vote_downs = Column(Integer)

然后和Comment(评论)和Article(文章)模型建立多对一的关系。

在查阅SQLAlchemy的文档时,我发现可以通过使用连接表继承来实现类似的功能。一开始这看起来很方便,因为避免了额外的间接引用(也就是说,可以直接用comment.votes而不是comment.votes_component.votes),但我有限的理解中看到的一个大缺点是,不支持多重继承,而用之前的方法,可以自由地为模型添加多个“组件”。

所以,我的问题是,使用继承映射相比于组合有什么好处,什么时候更适合使用其中一种,为什么?在这种情况下,你会推荐哪种方法(或者可能是其他不同的方法)?

编辑:我应该提到,我希望能够单独查询模型的“投票”部分,这样我就可以以多态的方式处理投票。

1 个回答

2

这个问题很好,但在这里有点偏题,因为在StackOverflow上,大家不太喜欢那些需要主观意见的问题。不过,我还是想分享一下我的个人看法(我真的不想引发争论)。

我的问题是,使用继承映射有什么好处,相比之下,组合又是什么时候更合适,为什么?

在大多数面向对象的编程语言中,继承通常与“是一个”的关系有关,而组合则与“有一个”的关系有关。在Python中,组合通常通过多重继承来实现,所以“组合和继承”的讨论有点奇怪。Python提倡一种叫做鸭子类型的风格,因此“有一个”的问题被认为更符合语言的习惯。

当我们谈论对象关系映射(ORM)时,还有其他的考虑:继承是如何在底层实现的?有些实现会把对象数据分散到多个表中,并进行SQL连接(JOIN),而其他的则会使用单个表并进行SQL并集(UNION)。在我看来,这些细节不值得你花太多精力去思考。第一个原因是,这些对你来说是透明的,这就是使用ORM的目的。第二,你可能缺乏判断哪种方式更适合你特定情况的知识(你需要深入了解SQL和各种关系数据库管理系统的具体实现细节,才能理解其中的性能影响)。

我的建议是,按照你喜欢的编程风格来实现,信任ORM的选择,把性能问题留给数据库管理员(DBA)去处理。

撰写回答