---
title: "Rails 6 adds create_or_find_by and create_or_find_by!"
description:
  "Rails 6 adds create_or_find_by and create_or_find_by! to find record if
  creation fails because of unique constraints"
canonical_url: "https://www.bigbinary.com/blog/rails-6-adds-create_or_find_by"
markdown_url: "https://www.bigbinary.com/blog/rails-6-adds-create_or_find_by.md"
---

# Rails 6 adds create_or_find_by and create_or_find_by!

Rails 6 adds create_or_find_by and create_or_find_by! to find record if creation
fails because of unique constraints

- Author: Amit Choudhary
- Published: March 25, 2019
- Categories: Rails 6, Rails

Rails 6 added [create_or_find_by](https://github.com/rails/rails/pull/31989) and
[create_or_find_by!](https://github.com/rails/rails/pull/31989). Both of these
methods rely on unique constraints on the database level. If creation fails, it
is because of the unique constraints on one or all of the given columns, and it
will try to find the record using `find_by!`.

[create_or_find_by](https://github.com/rails/rails/pull/31989) is an improvement
over
[find_or_create_by](https://api.rubyonrails.org/v5.2/classes/ActiveRecord/Relation.html#method-i-find_or_create_by)
because
[find_or_create_by](https://api.rubyonrails.org/v5.2/classes/ActiveRecord/Relation.html#method-i-find_or_create_by)
first queries for the record, and then inserts it if none is found. This could
lead to a race condition.

As mentioned by DHH in the pull request,
[create_or_find_by](https://github.com/rails/rails/pull/31989) has a few cons
too:

- The table must have unique constraints on the relevant columns.
- This method relies on exception handling, which is generally slower.

[create_or_find_by!](https://github.com/rails/rails/pull/31989) raises an
exception when creation fails because of the validations.

Let's see how both methods work in Rails 6.0.0.beta2.

#### Rails 6.0.0.beta2

```ruby

> > class CreateUsers < ActiveRecord::Migration[6.0]
> > def change
> > create_table :users do |t|
> > t.string :name, index: { unique: true }
> >
> >       t.timestamps
> >     end
> >
> > end
> > end

> > class User < ApplicationRecord
> > validates :name, presence: true
> > end

> > User.create_or_find_by(name: 'Amit')
> > BEGIN
> > INSERT INTO "users" ("name", "created_at", "updated_at") VALUES ($1, $2, \$3) RETURNING "id" [["name", "Amit"], ["created_at", "2019-03-07 09:33:23.391719"], ["updated_at", "2019-03-07 09:33:23.391719"]]
> > COMMIT

=> #<User id: 1, name: "Amit", created_at: "2019-03-07 09:33:23", updated_at: "2019-03-07 09:33:23">

> > User.create_or_find_by(name: 'Amit')
> > BEGIN
> > INSERT INTO "users" ("name", "created_at", "updated_at") VALUES ($1, $2, \$3) RETURNING "id" [["name", "Amit"], ["created_at", "2019-03-07 09:46:37.189068"], ["updated_at", "2019-03-07 09:46:37.189068"]]
> > ROLLBACK

=> #<User id: 1, name: "Amit", created_at: "2019-03-07 09:33:23", updated_at: "2019-03-07 09:33:23">

> > User.create_or_find_by(name: nil)
> > BEGIN
> > COMMIT

=> #<User id: nil, name: nil, created_at: nil, updated_at: nil>

> > User.create_or_find_by!(name: nil)

=> Traceback (most recent call last):
1: from (irb):2
ActiveRecord::RecordInvalid (Validation failed: Name can't be blank)
```

Here is the relevant [pull request](https://github.com/rails/rails/pull/31989).

Also note, [create_or_find_by](https://github.com/rails/rails/pull/31989) can
lead to primary keys running out, if the type of primary key is `int`. This
happens because each time
[create_or_find_by](https://github.com/rails/rails/pull/31989) hits
`ActiveRecord::RecordNotUnique`, it does not rollback auto-increment of the
primary key. The problem is discussed in this
[pull request](https://github.com/rails/rails/issues/35543).

## Links

- [Human page](https://www.bigbinary.com/blog/rails-6-adds-create_or_find_by)
