Rails custom validation - Only one record can be true

11,350

Solution 1

You also need to check against the ID if the record is already persisted. Otherwise, saving the active game again would not succeed because there is an existing active game, which happens to be itself.

validate :only_one_active_game
scope :active, where(:active => true)

protected

def only_one_active_game
  return unless active?

  matches = Game.active
  if persisted?
    matches = matches.where('id != ?', id)
  end
  if matches.exists?
    errors.add(:active, 'cannot have another active game')
  end
end

Solution 2

I think you can just check the uniqueness of active_game when it is true.

validates_uniqueness_of :active_game, if: :active_game

Share:
11,350

Related videos on Youtube

raphael_turtle
Author by

raphael_turtle

Updated on June 25, 2022

Comments

  • raphael_turtle
    raphael_turtle almost 2 years

    I'm trying to write a validation where only one record can be true. I have a 'game' model with an 'active' boolean column, only one game can be active at any time, so if someone tries to create a new 'game' record when there is an already active game then they should get an error. Below is what I currently have but isn't working!

    validate :active_game
    
      def active_game
        if active == true && Game.find_by(active: true) == true
           errors[:name] = "a game is already active!"
        end
      end
    
    • Tim S.
      Tim S. over 10 years
      What do you mean it isn't working?
  • raphael_turtle
    raphael_turtle over 10 years
    I get an error from your code: PG::UndefinedFunction: ERROR: operator does not exist: integer <> LINE 1: ...petitions" WHERE "games"."active" = 't' AND (id != )
  • lightswitch05
    lightswitch05 over 10 years
    He is missing a ?. 'id != ' should be 'id != ?'
  • kristinalim
    kristinalim over 10 years
    Oops, sorry! You're right, I missed the question mark. Edited.
  • kristinalim
    kristinalim over 10 years
    The id != ? part is problematic when id is NULL (i.e. not persisted). sqlfiddle.com/#!12/ae03e/3
  • kristinalim
    kristinalim over 10 years
    Sorry that wasn't clear. SELECT * FROM books WHERE id != NULL AND active IS TRUE; returns a false negative (doesn't find the existing record) if there is already an active game. sqlfiddle.com/#!12/7d865/3 Not sure if this holds true for most DBMSes, but the standard for checking that there is a value is to use "IS NOT NULL". Correct me if I am wrong.