How do you validate uniqueness of a pair of ids in Ruby on Rails?

匿名 (未验证) 提交于 2019-12-03 09:02:45

问题:

Suppose the following DB migration in Ruby:

     create_table :question_votes do |t|       t.integer :user_id       t.integer :question_id       t.integer :vote        t.timestamps     end 

Suppose further that I wish the rows in the DB contain unique (user_id, question_id) pairs. What is the right dust to put in the model to accomplish that?

validates_uniqueness_of :user_id, :question_id
seems to simply make rows unique by user id, and unique by question id, instead of unique by the pair.

回答1:

validates_uniqueness_of :user_id, :scope => [:question_id] 

if you needed to include another column (or more), you can add that to the scope as well. Example:

validates_uniqueness_of :user_id, :scope => [:question_id, :some_third_column] 


回答2:

If using mysql, you can do it in the database using a unique index. It's something like:

add_index :question_votes, [:question_id, :user_id], :unique => true 

This is going to raise an exception when you try to save a doubled-up combination of question_id/user_id, so you'll have to experiment and figure out which exception to catch and handle.



回答3:

From RailsGuides. validates works too:

class QuestionVote  { :scope => :question_id } end 


回答4:

Except for writing your own validate method, the best you could do with validates_uniqueness_of is this:

validates_uniqueness_of :user_id, :scope => "question_id" 

This will check that the user_id is unique within all rows with the same question_id as the record you are attempting to insert.

But that's not what you want.

I believe you're looking for the combination of :user_id and :question_id to be unique across the database.

In that case you need to do two things:

  1. Write your own validate method.
  2. Create a constraint in the database because there's still a chance that your app will process two records at the same time.


回答5:

The best way is to use both, since rails isn't 100% reliable when uniqueness validation come thru.

You can use:

  validates :user_id, uniqueness: { scope: :question_id } 

and to be 100% on the safe side, add this validation on your db (MySQL ex)

  add_index :question_votes, [:user_id, :question_id], unique: true 

and then you can handle in your controller using:

  rescue ActiveRecord::RecordNotUnique 

So now you are 100% secure that you won't have a duplicated value :)



回答6:

When you are creating a new record, that doesn't work because the id of your parent model doesn't exist still at moment of validations.

This should to work for you.

class B  :ab end  class AB  :ab    after_validation :validate_uniqueness_b    private   def validate_uniqueness_b     b_ids = ab.map(&:b_id)     unless b_ids.uniq.length.eql? b_ids.length       errors.add(:db, message: "no repeat b's")     end   end end 

In the above code I get all b_id of collection of parameters, then compare if the length between the unique values and obtained b_id are equals.
If are equals means that there are not repeat b_id.

Note: don't forget to add unique in your database's columns.



标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!