【问题标题】:Complex associations for RailsRails 的复杂关联
【发布时间】:2011-12-07 09:19:29
【问题描述】:

坚持这个,如果你能看一下.. :)

我想要的是获得所有未完成的项目,包括某个用户的所有未完成的任务。

这是我目前的设置:

User (devise)
    has_one :employee

Employee
    belongs_to :user
    has_and_belongs_to_many :tasks
    has_and_belongs_to_many :unfinished_tasks, :conditions => { :tasks => { :completed_at => nil } }, :class_name => "Task"
    has_many :unfinished_projects, :through => :unfinished_tasks, :source => :project, :uniq => true   ( :include => :unfinished_tasks OR :include => :tasks ? )

Project
    has_many :tasks

Task
    belongs_to :project
    has_and_belongs_to_many :employees

在我看来 (haml) 我想要这样的东西:

- for project in current_user.employee.unfinished_projects

    = project.name

    # THESE ARE NOT THE ONLY THE TASKS FOR THE CURRENT_USER
    - for task in project.tasks    ( OR project.unfinished_tasks ? )

        = task.name

此设置适用于项目,只有未完成任务的项目。

但我不确定如何将未完成的任务包含在这些项目中。

任何人都知道执行此操作的最佳方法,如果可能的话,我希望对所有这些都进行一次查询。

编辑: 棘手的部分是任务必须针对 current_user。 项目已完美加载。

但是当它加载任务时:

- for task in project.tasks.unfinished

这样做:

Task Load (1.3ms)  SELECT `tasks`.* FROM `tasks` WHERE `tasks`.`project_id` IN (12, 7, 13, 15, 14, 10, 16, 17, 9, 2, 3)
Task Load (0.4ms)  SELECT `tasks`.* FROM `tasks` WHERE `tasks`.`project_id` = 12 AND `tasks`.`completed_at` IS NULL
Task Load (0.3ms)  SELECT `tasks`.* FROM `tasks` WHERE `tasks`.`project_id` = 7 AND `tasks`.`completed_at` IS NULL
Task Load (0.6ms)  SELECT `tasks`.* FROM `tasks` WHERE `tasks`.`project_id` = 13 AND `tasks`.`completed_at` IS NULL
etc.

它应该做的是得到员工的任务:

Employee
  Projects
    Tasks

这应该是项目查询中内部连接的任务。

【问题讨论】:

  • 您的要求并不完全清楚。您的意思是您希望“unfinished_projects”成为未完成任务的项目列表吗?您的意思是要使用“incomplete_tasks”来表示属于该项目的尚未完成的任务(并且看不到已完成的任务)?
  • 一个未完成的项目是一个有许多未完成任务的项目。我想要某个员工未完成任务的所有未完成项目。理想的情况是包含所有这些记录的单个查询。
  • 最大的问题是,在我拿到项目并遍历项目的任务后,它们不再与该特定员工相关。

标签: sql ruby-on-rails ruby ruby-on-rails-3 associations


【解决方案1】:

所以我的问题的答案如下:

模型:

Employee:
  has_and_belongs_to_many :tasks
  has_many :projects, :through => :tasks, :uniq => true

Project:
  has_many :tasks
  scope :unfinished, :include => :tasks, :conditions => { :tasks => { :completed_at => nil } }

Task:
  scope :unfinished, :conditions => { :completed_at => nil }
  scope :of_user, lambda { |user| { :include => :employees, :conditions => { :employees => { :id => user.employee.id } } } }

观点:

- for project in current_user.employee.projects.unfinished

    - for task in project.tasks.unfinished.of_user( current_user )

我觉得 Project.unfinished 已经包含未完成的任务非常烦人。

为什么我需要再次获取这些,为每个项目提供一个新的查询!应该没必要吧?

【讨论】:

  • 要删除多余的 sql,请查看“eager loading”。
  • 我认为这就是我的问题..我希望它急切加载,但是我尝试的所有操作都会弄乱任务..如果我包含急切加载的任务,它们就不再是用户特定的了..
  • hmmm... 您可以要求 user_id 与 named_scope 中的指定任务相匹配。您甚至可以将其作为参数传递...我将在我的答案中添加一个示例。
  • Taryn,感谢您帮助我解决这个问题,但我还不能真正解决这个难题。我会再试一次,我会告诉你的效果如何.. 再次感谢!
