Rails - find_or_create_by - usages and alternatives
In this article, we cover the use cases of find_or_create_by in rails, and also briefly take a look at its alternative the first_or_create and the differences between the two.
Table of Contents
find_or_create_by method in rails
find_or_create_by is a common method used in Rails to find records based on one or many attributes that are input or create a record if it can't find it.
Let's see some examples of its use in action:
Using a single attribute:
//rails find_or_create_by example User.all => [#
] User.find_or_create_by_name("Jake") => # User.find_or_create_by_name("Joe") => # User.all => [# , # ]
Using multiple attributes
//rails find_or_create_by examples User.all => [#
] User.find_or_create_by_name_and_age("Jake", 30) => # User.find_or_create_by_name_and_age("Jake", 31) => # User.find_or_create_by_name_and_age("Joe", 40) => # User.all => [# , # , # ]
Like all methods, the find_or_create_by method has also had some upgrades. One significant addition is the addition of an exclamation mark at the end. ( find_or_create_by_name! ). The purpose of this is to raise an error when the method faces an error while creating a new record. The error does not get raised without the use of the exclamation mark.
This upgrade offers the find_or_create_by method a better usage in transactions. For example, if you want to create a new blog post and add tags to it using rails, you can do it using an after_save callback.
If there is a problem in the callback that causes the associated record not to be saved/created, then you might not want the first post to be saved either. Hence, by raising an error upon failed save, the entire transaction towards the database will be rolled back. This way, you can ensure that there are no half-saved records in the database.
Other Alternatives to find_or_create_by
first_or_create is an alternative to find_or_create_by, to insert or create new data. One major difference between find_or_create_by and first_or_create is that neither first_or create, first_create! are available in the public API anymore.
Documentation for Find or Build a New Object on Rails documentation does not enlist first_or_create and first_or_create! methods anymore. They were listed in documentation previously. Since, it was available in public documentation previously and people started using it in their repositories, it has created some confusion with newly available methods such as find_or_create_by and find_or_create_by! to perform similar functionality.
Let us take a look at an example using the first_or_create method below to understand how it works.
project = Project.first_or_create(name: 'Ruby on Rails', status: 'active')
What do you think the output will be when the code is run?
While we might think the code will find a record with attributes passed to the method, failing which it will create a new record. The first_or_create, in reality, will return any first record from the Table Project. It does not use the conditions passed to query and find the record to return if exists.
The find_or_create_by was introduced in rails to solve this problem.
So where can we use the first_or_create method?
We can use the first_or_create method without the where clause, to know if some record exists in the target table.
Having said that, we can also achieve the desired result of finding a record matching the attributes passed using first_or_create method.
Take a look at the code below:
project = Project.where(name: 'Ruby on Rails', status: 'active').first_or_create
Now, this will first search for a Project with the conditions given in the 'where' clause. If the record exists, the function will be returned. Else, a new record will be created with the given attributes.
If you know about any other interesting alternatives to find_or_create_by, do let us know in the comments below!