2017-02-05 64 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方法查詢應該在多態關聯中使用的類的用戶對象。