[英]:polymorphic/:through ActiveRecord associations (Rails 3.2)
[英]Rails Combining and Sorting ActiveRecord Relations when using polymorphic associations
我正在研究一个小型收集跟踪器,我觉得 STI 可以真正简化这个问题,但似乎普遍的共识是尽可能避免 STI,所以我已经把我的模型分开了。 目前,它们都是相同的,但我确实有一些不同的元数据,我可以看到自己附加到它们上。
无论如何,根是一个Platform
,它有许多Games
、 Systems
、 Peripherals
等,我试图在一个可过滤、可排序和可搜索的动态表中的视图上显示所有这些关系。
例如,查询可以是@platform.collectables.search(q).order(:name)
。
# Schema: platforms[ id, name ]
class Platform < ApplicationRecord
has_many :games
has_many :systems
has_many :peripherals
end
# Schema: games[ id, platform_id, name ]
class Game < ApplicationRecord
belongs_to :platform
end
# Schema: systems[ id, platform_id, name ]
class System < ApplicationRecord
belongs_to :platform
end
# Schema: peripherals[ id, platform_id, name ]
class Peripheral < ApplicationRecord
belongs_to :platform
end
在上面,当我将它们添加到Collection
时,多态性开始发挥作用:
# Schema: collections[ id, user_id, collectable_type, collectable_id ]
class Collection < ApplicationRecord
belongs_to :user
belongs_to :collectable, polymorphic: true
end
现在,当我查看一个Platform
时,我希望看到它的所有游戏、系统和外围设备,我称之为收藏品。 我将如何查询所有这些同时能够作为一个整体进行排序(即:“名称 ASC”)。 下面在理论上有效,但这会改变与 Array 的关系,这会阻止我在数据库级别进行进一步过滤、搜索或重新排序,因此我无法在另一个scope
或order
上添加标签。
class Platform < ApplicationRecord
...
def collectables
games + systems + peripherals
end
end
我偶然发现了委托类型,这听起来像是朝着我正在寻找的方向迈出的一步,但也许我错过了一些东西。
我很想尝试 STI 路线,我没有看到这些模型有太大的差异,并且不同的东西可以存储在 JSONB 列中,因为它主要只是用于填充视图的元数据,而不是真正的搜索。 基本上是这样的 model 但它似乎很不受欢迎,我觉得我一定错过了一些东西。
# Schema: collectables[ id, platform_id, type, name, data ]
class Collectable < ApplicationRecord
belongs_to :platform
end
class Platform < ApplicationRecord
has_many :collectables
def games
collectables.where(type: 'Game')
end
def systems
collectables.where(type: 'System')
end
...
end
这里的一个解决方案是委托类型(一个相对较新的 Rails 特性),它基本上可以通过多态性概括为多表 Inheritance。 所以你有一个包含共享属性的基表,但每个 class 也有自己的表 - 从而避免了 STI 的一些关键问题。
# app/models/concerns/collectable.rb
# This module defines shared behavior for the collectable "subtypes"
module Collectable
TYPES = %w{ Game System Peripheral }
extend ActiveSupport::Concern
included do
has_one :base_collectable, as: :collectable
accepts_nested_attributes_for :base_collectable
end
end
# This model contains the base attributes shared by all the collectable types
# rails g model base_collectable name collectable_type collectable_id:bigint
class BaseCollectable < ApplicationRecord
# this sets up a polymorhic association
delegated_type :collectable, types: Collectable::TYPES
end
class Game < ApplicationRecord
include Collectable
end
class Peripheral < ApplicationRecord
include Collectable
end
class System < ApplicationRecord
include Collectable
end
然后,您可以通过连接 model 设置多对多关联:
class Collection < ApplicationRecord
belongs_to :user
has_many :collection_items
has_many :base_collectables, through: :collection_items
has_many :games,
through: :base_collectables,
source_type: 'Game'
has_many :peripherals,
through: :base_collectables,
source_type: 'Peripheral'
has_many :systems,
through: :base_collectables,
source_type: 'Systems'
end
class CollectionItem < ApplicationRecord
belongs_to :collection
belongs_to :base_collectable
end
# This model contains the base attributes shared by all the collectable types
# rails g model base_collectable name collectable_type collectable_id:bigint
class BaseCollectable < ApplicationRecord
# this sets up a polymorhic association
delegated_type :collectable, types: %w{ Game System Peripheral }
has_many :collection_items
has_many :collections, through: :collection_items
end
这使您可以将其视为同质集合并按base_collectables
表上的列排序。
多态关联是围绕 object 关系阻抗不匹配的肮脏作弊方法,其中一个列具有主键引用,另一个列存储 class 名称。 它不是实际的外键,因为如果不先将记录从数据库中拉出,则无法解决关联。
这意味着您无法has_many:collectables, through: :base_collectables
。 而且您不能急切地加载所有委托类型或按游戏、外围设备或系统表上的列对整个集合进行排序。
它适用于特定类型,例如:
has_many :games,
through: :base_collectables,
source_type: 'Game'
由于可以事先知道该表。
这对于基于表且不面向 object 且外键形式的关系指向单个表的关系数据库而言,这简直是一个难以破解的难题。
这里的关键问题是,您填充到 JSON 列中的所有数据基本上都是无模式的,并且您受到 JSON 支持的 6 种类型的限制(其中都不是日期或体面的数字类型)并且使用数据可以是极其困难的。
JSON 的主要特点是简单,它作为一种传输格式工作得很好。 作为一种数据存储格式,它并不是那么好。
这是从 EAV 表或将 YAML/JSON 序列化到 varchar 列中的一大进步,但它仍然有很大的警告。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.