Ruby on rails学习:多态关联(Polymorphic)
Polymorphic Associations in Rails
背景
在Rails中设置API时,确定如何使用活动记录关联将所有模型相互连接是至关重要的。多态关联稍微高级一些,但在应用程序变得更复杂时非常有用。
什么是多态性?
这对我来说是一个新概念,到目前为止,我已经了解到多态性是面向对象编程中的一个核心概念,可以在许多不同的上下文中使用。本博客将在构建Rails API的上下文中重点介绍这个概念。我们可以将这一概念分解为希腊语,从而对其有更清晰的理解:
如果某事物是多态的,它有多种形式。例如,Ruby中的数字可以是整数(整数),也可以是浮点数(十进制)。Rails中的多态关联允许您通过一个关联声明将单个模型与多个模型关联起来(而不是将多个属于关联)。
这在哪里有用?
假设您正在构建一个应用程序,允许用户发布内容、对其他帖子发表评论,以及与其他帖子类似的内容。我们都熟悉的非常常见的特性。现在,您希望将like功能扩展到comments,允许用户喜欢单独的注释。您的ERD可能(也可能不)如下所示:
类似这样的功能可以很好地工作,但是如果您希望在应用程序中扩展like功能,以在将来应用到潜在的新模型,则需要为您引入的每个新模型在数据库中创建一个单独的like表。超时这可能会不必要地挤满您的数据库,并最终成为一项重复性任务。
多态关联
多态关联与多态关联我们可以创建一个poly_likes表,它将充当我们希望关联likes的其他模型之间的接口。这不是最漂亮的ERD,但看起来好一点。现在,每当我们想引入一个新模型时,我们不需要创建一个单独的likes表来支持该模型。
创建poly_likes表
创建poly_likes表要在终端中创建我们的新模型,我们将包括一个有趣的列/值对。
rails g model PolyLike user:belogs_to likeable:references{polymorphic}
这次迁移向我们展示了什么?
让我们运行迁移并签出模式
models/ploy_like.rb
多态关联的命名约定
如果您熟悉Rails,您就会知道坚持命名约定是Rails魔力的一部分。为了完成所有这些工作,Rails使用带类名的“-able”来命名多态关联。基本上,我们在这里做的是发表评论和“讨人喜欢”的帖子。让我们确保我们的其他模型都已设置好,以实现所有这些功能:
如果我错了,请纠正我,但我在用户模型中遗漏了“as:likeable”
,因为这会让用户变得可爱,而在这种情况下,这不是我想要的(谁知道也许你想让用户喜欢)。在这种情况下,我只希望多边形与特定用户相关联。
在控制台中进行测试如果您仔细查看正在生成的查询,它将查找具有用户id的多边形,这正是我想要的。在这些示例中,我没有创建任何类似poly_
的种子,因此我们得到的是一个空数组,但我们可以关注查询来查看这些关联是如何工作的。
我的实际项目操作
Rails模型中的关系有一对一,一对多还有多对多,这些关联关系都比较直观,除此之外Rails还支持多态关联,所谓的多态关联其实可以概括为一个模型同时与多个其它模型之间发生一对多的关联。
我的场景(1:N,1对多的情况)
works:我的作品表,开的的一些组件包
其它表:可能也有图片
pictures: 各种图片
生成相应的 migration
# workes 表
rails g model Work title summary content
# pictures 表
rails g model Picture url imageable:references{polymorphic}
models/concerns/imageable.rb
添加公用部分
module Imageable
extend ActiveSupport::Concern
included do
has_many :pictures, as: :imageable
end
end
需要有图片work
的表
# models/work.rb
class Work < ApplicationRecord
include Imageable
# ...
end
我的场景(N:N,多对多的情况)
基本的migration
# 两个含有tag的表
rails g model Episode name:string content:text
rails g model Article name:string content:text
# tag/tagging 表
rails g model Tag name:string
rails g model Tagging tag:references taggable:references{polymorphic}
几个关键的model
.
├── application_record.rb
├── article.rb
├── concerns
│ └── taggable.rb
├── episode.rb
├── tag.rb
└── tagging.rb
1 directory, 6 files
models/article.rb
与 models/episode.rb
class Article < ApplicationRecord
include Taggable
def self.tagged_with(name)
Tag.find_by!(name: name).articles
end
end
class Episode < ApplicationRecord
include Taggable
def self.tagged_with(name)
Tag.find_by!(name: name).episodes
end
end
公用的 taggable.rb
module Taggable
extend ActiveSupport::Concern
included do
has_many :taggings, :as => :taggable
has_many :tags, :through => :taggings
end
def tag_list
tags.map(&:name).join(', ')
end
end
models/tag.rb、models/tagging.rb
# models/tagg.rb
class Tag < ApplicationRecord
has_many :taggings
has_many :episodes, through: :taggings, source: :taggable, source_type: Episode
has_many :articles, through: :taggings, source: :taggable, source_type: Article
end
# models/tagging.rb
class Tagging < ApplicationRecord
belongs_to :tag
belongs_to :taggable, polymorphic: true
end
参考
- https://dev.to/jblengino510/polymorphic-associations-in-rails-167o
- https://guides.rubyonrails.org/association_basics.html#polymorphic-associations
- https://semaphoreci.com/blog/2017/08/16/polymorphic-associations-in-rails.html
- https://www.culttt.com/2016/01/13/creating-polymorphic-relationships-in-ruby-on-rails
- https://github.com/afeiship/rails-module-tagging-multiple