我有一个Rails应用程序,用户可以跟踪播出节目和剧集。
为了简化跟踪(尚未)观看节目的过程,用户可以将其帐户与其他服务同步。他们可以在用户设置页面中选择要与之同步的服务。
要进行同步,我从其他服务加载其配置文件,然后通过检测上次同步更改的算法运行它,并相应地更新DB。为了存储最后的同步状态,对于每个Show ID,我创建一个" UsersSyncIdStatus"存储show ID的对象,以及同步服务中该节目的当前状态。
请注意,这些服务不会使用与我的网站相同的显示ID,这意味着我有一个表,我可以使用它来转换"从他们的节目ID到我的节目ID。由于每个服务提供的信息不同,因此必须将它们存储在不同的表中。
现在,这是(简化版本)如何设置数据库架构:
create_table "service1_ids", force: :cascade do |t|
t.integer "service_id", null: false
t.integer "show_id", null: false
[...]
end
create_table "service2_ids", force: :cascade do |t|
t.integer "service_id", null: false
t.integer "show_id", null: false
[...]
end
create_table "users_sync_id_statuses", force: :cascade do |t|
t.integer "user_id"
t.integer "service_id", null: false
t.integer "sync_status", default: 0, null: false
t.datetime "sync_date", null: false
[...]
end
create_table "users", force: :cascade do |t|
[...]
t.datetime "synced_at"
t.boolean "sync_enabled", default: false, null: false
t.integer "sync_method", default: 0, null: false
[...]
end
特别是,users.sync_method
是一个枚举,用于存储用户为同步选择的服务:
SYNC_METHODS = {
0 => {
symbol: :service1,
name: 'Service1',
model_name: 'Service1Id',
show_scope: :service1_ids
}
1 => {
symbol: :service2,
name: 'Service2',
model_name: 'Service2Id',
show_scope: :service2_ids
}
}
这意味着只需执行SyncHelper::SYNC_METHODS[current_user.sync_method][:model_name]
即可轻松了解特定用户的ID的型号名称。
现在,问题是,如何在" users_sync_id_statuses"之间建立关系?和" serviceN_ids"?为了知道" service_id"列对应,我必须要问'用户模型。
我目前已将其作为一种方法实施:
class User
def sync_method_hash
SyncHelper::SYNC_METHODS[self.sync_method]
end
def sync_method_model
self.sync_method_hash[:model_name].constantize
end
end
class UsersSyncIdStatus
def service_id_obj
self.user.sync_method_model.where(service_id: self.service_id).first
end
end
但是,UsersSyncIdStatus.service_id_obj
是一种方法,而不是一种关系,这意味着我无法完成关系所允许的所有花哨的东西。例如,我无法轻松获取特定用户的UsersSyncIdStatus并显示ID:
current_user.sync_id_statuses.where(service_id_obj: {show_id: 123}).first
我可以把它变成一个多态关系,但我真的不想添加一个文本列来包含类名,当它是一个"常量"从每个用户的角度来看(对于用户切换同步服务,该用户的所有UsersSyncIdStatuses都被销毁,因此用户的UsersSyncIdStatuses中的服务类型永远不会超过1个。)
有什么想法吗?提前谢谢!
答案 0 :(得分:2)
我认为vanilla Rails 5不支持我想做的事情,如果我错了,请有人纠正我。
然而,在对Rails如何实现多态关系进行一些研究后,我能够相对容易地修补Rails 5以添加此功能:
config/initializers/belongs_to_polymorphic_type_send.rb
:
# Modified from: rails/activerecord/lib/active_record/associations/belongs_to_polymorphic_association.rb
module ActiveRecord
# = Active Record Belongs To Polymorphic Association
module Associations
class BelongsToPolymorphicAssociation < BelongsToAssociation #:nodoc:
def klass
type = owner.send(reflection.foreign_type)
type.presence && (type.respond_to?(:constantize) ? type.constantize : type)
end
end
end
end
app/models/users_sync_id_status.rb
:
class UsersSyncIdStatus
belongs_to :service_id_obj, polymorphic: true, foreign_key: :service_id, primary_key: :service_id
def service_id_obj_type
self.user.sync_method_model
end
end
使用这个monkey-patch,belongs_to多态关联不会假设type字段是varchar列,而是将其称为对象上的方法。这意味着您可以非常轻松地添加自己的动态行为,而不会破坏任何旧行为(AFAIK,没有对此进行密集测试)。
对于我的特定用例,我使用sync_id_obj_type方法查询应该在多态关联中使用的类的用户对象。