2015-01-14 4 views
2

Мои ассоциации (упрощенный):Я думаю, что этот запрос не так?

проекта

has_many :users 
has_many :tasklists 

Tasklist

has_many :tasks 

Задача

belongs_to :tasklist 

Пользователь

has_many :assigned_tasks, class_name: 'Task', foreign_key: 'assignee_id' 

То, что я хочу сделать

Я хочу показать ресурс проекта, содержащих пользователь проекта, каждый из которых содержит возложенную на них задачу.

Я пробовал:

project = Project.includes(users: :assigned_tasks).find(params[:id]) 

Этот вид работ, за исключением, что она возвращает даже assigned_tasks, которые не принадлежат к исходному проекту. И это не то, что я хочу. Мне нужны только назначенные задания, являющиеся дочерними элементами этого проекта.

Возможно, весь подход неправильный. Может быть, мне стоит подумать с точки зрения задач, например, например:

project = Project.includes(tasklists: :tasks) 

Но тогда Как организовать задачи у пользователей?

Надеюсь, у меня есть смысл здесь. Любая помощь будет очень оценена. Thanks

ответ

2

Существует несколько способов решения этой проблемы в зависимости от ваших приоритетов. Давайте начнем с первым запросом:

project = Project.includes(users: :assigned_tasks).find(params[:id]) 

Это имеет то преимущество, что вы только выполняя один запрос к базе данных, которая тянет назад всю информацию - и тот недостаток, что вы получаете обратно все назначенное а не только для этого проекта. Но вы всегда можете отфильтровать задачи, когда вы показываете им:

project.users.each do |user| 
    user.assigned_tasks.select {|t| t.project_id == project.id}.each do |task| 
    puts task 
    end 
end 

В качестве альтернативы, вы можете просто получить проект и его пользователей на первый:

project = Project.includes(:users).find(params[:id]) 

И add a scope к вашему Task с a where clause on the join чтобы найти только задачи для этого проекта:

class Task < ActiveRecord::Base 
    belongs_to :tasklist 
    scope :for_project, -> (project) {joins(:tasklist).where(tasklist: {project: project})} 
end 

Затем вы можете использовать эту область при отображении задач:

project.users.each do |user| 
    user.assigned_tasks.for_project(project).each do |task| 
    puts task 
    end 
end 

Недостаток это вы, вероятно, работать в N+1 queries problem. Это также не большой прогресс в ясности.


Может быть, лучший подход, чтобы обеспечить прямой доступ к задачам статей проекта от project объекта:

class Project < ActiveRecord::Base 
    has_many :users 
    has_many :tasklists 
    has_many :tasks, through: :tasklists 
end 

class Task < ActiveRecord::Base 
    belongs_to :tasklist 
    has_one :project, through: :tasklist 
end 

, который позволяет получить доступ задач проекта с помощью project.tasks. Ассоциации являются Enumerable, так что вы можете использовать group_by так же, как вы бы с любым массивом:

project.tasks.group_by(&:user).each do |user, tasks] 
    puts user.name 
    tasks.each do |t| 
    puts t 
    end 
end 

Чтобы сохранить количество запросов к базе данных с низким, вы можете загрузить свои задачи с проектом:

project = Project.includes(:users, :tasks).find(params[:id]) 
+0

Привет, спасибо за этот очень полный ответ. Мне очень нравится последний подход. Я не знал, что вы можете использовать has_many через этот путь, и это здорово. Тем не менее, я закончил использовать второй подход, так как мое приложение rails - это REST API, и структура данных имеет большое значение (рельсы были недовольны тем, что я делал project.users = project.tasks.group_by (&: user) it бросил ошибку типа). Но в любом случае это решило мою проблему, и я узнал новые трюки, поэтому спасибо Alex P :) – MonsieurNinja

Смежные вопросы