rails: delegated_type 委托类型(一对多的场景)
在 Rails 7 中引入了 delegated_type
方法,这个方法提供了一种新的多态关联方式,简化了不同模型之间的多态关系管理。它用于实现“代表类型(delegated_type)
”模式,让一个表(代表)通过代理访问不同的关联对象,而不用像传统的 polymorphic
关联那样处理单个表中的所有逻辑。
- Article 有多个 Comment (
1:N
) - Photo 有多个 Comment (
1:N
)
01 示例
假设我们希望 Comment
模型可以关联到 Article
或 Photo
,并且可以通过 comment.commentable
来直接访问具体的对象。
02 创建 Comment 模型
首先,我们用 rails g model
命令生成 Comment
模型。
$ Could not generate field '
commentable_id
' with unknown type 'bigint'.
# 这个是公用的 delegate 模型
rails g model Comment content:text commentable_type:string commentable_id:integer
这里我们定义了 commentable_type
和 commentable_id
,因为 delegated_type
依赖这两个字段来确定实际的关联模型。生成并迁移数据库:
rails db:migrate
03 创建 Article 和 Photo 模型
接下来,创建两个可以拥有 Comment
的模型:Article
和 Photo
。
rails g model Article title:string content:text
rails g model Photo title:string url:string
生成迁移并迁移数据库:
rails db:migrate
04 设置 Comment 的 delegated_type
在 Comment
模型中,使用 delegated_type
定义多态关系,使得 Comment
可以代理到 Article
或 Photo
。
打开 app/models/comment.rb
,添加 delegated_type
配置:
# app/models/comment.rb
class Comment < ApplicationRecord
delegated_type :commentable, types: %w[Article Photo], dependent: :destroy
# 这里可以添加其他方法或验证
end
通过 delegated_type :commentable
,我们将 Comment
配置为一个代表类型,其中 commentable
可以是 Article
或 Photo
。
05 设置 Article 和 Photo 的关联
在 Article
和 Photo
模型中,我们分别添加与 Comment
的关联关系。
# app/models/article.rb
class Article < ApplicationRecord
has_many :comments, as: :commentable
end
# app/models/photo.rb
class Photo < ApplicationRecord
has_many :comments, as: :commentable
end
通过 as: :commentable
,Article
和 Photo
表示它们都是可以拥有 Comment
的对象。
06 使用示例
完成以上配置后,我们可以通过以下方式创建和访问 Comment
的多态关联。
创建 Comment 实例
比如我们创建一个 Article
和一个 Photo
,然后为它们分别添加评论:
# 创建 Article 和 Photo 实例
article = Article.create(title: "An Interesting Article", content: "Content of the article")
photo = Photo.create(title: "A Beautiful Photo", url: "photo.jpg")
# 创建 Comment 实例,分别属于 article 和 photo
comment1 = Comment.create(content: "Nice article!", commentable: article)
comment2 = Comment.create(content: "Amazing photo!", commentable: photo)
访问 Comment 的关联对象
可以直接通过 commentable
访问 Comment
所属的对象,无论是 Article
还是 Photo
:
comment1.commentable # => #<Article id: 1, title: "An Interesting Article", content: "Content of the article">
comment2.commentable # => #<Photo id: 1, title: "A Beautiful Photo", url: "photo.jpg">
这样,Comment
可以代理到不同的类型,并且我们可以通过 commentable
直接访问它属于的对象。
总结
使用 delegated_type
定义了一个简单而清晰的多态关联,使 Comment
可以属于 Article
或 Photo
。这使得代码结构更加模块化,且容易扩展。如果以后想增加其他类型,只需在 Comment
模型中添加到 types
数组即可。
07 delegated_type 和 polymorphic 的区别
特性 | delegated_type | polymorphic |
---|---|---|
适用关系 | 一对多多态关系 | 一对多和多对多多态关系 |
关系的类型集 | 固定、有限、稳定的类型集 | 灵活、可以随时扩展 |
使用场景 | 代理单一属主类型,简化多态逻辑 | 多态关联较多、类型集多样的关系 |
维护成本 | 当新增类型时需修改代码 | 扩展性强,不需修改代码 |
delegated_type
是一种非常有用的工具,但适用范围有限。它在特定的多态场景中比 polymorphic
更简洁,但无法完全取代 polymorphic
。如果是复杂或灵活需求较高的多态场景,传统的 polymorphic
关联依然更合适。