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.


Would you like to get the most interesting content about programming every Monday?
Sign up to Programming Digest and stay up to date!