17

I have the following:

@permission = @group.permissions.create(
  :user_id => @user.id,
  :role_id => 2,
  :creator_id => current_user.id)

How can I update that to be find_or_create, so that if this record already exists, it's assigned to @permission, and if it doesn't exist, the record is created?

AnApprentice
  • 108,152
  • 195
  • 629
  • 1,012

4 Answers4

52

While the accepted answer is correct it's important to note that in Rails 4 this syntax will be changing (and the hash syntax). You should be writing the following:

@permission = Permission.where(
  user_id: @user.id, 
  role_id: 2, 
  creator_id: current_user.id).first_or_create

Which actually looks much closer to your original method! See the sub-section Deprecated Finders for more details.

Gavin Miller
  • 43,168
  • 21
  • 122
  • 188
  • 4
    You know what else will be changing? The old hash syntax. You might want to update your answer. Should be, for example: `user_id: @user.id`. – Damien Roche Jan 25 '13 at 06:09
  • 2
    That would be a change to Ruby not Rails ;) Might want to update your comment to reflect this...and unless you are close friends with developers in charge of completely removing the old hash rocket syntax from Ruby, I would retract your statement completely. The developer's post at the following SO link explains why, and although this was written in 2012, it is still valid: http://stackoverflow.com/a/10004344/1629012 – mkralla11 Dec 05 '13 at 20:44
19

Related topic:

find_or_create_by in Rails 3 and updating for creating records

You can extend ActiveRecord with your own update_or_create method (see related topic) and then you can use this

@permission = Permission.update_or_create_by_user_id_and_role_id_and_creator_id(@user.id, 2, current_user.id) do |p|
  p.group_id = @group.id
end

Or you can use find_or_create_by... method:

@permission = Permission.find_or_create_by_user_id_and_role_id_and_creator_id(@user.id, 2, current_user.id)
@permission.group = @group
@permission.save
Community
  • 1
  • 1
fl00r
  • 82,987
  • 33
  • 217
  • 237
  • 1
    Thanks, tried this but got: NoMethodError (undefined method `update_or_create_by_user_id_and_role_id_and_creator_id' for #): – AnApprentice Apr 20 '11 at 16:33
  • 2
    Because you should extend it by yourself (look into related topic http://stackoverflow.com/questions/5578625/find-or-create-by-in-rails-3-and-updating-for-creating-records/5580108#5580108) – fl00r Apr 20 '11 at 16:34
  • While I got the find_or_create_by method working, I just noticed that every save is updating an existing group permission. Meaning a user can only belong to one group versus multiple. – AnApprentice Apr 22 '11 at 17:09
  • Eeehmm. It isn't connected actually. User can belon as many groups as it wants. Clarify what you mean – fl00r Apr 22 '11 at 18:25
  • answer with rails3 & rails4 versions: http://stackoverflow.com/a/29243322/1536309 – Blair Anderson Dec 31 '16 at 00:34
7

I'm updating questions with versioned answers. Because its important.


Rails 4 (docs)

There are a few ways to "find or create" an object, one is find_or_create_by(args)

Client.find_or_create_by(email: "bambam@flinstones.com", phone: "4255551212")

But the community preferred way is using where

client = Client.where(email: "bambam@flinstones.com", phone: "4255551212").first_or_create

and then you can do something like:

client = Client.where(client_params.slice(:email, :phone)).first_or_create
client.update(client_params)

Rails 3 (docs)

Suppose you want to find a client named ‘Andy’, and if there’s none, create one and additionally set his locked attribute to false. You can do so by running:

client = Client.where(:first_name => 'Andy').first_or_create(:locked => false)
# => #<Client id: 1, first_name: "Andy", orders_count: 0, locked: false, created_at: "2011-08-30 06:09:27", updated_at: "2011-08-30 06:09:27">
Blair Anderson
  • 19,463
  • 8
  • 77
  • 114
  • 1
    @ethicalhack3r because `find_or_create_by` it another abstraction away from the SQL that is happening when you call the method. – Blair Anderson Jul 02 '15 at 18:30
2

Or you wanna try this if you have many fields to fill in:

conditions = { :user_id => @user.id, 
               :role_id => 2,
               :creator_id => current_user.id }

    @permission = group.permissions.find(:first, :conditions => conditions) || group.permissions.create(conditions)

see this post: How can I pass multiple attributes to find_or_create_by in Rails 3?

Community
  • 1
  • 1
lkahtz
  • 4,706
  • 8
  • 46
  • 72
  • 2
    `group.permissions.where(:conditions => conditions).limit(1)` would probably work a little better as it won't be deprecated in Rails 3.1. – Patrick Robertson Apr 20 '11 at 16:32