2017-07-20 26 views
1

我想知道有沒有強制執行了對兩個相關模型的唯一性約束的最好方式,它們都沒有主鍵軌道屬性Rails的兩個值唯一性約束不同型號

class Parent > ApplicationRecord 
    has_many :children 
    :name 
end 

class Child > ApplicationRecord 
    :name 
end 

我想以強制(parent.name,child.name)對每個父級都是唯一的。例如

  • (parent1, child1)(parent2, child1)允許
  • (parent1, child1)(parent1, child1)違反

理想情況下,我會在Postgres的強制執行這一點,但是我只觀察到在的多個列中添加唯一性約束的選項同一張桌子。

另外,我寫了一個自定義驗證器的軌道,做我想要的,但這很麻煩。需要更好的解決方案...

爲了完整起見,下面是約束驗證器,它需要將children函數添加到返回兒童列表的模型中。

class NamePairValidator < ActiveModel::Validator 
    def validate(record) 
    record.children.values.each do |model_children| 
     names = model_children.to_a.collect {|model| model.name} 
     if (names.select{|name| names.count(name) > 1 }.size > 0) 
     record.errors[:name] << 'Path leading to this resource has no unique name' 
     end 
    end 
    end 
end 

(在Parent.rb)

def children 
    {children: :children} 
end 

遷移:

class CreateDomains < ActiveRecord::Migration[5.0] 
    def change 
    create_table :domains do |t| 
     t.string :name 
     t.string :domain_type 
     t.timestamps 
    end 
    end 
end 

class CreateSubjects < ActiveRecord::Migration[5.0] 
    def change 
    create_table :subjects do |t| 
     t.string  :name 
     t.string  :subject_type 
     t.timestamps 
    end 
    end 
end 

class CreateJoinTableDomainSubject < ActiveRecord::Migration[5.0] 
    def change 
    create_join_table :domains, :subjects do |t| 
     t.index [:domain_id, :subject_id] 
     t.index [:subject_id, :domain_id] 
    end 
    end 
end 
+0

您可以展示如何爲您的模型編寫遷移文件?因爲您還可以添加索引以確保數據庫級別的完整性,例如https://robots.thoughtbot.com/the-perils-of-uniqueness-validations –

+0

如果您想在數據庫級別執行此操作,則可以更改'parent_id'列和'child_id'到'parent_name'和'child_name'列中,併爲它們添加唯一約束。對於一些ActiveRecord hackery,可以將這些列用作外鍵。我不確定這個解決方案只是一個想法。 – Ptr

+0

Thx!我認爲你有一個數據透視表!這就是爲什麼Nithin的答案不起作用。請看看這個鏈接:http://guides.rubyonrails.org/association_basics.html#the-has-many-through-association。它可能會幫助你的語法和關係 –

回答

0

Insipered由紅寶石的官方文檔的the-has-many-through-association軌道上:

class CreateAppointments < ActiveRecord::Migration[5.0] 
    def change 
    create_table :domains do |t| 
     t.string :name, null: false 
     t.string :domain_type 
     t.timestamps 
    end 

    create_table :subjects do |t| 
     t.string  :name, null: false 
     t.string  :subject_type 
     t.timestamps 
    end 

    create_table :fields do |t| 
     t.belongs_to :domain, index: true 
     t.belongs_to :subject, index: true 
     t.timestamps 
    end 
    end 
end 

注意

  • 我把開始重新命名您的模型JoinTableDomainSubject通過Field更具可讀性。

  • 我也強迫name字段不是nil來檢查唯一性。 (在兩個模型添加在遷移文件null: falsevalidates :name, presence: true

現在專用類:

class Subject < ApplicationRecord 
    has_many :fields 
    has_many :domains, through: :fields 

    validates :name, presence: true 
end 

class Domain < ApplicationRecord 
    has_many :fields 
    has_many :subjects, through: :fields 

    validates :name, presence: true 
end 

class Field < ApplicationRecord 
    belongs_to :domain 
    belongs_to :subject 

    validate :domain_and_subject_names_uniqueness 

    private 

    def domain_and_subject_names_uniqueness 
    if class.includes(:domain, subject) 
      .where(domain: { name: domain.name }, subject: { name: subject.name }) 
      .exists? 
     errors.add :field, 'duplicity on names' 
    end 
    end 
end 

由於模型是相關聯的,我可以用Field.first.domain訪問給定FieldDomain模型和反之亦然

+0

添加主題到域的用法已更改。我做了:'@ domain.ubjects << Subject.new(params) @ domain.save'。這似乎不工作了... –

+0

什麼是錯誤?你有沒有嘗試申請我的代碼? –

+0

是的。出現此錯誤:'SubjectsController#create#'中的NoMethodError# '未定義的方法subject_name'for#您是否意思?主題' –