title: Rails uniqueness constraint on two column values in different models
body: <p>I want to know the best way to have an uniqueness constraint enforced on two related model attributes in rails <strong>that are both no primary keys</strong></p>
  
  <pre><code>class Parent &gt; ApplicationRecord
    has_many :children
    :name
  end
  
  class Child &gt; ApplicationRecord
    :name
  end
  </code></pre>
  
  <p>I want to enforce that (parent.name, child.name) is unique for every parent. e.g. </p>
  
  <ul>
  <li><code>(parent1, child1)</code> and <code>(parent2, child1)</code> is allowed    </li>
  <li><code>(parent1, child1)</code> and <code>(parent1, child1)</code> is a violation</li>
  </ul>
  
  <p>Ideally, I would enforce this in Postgres, however I have only seen the option to add uniqueness constraints on multiple columns of the same table.</p>
  
  <p>Alternatively, I have written a custom validator for rails that does what I want, but this is cumbersome. There needs to be a better solution...</p>
  
  <p>For completeness, here is the constraints validator which requires one to add a <code>children</code> function to a model returning the list of children. </p>
  
  <pre><code>class NamePairValidator &lt; 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) &gt; 1 }.size &gt; 0)
          record.errors[:name] &lt;&lt; 'Path leading to this resource has no unique name'
        end
      end
    end
  end
  </code></pre>
  
  <p>(in Parent.rb)</p>
  
  <pre><code>def children
    {children: :children}
  end
  </code></pre>
  
  <p>Migrations:</p>
  
  <pre><code>class CreateDomains &lt; 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 &lt; 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 &lt; 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
  </code></pre>