user3796633
user3796633

Reputation: 11

Rails4 validate uniqueness of multiple columns

I have a model Friendship where :friend_id is also a foreign key as :user_id

friendships id | user_id | friend_id I'm aware that validating the uniqueness of the two fields would be something like this

validates :user_id, :uniqueness => {:scope => :friend_id}

But is there a way to validate

user_id = 1, friend_id = 3
user_id = 3, friend_id = 1 

so that only user_id = 1, friend_id = 3 is stored?

Upvotes: 0

Views: 1134

Answers (1)

Richard Peck
Richard Peck

Reputation: 76774

Join Model

Looks like you're using a join model with Rails - as mentioned in the comments, you'll want to change this to friendship (not friend).

Rails has two types of join mechanism - has_many :through and has_and_belongs_to_many - both allow you to associate many-to-many records, like this:

#app/models/user.rb
Class User < ActiveRecord::Base
   has_many :friendships
   has_many :friends, through: :friendships
end

#app/models/friendship.rb
Class Friendship < ActiveRecord::Base
   belongs_to :friend, class: "User", foreign_key: "friend_id"
   belongs_to :user
end

#friendships
id | user_id | friend_id | created_at | updated_at

I would recommend you have a friendship model (as opposed to a model-less HABTM setup), because HMT allows you to add extra data, such as created_at, updated_at, permissions, etc)


Self Referential

I would also use a self-referential model. This will allow you to keep a single model (for User), and then call @user.friends without an issue.

Whether my above association definitions are correct is another matter; this is how you'd want to set them up.

--

Validation

In terms of validating your associations, I believe you'll be able to use indexes in your DB. This is used often for HABTM tables -

#Migration

# Adding the index can massively speed up join tables. Don't use the
# unique if you allow duplicates.
add_index(:friendships, [:user_id, :friend_id], :unique => true)

This will work for your original case, but not for the second

Upvotes: 1

Related Questions