find_or_create_by
- 1.0.0
- 1.1.6
- 1.2.6
- 2.0.3
- 2.1.0
- 2.2.1
- 2.3.8
- 3.0.0
- 3.0.9
- 3.1.0
- 3.2.1
- 3.2.8
- 3.2.13
- 4.0.2 (0)
- 4.1.8 (0)
- 4.2.1 (0)
- 4.2.7 (0)
- 4.2.9 (0)
- 5.0.0.1 (8)
- 5.1.7 (0)
- 5.2.3 (0)
- 6.0.0 (-38)
- 6.1.3.1 (0)
- 6.1.7.7 (0)
- 7.0.0 (0)
- 7.1.3.2 (29)
- 7.1.3.4 (0)
- What's this?
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 # different 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.
Please note *this method is not atomic*, it runs first a SELECT, and if there are no results an INSERT is attempted. If there are other threads or processes there is a race condition between both calls and it could be the case that you end up with two similar records.
Whether that is a problem or not depends on the logic of the application, but in the particular case in which rows have a UNIQUE constraint an exception may be raised, just retry:
begin CreditAccount.transaction(requires_new: true) do CreditAccount.find_or_create_by(user_id: user.id) end rescue ActiveRecord::RecordNotUnique retry end
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..