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

前端 未结 6 2042
梦如初夏
梦如初夏 2020-12-04 17:31

Suppose the following DB migration in Ruby:

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

          


        
相关标签:
6条回答
  • 2020-12-04 18:00

    From RailsGuides. validates works too:

    class QuestionVote < ActiveRecord::Base
      validates :user_id, :uniqueness => { :scope => :question_id }
    end
    
    0 讨论(0)
  • 2020-12-04 18:02

    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 < ActiveRecord::Base
      has_many :ab
      has_many :a, :through => :ab
    end
    
    class AB < ActiveRecord::Base
      belongs_to :b
      belongs_to :a
    end
    
    class A < ActiveRecord::Base
      has_many :ab
      has_many :b, :through => :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.

    0 讨论(0)
  • 2020-12-04 18:06

    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 :)

    0 讨论(0)
  • 2020-12-04 18:08
    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]
    
    0 讨论(0)
  • 2020-12-04 18:09

    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.
    0 讨论(0)
  • 2020-12-04 18:25

    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.

    0 讨论(0)
提交回复
热议问题