Create record if it does not exist in ActiveRecord
When writing a rails application you might want to create a record with parameters only if it doesn’t exist yet and if it does use that one.
A naive implementation may look similar to the following code.
if User.exists?(first_name: "John", last_name: "Smith")
User.find(first_name: "John", last_name: "Smith")
else
User.create!(first_name: "John", last_name: "Smith")
end
That is very tedious and it also adds a little bit performance overhead along the way. Fortunately, rails provides you with two nice methods for exactly this use case.
User.find_or_create_by(first_name: "John", last_name: "Smith")
User.where(first_name: "John", last_name: "Smith").first_or_create
Both of them accepts a block where you can more custom code and initialization. Keep in mind that the block gets executed only when the record is created.
User.where(last_name: "Smith").first_or_create do |user|
user.first_name = "John"
end
User.find_or_create_by(last_name: "Smith") do |user|
user.first_name = "John"
end
There are also bang equivalents find_or_create_by!
and first_or_create_by!
which raise an exception if the validation fails during the record creation.
The difference between the two of them is that find_or_create_by
is accepting a hash of attributes and uses them to perform the search and creation.
def find_or_create_by(attributes, &block)
find_by(attributes) || create(attributes, &block)
end
On the other hand, the first_or_create
uses attributes only for the creation and it returns the first record from provided scope.
def first_or_create(attributes = nil, &block) # :nodoc:
first || create(attributes, &block)
end
Therefore is more useful in combination with various scopes and where clauses where the parameters from the where clause are passed to the creation.