Rails first_or_create ActiveRecord method
If you check the source, you will see that they are almost identical. The only difference is that the first one calls the "create" method and the other one "create!". This means that the second one will raise an exception, if the creation is not successful.
Gets the first record that matches what you have specified or creates one if there are no matches
I believe first_or_create
should generally be avoided these days. (Although it's still in Rails 6.1.1.) In 4.0 they added find_or_create_by
and friends, which are apparently meant to replace the first_or_create
methods. first_or_create
was once mentioned in the guides, now it's not. And it is no longer documented in the code (since Rails 4.0). It was introduced in Rails 3.2.19.
The reasons are:
It might seem that
first_or_create(attrs)
doesfirst(attrs) || create(attrs)
, where in fact it doesfirst || create(attrs)
. The proper usage is:Model.where(search_attrs).first_or_create(create_attrs)
That is, it might confuse people.
first(attrs) || create(attrs)
is whatfind_or_create_by
does.Also, using
first_or_create
introduces a scope, that might affect thecreate
callbacks in an unexpected way.
More on it in the changelog (search for first_or_create
).
From the Guides
first_or_create
The first_or_create
method checks whether first
returns nil or not. If it does return nil, then create
is called. This is very powerful when coupled with the where
method. Let’s see an example.
Suppose you want to find a client named ‘Andy’, and if there’s none, create one and additionally set his locked attribute to false. You can do so by running:
Client.where(:first_name => 'Andy').first_or_create(:locked => false)
# => #<Client id: 1, first_name: "Andy", orders_count: 0, locked: false, created_at: "2011-08-30 06:09:27", updated_at: "2011-08-30 06:09:27">
The SQL generated by this method looks like this:
SELECT * FROM clients WHERE (clients.first_name = 'Andy') LIMIT 1
BEGIN
INSERT INTO clients (created_at, first_name, locked, orders_count, updated_at) VALUES ('2011-08-30 05:22:57', 'Andy', 0, NULL, '2011-08-30 05:22:57')
COMMIT
first_or_create
returns either the record that already exists or the new record. In our case, we didn’t already have a client named Andy so the record is created and returned.
first_or_create!
You can also use first_or_create!
to raise an exception if the new record is invalid. Validations are not covered on this guide, but let’s assume for a moment that you temporarily add
validates :orders_count, :presence => true
to your Client model. If you try to create a new Client without passing an orders_count, the record will be invalid and an exception will be raised:
Client.where(:first_name => 'Andy').first_or_create!(:locked => false)
# => ActiveRecord::RecordInvalid: Validation failed: Orders count can't be blank