8

I'm using Rails 3.2.8. I need to create a status record if a status record doesn't already exist. If a status record exists, I don't do anything. So my code is something like this:

user_level_status = UserLevelStatus.where(user_id: user_id, level_id: level_id).first
if !user_level_status
  UserLevelStatus.create(user_id: user_id, level_id: level_id, status: UserLevelStatus::READY)
end

Is there a better way to handle this in Rails/ActiveRecord? Is there an equivalent mechanism as find_or_create_by_? Can I use find_or_create_by_user_id and also check for level_id? I would just be discarding the results so even that's not so elegant.

at.
  • 50,922
  • 104
  • 292
  • 461

4 Answers4

20

Yes there is.

Rails 3

user_level_status = UserLevelStatus.find_or_create_by_user_id_and_level_id(user_id, level_id)

Rails 4:

user_level_status = UserLevelStatus.find_or_create_by(user_id: user_id, level_id: level_id)

And you should set the default status in your model.

Community
  • 1
  • 1
KARASZI István
  • 30,900
  • 8
  • 101
  • 128
  • This is the most elegant solution presented, but I want to be able to create the status record with 1 of many different statuses. Sometimes it needs to be a READY status and sometimes it needs to be some other status. I guess I could check the user_level_status object's status field and if nil, then set it. Is there a way to set the status field within the find_or_create_by_* method? – at. Sep 21 '12 at 16:03
  • as of Rails 4, the dynamically created versions of find_or_create_by_XXX seem to have been deprecated in favor of find_or_create_by(), passing in the name and value. – Jeremy Burton Nov 19 '13 at 20:52
5

You should use

UserLevelStatus.where(user_id: user_id, level_id: level_id).first_or_create(status: UserLevelStatus::READY)
iosctr
  • 471
  • 4
  • 4
2

Use find_or_create_*

hash = {user_id: user_id, level_id: level_id, status: UserLevelStatus::READY}
UserLevelStatus.find_or_create_by_user_id_and_level_id_and_status(hash)

EDITED For your case you should use following

hash = {user_id: user_id, level_id: level_id}
UserLevelStatus.find(:first, :conditions => hash) || UserLevelStatus.create(hash.merge({status: UserLevelStatus::READY}) 
Community
  • 1
  • 1
Salil
  • 46,566
  • 21
  • 122
  • 156
  • This would create a new `UserLevelStatus` if the `status` field is different for the actual `user_id` and `level_id`, wouldn't it? – KARASZI István Sep 21 '12 at 09:32
  • So if there is a validation for the uniqueness on `level_id` with scope of `user_id`, then your solution would create an invalid object. – KARASZI István Sep 21 '12 at 09:38
0

Another alternative is

UserLevelStatus.where(user_id: user_id, level_id: level_id).first ||
  UserLevelStatus.create(user_id: user_id, level_id: level_id, status: UserLevelStatus::READY)
Marlin Pierce
  • 9,931
  • 4
  • 30
  • 52