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'
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.
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]"}
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.
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
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