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.

Add a comment


Would you like to get top 5 links on Programming every Monday?
Sign up to Programming Digest and stay up to date!