views:

122

answers:

3

I have a Rails app which has a table called friendrequests. It looks like this:

user1_id:integer user2_id:integer hasaccepted:boolean

I'm creating an option to add friends, but a friendrequest can only be send once. So you cannot have something like this in the database's data:

user1_id | user2_id | hasaccepted
       1 |        2 |       false
       1 |        2 |       false

or

user1_id | user2_id | hasaccepted
       1 |        2 |       false
       2 |        1 |       false

The user1_id/user2_id combination must be unique, not the columns themselves, so this would be possible:

user1_id | user2_id | hasaccepted
       1 |        2 |       false
       1 |        3 |       false

Is it possible to define this in a model? How can I do this?

A: 

For the first case, in your FriendRequest model, use validates_uniqueness_of(:user1_id, :scope => :user2_id). You might also need the reverse. For the second case I'd override #validate in FriendRequest and do a check in there (see the API docs for details on how the method should perform).

Chris
A: 

Try this:

  validates_each :user1_id, :user2_id  do |record, attr, value|
    if FriendRequest.exists?( :user1_id => [record.user1_id, record.user2_id], 
               :user2_id => [record.user1_id, record.user2_id])
        record.errors.add attr, 'Duplicate friend request'
    end
  end
KandadaBoggu
A: 

I would also make sure to add an index to the database itself. Validation checks in the model can't be relied upon to preserve data integrity because there are edge cases in a concurrent system where the validation will not protect you.

eg, another record may be inserted after the validation check, but before the new record is created:

  1. Model A: Validate (pass)
  2. Model B: Validate (pass)
  3. Model A: Insert (success)
  4. Model B: Insert (success: bad data)
Toby Hede