method

find_or_create_by

Importance_3
find_or_create_by(attributes, &block) public

Finds the first record with the given attributes, or creates a record with the attributes if one is not found:

# Find the first user named "Penélope" or create a new one.
User.find_or_create_by(first_name: 'Penélope')
# => #<User id: 1, first_name: "Penélope", last_name: nil>

# Find the first user named "Penélope" or create a new one.
# We already have one so the existing record will be returned.
User.find_or_create_by(first_name: 'Penélope')
# => #<User id: 1, first_name: "Penélope", last_name: nil>

# Find the first user named "Scarlett" or create a new one with
# a particular last name.
User.create_with(last_name: 'Johansson').find_or_create_by(first_name: 'Scarlett')
# => #<User id: 2, first_name: "Scarlett", last_name: "Johansson">

This method accepts a block, which is passed down to #create. The last example above can be alternatively written this way:

# Find the first user named "Scarlett" or create a new one with a
# particular last name.
User.find_or_create_by(first_name: 'Scarlett') do |user|
  user.last_name = 'Johansson'
end
# => #<User id: 2, first_name: "Scarlett", last_name: "Johansson">

This method always returns a record, but if creation was attempted and failed due to validation errors it won’t be persisted, you get what #create returns in such situation.

If creation failed because of a unique constraint, this method will assume it encountered a race condition and will try finding the record once more. If somehow the second find still does not find a record because a concurrent DELETE happened, it will then raise an ActiveRecord::RecordNotFound exception.

Please note this method is not atomic, it runs first a SELECT, and if there are no results an INSERT is attempted. So if the table doesn’t have a relevant unique constraint it could be the case that you end up with two or more similar records.

Show source
Register or log in to add new notes.
June 19, 2015
1 thank

Use :where or any defined scope before :find_or_create_by

You can chain find_or_create_by with :where, or any custom scope.

E.g.:

User.where(girls: true).find_or_create_by(first_name: ‘Scarlett’)


scope :celebrities, -> { where(celebrity: true) }

User.celebrities.create_with(last_name: ‘Johansson’).find_or_create_by(first_name: ‘Scarlett’)

May 18, 2015
0 thanks

Are those supported versions correct?

It seems like this method was supported in versions prior to 4.0.2.

UPDATE never mind, wish I could delete this comment..