Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Rails: Ensure only one boolean field is set to true at a time

I have a Logo model that has fields of name:string, default:boolean. I want the true value to be unique so that only one item in the database can be set to true at once. How do I set my update and new actions in my controller to set all the rest of the values of my logos to false?

Let's say I have the following setup in my database
Model Logo
name:string | default:boolean |
Item1 | true |
Item2 | false |
Item3 | false |

If I change Item2 default value to true, I want it to loop through all logos and set the rest of them to false, so only one is true at once, so it looks like this.

name:string | default:boolean |
Item1 | false |
Item2 | true |
Item3 | false |

Thanks for any help in advance.

like image 728
ruevaughn Avatar asked Apr 13 '12 03:04

ruevaughn


4 Answers

This code is stolen from previous answer and slightly simplified:

def falsify_all_others
  Item.where('id != ?', self.id).update_all("default = 'false'")
end

You can use this method in before_save callback in your model.

Actually, it is better to "falsify" only records which values are 'true', like this:

Item.where('id != ? and default', self.id).update_all("default = 'false'")

UPDATE: to keep code DRY, use self.class instead of Item:

self.class.where('id != ? and default', self.id).update_all("default = 'false'")
like image 54
denis.peplin Avatar answered Nov 19 '22 00:11

denis.peplin


I think it's good to check if the one you save is true before you falsify others. Otherwise you falsify everyone when you save a record that isn't active.

def falsify_all_others
    if self.default
        self.class.where('id != ? and default', self.id).update_all("default = 'false'")
    end
end
like image 33
Philip Avatar answered Nov 18 '22 22:11

Philip


In your controller code you could do something like this.... please note you're probably taking Item2 as a param[...] so you can interchange that below

@items_to_be_falsified = Item.where('id != ?', Item2.id)

@items_to_be_falsified.each do |item|
  item.default = false
  item.save
end

Please note when you get this working, its good practice to move this into the model, make it into a function and call it like Item2.falsify_all_others like below

def falsify_all_others
  Item.where('id != ?', self.id).each do |item|
    item.default = false
    item.save
  end
end

Enjoy!

like image 3
Norto23 Avatar answered Nov 18 '22 22:11

Norto23


I also recommend falsifying all your records then making them true.

add_column :users, :name ,:boolean, default: false
like image 3
CrazyCoderMonkey Avatar answered Nov 18 '22 22:11

CrazyCoderMonkey