[Rails] Don't use the select method just to narrow down the columns!

About ActiveRecord select method

When you get data with ActiveRecord, you basically get all the items in the corresponding table. As you can see from the SQL to be issued, all items are acquired with *. Since all items have been acquired, any item can be referenced in the subsequent processing.

pry(main)> user = User.first
  User Load (0.7ms)  SELECT `users`.* FROM `users` ORDER BY `users`.`id` ASC LIMIT 1
=> #<User id: 1, name: "ham", created_at: "2020-03-10 01:03:37", updated_at: "2020-06-16 02:18:39">
pry(main)> user.id
=> 1
pry(main)> user.name
=> "ham"

However, not all columns are used, so why not get only the necessary columns? I think there is also the idea. In such a case, you can narrow down the columns to be acquired by using the method called select. For more information on select, see Rails Guides (https://railsguides.jp/active_record_querying.html#%E7%89%B9%E5%AE%9A%E3%81%AE%E3%83%95%E3%82% A3% E3% 83% BC% E3% 83% AB% E3% 83% 89% E3% 81% A0% E3% 81% 91% E3% 82% 92% E5% 8F% 96% E3% 82% 8A% Please see E5% 87% BA% E3% 81% 99).

You can get only the required columns by specifying select. Of course, the columns that have not been acquired cannot be referenced in the subsequent processing.

pry(main)> user = User.select(:id, :created_at).first
  User Load (0.7ms)  SELECT `users`.`id`, `users`.`created_at` FROM `users` ORDER BY `users`.`id` ASC LIMIT 1
=> #<User id: 1, created_at: "2020-03-10 01:03:37">
pry(main)> user.id
=> 1
pry(main)> user.name
ActiveModel::MissingAttributeError: missing attribute: name
from /usr/local/bundle/gems/activemodel-6.0.3.2/lib/active_model/attribute.rb:221:in `value'

Don't use select that just narrows down the columns!

It's just my personal opinion, but if you are developing with multiple people such as team development, I think that it is better not to use select that only narrows down the columns.

Why?

See the code below.

def hoge(user_id)
  #id with select,Get only name
  user = User.select(:id, :name).find(user_id)

  ...(Various processing)

  generate_response(user)
end

private

def generate_response(user)
  { id: user.id, name: user.name }
end

What if you later decided to add email to the response of the hoge method? Let's assume that the User model has an email column.

You probably just need to find it and add email to generate_response! I think that I will fix it as follows.

def generate_response(user)
-  { id: current_user.id, name: current_user.name }
+  { id: current_user.id, name: current_user.name, email: current_user.email }
end

Alright correction is over! It was done in one line! !! Test execution! !! !!

pry(main)> { id: user.id, name: user.name, email: user.email }
ActiveModel::MissingAttributeError: missing attribute: email
from /usr/local/bundle/gems/activemodel-6.0.3.2/lib/active_model/attribute.rb:221:in `value'

that? ?? It doesn't work ... Is the receiving user strange? Follow me ...

That's right. Since the acquisition column is narrowed down by select, it is necessary to add email to that. If you modify the following, it will work.

def hoge(user_id)
  #id with select,Get only name
-  user = User.select(:id, :name).find(user_id)
+  user = User.select(:id, :name, :email).find(user_id)

The test went well!

pry(main)> { id: user.id, name: user.name, email: user.email }
=> {:id=>1, :name=>"hoge", :email=>"[email protected]"}

What do you think?

I think that if you use Rails ActiveRecord, you will get all the basic columns, so I think that many people are addicted to it once as mentioned above.

It may not be that much effort each time, but the same thing happens every time if the system is continuously developed. This is a good cost. In the worst case, it can even go unnoticed and create bugs.

Is it necessary to implement this select to increase the development cost and the risk of bugs? I prefer code that is hard for others to misunderstand, even if it's a little less optimal. That's why I prefer not to use select, which I only use to narrow down columns.

How to use select

It has become an article that completely denies the existence of select, but of course it has some uses. When using an aggregate function as shown below.

users_group_by_name = User.select('name, count(*) AS cnt').group(:name)
users_group_by_name.each do |u|
  p u.name
  # u.You can get the count with cnt
  p u.cnt
end

However, in this case as well, it is highly likely that you will misunderstand if the variable name is changed to ʻusers`, so it is better to use a variable name that can be understood.

Also, sometimes I make it possible to directly access the table to which I joined using select, but this is also very difficult to understand, so I think it is better to stop it.

review = Review.select('reviews.id, users.name').joins(:user).find_by(id: 1)
#Now user.can access name
review.name

Access via association normally or implement delegate.

app/models/review.rb


review = Review.find_by(id: 1)
#Access via association
review.user.name
#Or define delegate in Review model(delegate :name, to: :user, prefix: true)
review.user_name

Summary

This article focused on select, but I think it's important to write code that is easy for others to understand (hard to misunderstand) in team development where multiple people touch the same code. Writing code that is easy to read (hard to misunderstand) will speed up development and reduce bugs.

Recommended Posts

[Rails] Don't use the select method just to narrow down the columns!
[Rails] How to use the map method
Use the where method to narrow down by referring to the value of another model.
[Rails] I don't know how to use the model ...
How to use the link_to method
How to use the include? method
How to use the form_with method
[Ruby on Rails] Use the resources method to automatically create routes.
[Java] How to use the toString () method
[Rails] How to use helper method, confimartion
[Rails] How to use select boxes in Ransack
When you want to use the method outside
Output of how to use the slice method
[Ruby on Rails] How to use session method
[Ruby basics] How to use the slice method
[rails] How to use devise helper method before_action: authenticate_user!
Use collection_select to pull down the data stored in Active_Hash
I want to use the sanitize method other than View.
I want to narrow down the display of docker ps
What to do if you can't use the rails command
[Rails] How to use enum
about the where method (rails)
[Rails] How to use enum
How to use rails join
[Rails] How to use validation
[Rails] How to use authenticate_user!
[Rails] How to use "kaminari"
Easy to use array.map (&: method)
[Rails] How to use Scope
Use Modifier # isStatic to determine if the [Reflection] method is static.
[Rails] How to omit the display of the character string of the link_to method
The first thing to do before you can use Rails6 Ajax
I tried to understand how the rails method "link_to" is defined
How to narrow down the image format from the gallery on Android and then select and import multiple images
[Rails] Implementation procedure of the function to tag posts without gem + the function to narrow down and display posts by tags
[Rails] How to use gem "devise"
[Rails] How to use devise (Note)
[Rails] How to use flash messages
[Java] How to use join method
How to use the wrapper class
How to use Ruby on Rails
[Ruby] How to use any? Method
I tried to explain the method
[Rails] How to use Active Storage
[Rails 6] destroy using the resources method
[Introduction to Rails] How to use render
How to use Ruby inject method
Use the permutation method to make all the default users follow each other
[Rails] Use devise to get information about the currently logged in user
To you who were told "Don't use Stream API" in the field
[Rails] How to solve the time lag of created_at after save method