Suponha que você precise obter o nome de usuário dos cinco primeiros posts. Você escreve rapidamente a consulta abaixo e vai curtir seu final de semana.
posts = Post.limit(5)
posts.each do |post|
puts post.user.name
end
Bom. Mas vamos ver as consultas
Post Load (0.5ms) SELECT `posts`.* FROM `posts` LIMIT 5
User Load (0.3ms) SELECT `users`.* FROM `users` WHERE `users`.`id` = 1 LIMIT 1
User Load (0.3ms) SELECT `users`.* FROM `users` WHERE `users`.`id` = 1 LIMIT 1
User Load (0.3ms) SELECT `users`.* FROM `users` WHERE `users`.`id` = 2 LIMIT 1
User Load (0.3ms) SELECT `users`.* FROM `users` WHERE `users`.`id` = 2 LIMIT 1
User Load (0.3ms) SELECT `users`.* FROM `users` WHERE `users`.`id` = 1 LIMIT 1
1 query
para buscar todos os posts
e 1 query
para buscar users
para cada postagem resulta em um total de 6 queries
. Confira a solução abaixo que faz a mesma coisa, apenas em 2 queries
:posts = Post.includes(:user).limit(5)
posts.each do |post|
puts post.user.name
end
#####
Post Load (0.3ms) SELECT `posts`.* FROM `posts` LIMIT 5
User Load (0.3ms) SELECT `users`.* FROM `users` WHERE `users`.`id` IN (1, 2)
Há uma pequena diferença. Adicione
includes(:posts)
para sua consulta e problema resolvido. Rápido, bonito e fácil. Mas não basta adicionar
includes
em sua consulta sem entendê-la corretamente. Usando includes
com joins
pode resultar em junções cruzadas, dependendo da situação, e você não precisa disso na maioria dos casos. Se você quiser adicionar condições aos seus modelos incluídos, precisará referenciá-los explicitamente . Por exemplo:
User.includes(:posts).where('posts.name = ?', 'example')
Irá lançar um erro, mas isso funcionará:
User.includes(:posts).where('posts.name = ?', 'example').references(:posts)
Observe que
includes
funciona com association names
enquanto references
precisa do the actual table name
.