Validating Uniqueness of with a Scope
I have a project which has many users through: a project_members association.
Here is the code:
class User < ApplicationRecord
has_many :project_members
has_many :projects, through: :project_members
end
class ProjectMember < ApplicationRecord
belongs_to :project
belongs_to :user
validates :project, uniqueness: { scope: :user}
end
class Project
has_many :project_members
has_many :users, through: :project_members
end
What’s wrong with this?
This protects things simply in the application / or model layer. There are instances - very rare instances, where your app is under tremendous load. You may stumble upon a situation where you have two project members that are identical. Debugging those sorts of things at scale would be a night mare.
You can avoid those problems by adding a database constraint:
class CreateProjectMembers < ActiveRecord::Migration[6.0]
def change
create_table :project_members do |t|
t.references :project, null: false, foreign_key: true
t.references :user, null: false, foreign_key: true
t.timestamps
end
## Add this constraint here:
add_index :project_members, [:user_id, :project_id], unique: true
end
end
That will ensure that the database will absolutely reject any duplicates. Add a database constraint. Then you’re safe. Unfortunately, abstractions are leaky.
(I’ve hacked this together quickly without testing it, so there may be a few errors. Please let me know if you’ve found any!)