2017-10-17 58 views
0

我正在構建一個系統,該模型記錄中有一些記錄,這些記錄是所有帳戶都可查看的,並且可以稍後複製以創建個人帳戶的實時記錄。Rails:在同一張桌子上運行的多個模型

這個設計決策背後的原因是模板記錄和活動記錄共享相同代碼的95%,所以我不想創建一個單獨的表來跟蹤大多數相同的字段。

舉例來說,我有一個workflows表:

  • ID:整數
  • ACCOUNT_ID:整數
  • 名:字符串(必填)
  • 的is_a_template:布爾(默認:false)
  • is_in_template_library:布爾(默認值:false)

在這張表中,我有一些記錄是模板。當我去創建一個新的直播實錄,我可以用一個模板記錄:

# workflows_controller.rb (pseudo-code, not fully tested) 
def create 
    @workflow_template = Workflow.where(is_a_template: true).find_by(id: params[:workflow_template_id]) 
    @workflow = current_account.workflows.new(workflow_params.merge(@workflow_template.dup)) 

    if @workflow.save 
    ... 
    else 
    ... 
    end 
end 

當我建立了更多的功能,我發現我真的很需要那桌子上不同的動作2個不同的模型。其他還有一些差異,但低於上市,足以顯示出差異:

class Workflow < ApplicationRecord 
    default_scope -> { where(is_a_template: false) } 

    belongs_to :account 

    validates :account, presence: true 
    validates :name, presence: true 
end 

class WorkflowTemplate < ApplicationRecord 
    default_scope -> { where(is_a_template: true) } 

    validates :name, presence: true 
end 

class WorkflowLibraryTemplate < ApplicationRecord 
    default_scope -> { where(is_a_template: true, is_in_template_library: true) } 

    validates :name, presence: true 
end 

正如你所看到的,workflows表有記錄3種不同的「類型」:

  1. 「活」屬於帳戶的工作流程
  2. 模板工作流也屬於一個帳戶並被複制以創建「實時」工作流程
  3. 不屬於某個帳戶且可以被任何帳戶查看的庫模板工作流程,因此他們可以複製它們到自己的模板

問題列表

我試圖搞清楚,是我打破什麼點了這個單表到多個表,對保持相同的表格,並具有多個模型,或者有什麼解決方案來解決這個問題?

令人沮喪的部分是,有5 +其他表是workflows表的「兒童」協會的5+其他表。所以如果我決定每個表都需要單獨的表格,我最終會從6個表格變成18個類似的表格,每次我添加一個字段時,我都必須對錶格的所有3個「版本」執行此操作。

因此,我很不情願下去多個表的路線。

如果我保留單個表格和多個模型,那麼我會在表格中得到不同版本的數據,這不是世界末日。我只通過我的應用程序(或未來的API I控件)與數據交互。

我在考慮的另一個解決方案是在表中添加一個role:string字段,該字段非常類似於Rails中的type字段。然而,我不想使用STI,因爲Rails中有太多的烘焙需求,我不想與之衝突。

什麼我構想是:

class Workflow < ApplicationRecord 
    scope :templates, -> { where(role: "template") } 
    scope :library_templates, -> { where(role: "library_template") } 

    validates :account, presence: true, if: :account_required? 
    validates :name, presence: true 

    # If record.role matches one of these, account is required 
    def account_required 
    ["live", "template"].include?(role.to_s.downcase) 
    end 
end 

這似乎解決幾個的問題,使我有1臺和1種模式,而是開始在模型條件邏輯,這似乎是一個不好的主意給我。

有沒有一種更清潔的方式來實現表內的模板系統?

+0

您遇到或期望遇到與您目前的方法有什麼特別的困難? – EJ2015

+0

我現在在製作的東西是一堆非常醜陋的條件邏輯。如果我採用STI方法,我將不得不創建幾個不同的模型文件,分別爲每個模型實現邏輯。不過,我認爲這比維護多張表更容易。去STI的一個大問題是,我必須爲所有的關聯設置不同的模型文件,這樣可以添加10個以上的模型文件。 –