【解决方案2】:

假设我的问题(上图)是您希望看到的真实示例。

您可以使用 named_scopes 来解决这个问题。

例如:

Task
   named_scope :unfinished, :conditions => {:unfinished => true}

或任何用于表明它未完成的条件。然后你可以使用:

 - for task in project.tasks.unfinished

“unfinished_projects”的named_scope 类似,但稍微复杂一些。 它需要一些裸 SQL - 这将取决于您使用的数据库,但通常要求您加入任务并找到有一项未完成任务的项目。我猜这可能接近你需要的:

Project
   named_scope :unfinished, 
    :joins => 'inner join tasks on tasks.project_id = projects.id',
    :conditions => ['tasks.unfinished IS TRUE']

然后你会像上面那样使用它:

  - for project in current_user.employee.projects.unfinished

对于仅查找具有未完成任务的项目的项目为用户,您可以将用户作为参数添加到范围,例如:

Project
   named_scope :unfinished_for_user, lambda {|the_user_id|
    :joins => 'inner join tasks on tasks.project_id = projects.id',
    :conditions => ['tasks.unfinished IS TRUE AND tasks.user_id = ?', the_user_id]
   }

【讨论】:

  • 现在尝试使用范围,希望它能让一切更清楚。唯一的问题是 Project.unfinished 范围。真的不知道怎么创作。范围 :unfinished, :condition => { :tasks => "completed_at IS NOT NULL" }
  • 我已经更新了我的评论,包括尝试使这项工作发挥作用……您可能需要为此使用 sql。您可以通过使用“Project.all”尝试条件来测试它。
  • 好吧,我正在使用 Rails3,并创建了以下范围:scope :unfinished, :include => :tasks, :conditions => "'tasks.completed_at IS NOT NULL'"。现在的问题是我没有独特的项目。
  • 我真的很迷,条件应该是{ :tasks => { :completed_at => nil } }offcourse
  • 我已经更新了我的问题,范围使我的模型更加清晰,所以这是一种解脱。但它仍然无法正常工作。
【解决方案3】:

所以,在重新做了一遍之后,我并没有真正找到我的问题所在。 现在发生了一些变化:我正在使用范围,用于急切加载的包含在范围内,而 project.tasks 本身没有范围。

项目模型:

has_many :tasks
scope :assigned_to, lambda { |employee| { :include => { :tasks => :employees }, :conditions => { :tasks => { :employees => { :id => employee.id } } } } }
scope :unfinished, :include => :tasks, :conditions => { :tasks => { :completed_at => nil } }

观点:

- for project in Project.assigned_to( current_user.employee ).unfinished
  = project.name
  - for task in project.tasks
    = task.name

给我这个漂亮的 MySQL 查询:

SELECT `projects`.`id` AS t0_r0, `projects`.`name` AS t0_r1, `projects`.`description` AS t0_r2, `projects`.`completed_at` AS t0_r3, `projects`.`created_at` AS t0_r4, `projects`.`updated_at` AS t0_r5, `tasks`.`id` AS t1_r0, `tasks`.`name` AS t1_r1, `tasks`.`description` AS t1_r2, `tasks`.`estimated_duration` AS t1_r3, `tasks`.`calculated_duration` AS t1_r4, `tasks`.`status` AS t1_r5, `tasks`.`completed_at` AS t1_r6, `tasks`.`project_id` AS t1_r7, `tasks`.`created_at` AS t1_r8, `tasks`.`updated_at` AS t1_r9, `employees`.`id` AS t2_r0, `employees`.`alias` AS t2_r1, `employees`.`comments` AS t2_r2, `employees`.`user_id` AS t2_r3, `employees`.`created_at` AS t2_r4, `employees`.`updated_at` AS t2_r5 FROM `projects` LEFT OUTER JOIN `tasks` ON `tasks`.`project_id` = `projects`.`id` LEFT OUTER JOIN `employees_tasks` ON `employees_tasks`.`task_id` = `tasks`.`id` LEFT OUTER JOIN `employees` ON `employees`.`id` = `employees_tasks`.`employee_id` WHERE `employees`.`id` = 3 AND `tasks`.`completed_at` IS NULL

像魅力一样工作!

特别感谢 Taryn.. ;)

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-01-31
    • 2021-08-17
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多