Ruby on Rails Finder Methods & Examples

Mav Tipi
4 min readAug 26, 2020

--

ActiveRecord is a Ruby gem for managing a relational database. It is a key part of the Rails framework.

In this article we will look at some of the methods ActiveRecord provides for us to retrieve an object or set of objects from a database. These are called finder methods.

We’re only looking at methods that select an entire row.

Our Database

Let’s make up a database to work with.

Here we’ve got the first few world chess champions; let’s say the table is called “Champions”. If we wanted to ask our database to give us, for example, each champion born in Russia, we would need a finder method to find those specific table entries.

The standard language for communicating with a database is SQL. SQL finds our Russian champions like this:

SELECT * FROM champions WHERE (champions.country_of_birth = 'Russia')

ActiveRecord makes the above SQL query if we ask it like this:

Champion.find_by(country_of_birth: 'Russia')

Easy. One fun fact is that the parentheses are optional;

Champion.find_by country_of_birth: 'Russia'

works just as well. We’ll use the parentheses in this article.

Of course Smyslov was actually born in the Russian SSR in the Soviet Union, but our database sidesteps any political questions about countryhood and successor states. Please leave your opinions about the Russian SSR as comments below. Now let’s look at all of the finder methods ActiveRecord includes.

Finder Methods

take

.take returns the first entry in the table, or it can take an argument to return the first n entries in the table.

Champion.take
Champion.take(3)

The first entry in the table is not always going to be the entry with primary key 1, so be careful of that.

find

.find takes a bare numerical argument and finds the entry with that primary key. So

Champion.find(2)

Would give us Lasker. Note here that the ids are numbered from 1, not 0.

We can also pass in multiple primary keys:

Champion.find(2, 4)
Champion.find([2,4])

These would both give us Lasker and Alekhine. However, if either entry didn’t exist, the method would throw an error instead.

find_by

.find_by allows you to search by condition. We saw one earlier, for Russia. We can ask about multiple conditions as so:

Champion.find_by(country_of_birth: 'Russia', year_became_champ: 1927)

find_or_initialize_by, find_or_create_by

These are like find_by, but if they don’t find a matching record, they create one.

Champion.find_or_initialize_by(name: 'Mikhail Tal')
Champion.find_or_create_by(name: 'Mikhail Tal')

Let’s take a look at our database now:

Hey, wait, where’s Morphy? The difference between ‘initialize’ (‘new’) and ‘create’ is that the latter saves it to the database and the former doesn’t. We can still work with the former, but it won’t be saved and it won’t have a primary key unless we call .save on it.

first, second, etc.

We can call the following methods to get that record from the database. Remember that these are referring the the order in the table, not the primary key. In our example it happens to be the same, because nothing much has happened to our table.

Champion.first
Champion.second
Champion.third
Champion.fourth
Champion.fifth
Champion.forty_two
Champion.third_to_last
Champion.second_to_last
Champion.last

No, it doesn’t work for the intervening numbers. It really is just “1, 2, 3, 4, 5, 42”.

limit

.limit specifies a maximum number of records to be retrieved. It can be used on its own:

Champion.limit(2)

Here we’d grab two champions. They’d be the first two on the table, just like .take.

.limit can also by combined fruitfully with others:

Champion.limit(2).find(4, 3, 2, 1)
Champion.limit(2).find_by(country_of_birth: 'Russia')

The first will give us Alekhine and Capablanca; the second, Alekhine and Smyslov.

offset

.offset specifies a number of records to skip. You could call it alone:

Champion.offset(5)

to give us Botvinnik, Smyslov and Tal. It’s also nice to combine:

Champion.find_by(country_of_birth: 'Russia').offset(2)

for Tal.

order

You can .order by a specified attribute to get the functionality you want from .take, .first, etc.

Champion.order(:name).first

Will give us Alexander Alekhine, who is alphabetically alpha.

reverse_order

.reverse_order lets us order by DSC. It works on its own:

Champion.all.reverse_order.first

in which case it orders by primary key (descending), returning Tal. It also works to reverse a previous ordering:

Champion.order(:name).reverse_order.first

Which will give us Wilhelm Steinitz.

readonly

.readonly prevents us from altering the returned object.

steinitz = Champion.readonly.first
steinitz.name = 'Paul Morphy'

This is fine, but if we now try:

steinitz.save

we’ll get an error. Sorry Morphy.

In the next installment, we’ll look at the methods that help us grab records from multiple tables at once.

Sign up to discover human stories that deepen your understanding of the world.

Free

Distraction-free reading. No ads.

Organize your knowledge with lists and highlights.

Tell your story. Find your audience.

Membership

Read member-only stories

Support writers you read most

Earn money for your writing

Listen to audio narrations

Read offline with the Medium app

--

--

Mav Tipi
Mav Tipi

Written by Mav Tipi

Weakly Pseudonymous Software Engineer

No responses yet

Write a response