2017-02-05 148 views
1

我有一个Rails应用程序,用户可以跟踪播放节目和剧集。多态belongs_to与动态类的关联(取自其他关系)

为了简化跟踪的(没有)观看节目的过程中,用户能够将其帐户与其他服务同步。他们可以在他们的用户设置页面中选择他们想要同步的服务。

要同步,我从其他服务加载他们的个人资料,然后通过检测从最后的同步变化,并相应地更新DB的算法运行。为了存储最后的同步状态,对于每个Show ID,我创建一个“UsersSyncIdStatus”对象,该对象存储节目ID以及同步服务中该节目的当前状态。

注意,服务不使用同一个节目ID作为我的网站,这意味着我有,我可以用它来从他们的表演标识“转换”我的节目ID的表。由于每个服务提供的信息不同,它们必须存储在不同的表中。

现在,这是(的简化版本)DB模式的设置方式:

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

我并不真正理解你没有尝试多态关系,甚至是'has_many:through'的理由。看起来'sync_method'就是你通常在模型中找到的东西。请记住像'has_many'这样的关联是Ruby语句,可以用条件来控制。看起来你很难将其编码到数据库中,但为什么? –

+0

多态关联需要向包含关系模型名称的'users_sync_id_statuses'表中添加文本列。由于单个用户可能有成千上万个'users_sync_id_statuses'行,并且所有这些行都与同一个模型类有关系,所以将它们添加到其中的每一个都是数据库空间的巨大浪费......问题是每个用户可能有不同的关系模型,我想要一个关系自动查询关系模型的用户对象,而不是依赖每个行存储的关系。 – rpinheiro

+0

我看到了,说实话我从来没有结束使用这些。但我想我的建议归结为试图从数据库中删除关系声明;只保留外键,并使用ActiveRecord关联来构建查询。你对这种方法有所保留吗? –

回答

1

我不认为香草的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 

有了这个猴子补丁,belongs_to多态关联不会假定类型字段是一个变量字符列,而是将其称为对象上的方法。这意味着你可以很容易地添加自己的动态行为,而不会破坏任何旧行为(AFAIK,没有对此做过深入的测试)。

对于我的具体使用情况,我有sync_id_obj_type方法查询应该在多态关联中使用的类的用户对象。