回答

3

所以你在這裏看到的是所謂的Single Table Inheritance。這些模型被稱爲polymorphic。至於什麼時候把STI分解成不同的表格,答案是:當你有足夠的分歧時,你開始有專門的專欄。 STI的問題是讓我們說WorkFlows和WorkFlowTemplate開始分歧。也許模板開始獲得很多額外的屬性作爲不符合普通舊工作流的列。現在你有很多數據是空的一個類(或不需要),對另一個有用和必要。在這種情況下,我可能會把桌子分開。你應該問的真正問題是:

  1. 這些模型在需求方面會相差多遠?

  2. 這會發生多久?

如果在我的應用程序的生命週期內發生得很晚:

會不會是難以/不可能遷移這些表由於有多少行/有多少數據我有嗎?

編輯:

有沒有更清潔的方法?在這種特殊情況下,我不認爲給定模板和該模板的副本,可能會彼此緊密耦合。

+0

記錄總是95%+相似;如果模板變得非常不同,那麼我會將它們分解成自己的資源,但我永遠不會看到發生這種情況。目前,它們僅在'account_id,is_a_template,is_in_template_library'中有所不同,如果我使用「角色/類型」字段,則會摺疊模板字段。我對2-3個不同的領域都很滿意,我沒有看到超過5的,但我不能總是告訴未來:) –

+0

沒錯。我還會補充一點:給自己留個空間。梅斯說,如果你可以避免做出決定,那麼做,如果可以的話,打個招呼。 – apanzerj

0

我採取的方法是責任分解。

分解的責任

現在,你有3個不同的數據來源和2種不同的方法來創建/驗證工作流程。

爲了達到這個目的,您可以引入RepositoriesFormObject的概念。

存儲庫是包裝對象,它將抽象查詢模型的方式。它不關心它是同一個表還是多個表。它只知道如何獲取數據。

例如:

class Workflow < ApplicationRecord 
    belongs_to :account 
end 

class WorkflowRepository 
    def self.all 
    Workflow.where(is_a_template: false) 
    end 
end 

class WorkflowTemplateRepository 
    def self.all 
    Workflow.where(is_a_template: true) 
    end 
end 

class WorkflowLibraryTemplateRepository 
    def self.all 
    Workflow.where(is_a_template: true, is_in_template_library: true) 
    end 
end 

這確保了無論你在未來做的決定,你會不會改變代碼的其他部分。

所以,現在讓我們來討論FormObject

FormObject將抽象的方式,你驗證,建立自己的對象。它現在可能不是一個很好的補充,但通常會從長遠來看付出代價。

例如

class WorkFlowForm 
    include ActiveModel::Model 

    attr_accessor(
    :name, 
    :another_attribute, 
    :extra_attribute, 
    :account 
) 

    validates :account, presence: true 
    validates :name, presence: true 

    def create 
    if valid? 
     account.workflows.create(
     name: name, is_a_template: false, 
     is_in_template_library: false, extra_attribute: extra_attribute) 
    end 
    end 
end 

class WorkflowTemplateForm 
    include ActiveModel::Model 

    attr_accessor(
    :name, 
    :another_attribute, 
    :extra_attribute 
) 

    validates :name, presence: true 

    def create 
    if valid? 
     Workflow.create(
     name: name, is_a_template: true, 
     is_in_template_library: false, extra_attribute: extra_attribute) 
    end 
    end 
end 

class WorkflowLibraryTemplateForm 
    include ActiveModel::Model 

    attr_accessor(
    :name, 
    :another_attribute, 
    :extra_attribute 
) 

    validates :name, presence: true 

    def create 
    if valid? 
     Workflow.create(
     name: name, is_a_template: true, 
     is_in_template_library: true, extra_attribute: extra_attribute) 
    end 
    end 
end 

這種方法與可擴有助於爲一切都是一個單獨的對象。 唯一的缺點是,在我的小見解中,WorkflowTemplate和WorkflowLibraryTemplate與額外的布爾值是相同的語義,但這是一個可選的事情,你可以採取或離開。

相關問題