find_or_create_by
find_or_create_by(attributes, &block)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.
2Notes
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')
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..