Hi guys, the actual question is the following is not possible to bring the related rows from another table, there are two models Project and Todo two tables, respectively projects and todos, describe the models:

class Project < ApplicationRecord has_many :todos end 

and second

 class Todo < ApplicationRecord belongs_to :project end 

the task to display a list of projects and tasks assigned to them, in the controller I write the following

 class ProjectsController < ApplicationController def index @projects = Project.all @todos = Project.joins(:todos) end end 

then I write in the view

 <% @projects.each do |pr| %> <h2><%= pr.title %></h2> <% @todos.each do |to| %> <h4><%= to.todo %></h4> <% end %> <% end %> 

and get

such a result

Ok, he suggests that I specify the todos method to to where, in theory, I keep associations for projects, but why todos? if this is the name of the table and not the todo attribute, ie the name of the column where I have the tasks in the todos table, because it works for the project.title? and yes if I do todos as he asks then I get this number of associations with this project please tell me what I do not understand? what am i doing wrong?

    2 answers 2

    You at least in the rail console ( rails c ) check what you have done.

     @todos = Project.joins(:todos) sample_todo = @todos.first sample_todo.is_a?(Todo) # => false # вот это поворот! sample_todo.class # => Project # вот это поворо-о-от! 

    Project.joins(:todos) is a list of projects taken from the join of the tables of the Todo and Project models. In your case, this means that each Project will meet in it as many times as he has Todo NIS. That is, yes, projects without Todo will not get there at all.

    A casket just opened. Well, you declared has_many :todos for Project , so every object of the Project class will now have a todos method that returns only the Todo collection. And since you have Project objects in @todos , they all have a todos method, which you are offered.

    You don’t need @todos at all. The tasks corresponding to projects can be drawn directly from the objects of the projects themselves:

     Project.all.each do |project| project.todos.each do |todo| # что вы там хотели сделать с todo end end 

    But there is a slight nuisance, unless additional measures are taken, Rails will make one additional request to todos for each project, which is rather slow. This can be avoided if you “greedily download” (see the tutorials) them in advance for all projects using includes :

     Project.includes(:todos).all.each do |project| project.todos.each do |todo| # что вы там хотели сделать с todo end end 

    And watch what requests are executed in the console in each of two cases.

    • Thank you so much for such a detailed and detailed answer. This information is very useful to me. - newbie_rails
    • @newbie_rails you can change the selected answer if you want. - Mikhail Vaysman

    You do not need to do joins, you need includes

     class ProjectsController < ApplicationController def index @projects = Project.includes(:todos).all end end 

    And then

     <% @projects.each do |pr| %> <h2><%= pr.title %></h2> <% pr.todos.each do |to| %> <h4><%= to.todo %></h4> <% end %> <% end %> 
    • And here is another small question. When we write Project.includes(:todos).all to the instance variable, all Project objects are entered there and its associations are tightened to each object, which become a method of the Project, after which we can refer to any columns of the Projects table as well as to Todos but via the Projects method. In addition, empty Project objects are also displayed. In the controller, the .all method .all not change absolutely anything. With it, that without it, the result is the same, it turns out that in one variable there is just a dump of everything that is possible and this is all due to has_many ? - newbie_rails
    • @newbie_rails ActiveRecord is a god object. Therefore, he does everything and has so many methods. In your case, all really is not needed. - Mikhail Vaysman