2013-03-06 22 views
4

我正在研究一些时间跟踪应用程序,并遇到了一个我不知道如何解决的问题。我有一个Task模型和一个Client模型。每个任务都属于客户端。如何将Rails的ActiveRecord结果按列分组?

class Task < ActiveRecord::Base 
    belongs_to :client 
    attr_accessible :client_id, :description, :start, :end 

    scope :yesterday, -> { 
    where('start > ?', Date.yesterday.to_time).where('start < ?', Date.today.to_time) 
    } 
end 

class Client < ActiveRecord::Base 
    attr_accessible :name 
    has_many :tasks 
end 

现在我正在显示由天作用域的任务列表中的任务已经完成,并通过他们完成时间排序。我想显示相同的列表,但由客户端分组,并按客户端名称排序。这是我想要做什么:

<div id="yesterday_summary"> 
    <% @yesterday_clients.each do |client| %> 
    <h2><%= client.name %></h2> 
    <ul> 
     <% client.tasks.each do |task| %> 
     <li><%= task.description %></li> 
     <% end %> 
    </ul> 
    <% end %> 
</div> 

在我的控制器我目前有:

@tasks_yesterday = Task.yesterday 
@yesterday_clients = group_tasks_by_client @tasks_yesterday 

而在group_tasks_by_client方法,我有一些非常丑陋的代码,甚至没有做什么工作的:

def group_tasks_by_client(tasks) 
    clients = [] 
    tasks.collect(&:client).each do |client| 
     clients << {client.id => client} unless clients.has_key? client.id 
    end 
    clients_with_tasks = [] 
    clients.each do |client| 
     c = Struct.new(:name, :tasks) 
     cl = c.new(client.name, []) 
     tasks.each do |task| 
     cl.tasks << task if task.client_id = client.id 
     end 
     clients_with_tasks << cl 
    end 
    clients_with_tasks 
    end 

我敢肯定,有一个干净,简单的方式来做到这一点,但我不知道如何。如何才能做到这一点?

+1

是否轨GROUP_BY http://apidock.com/rails/Enumerable/group_by http://railscasts.com/episodes/29-逐个月的方法帮助? – Catfish 2013-03-06 18:34:11

+0

@Catfish这个'group_by'方法看起来非常有用。谢谢! – Andrew 2013-03-06 19:14:35

回答

8

你可以为你的数据库做像这样:

@yesterdays_clients = Client.includes(:tasks).merge(Task.yesterday).order(:name) 

除了作为清洁剂,它的效率更高,因为它得到所有客户端和任务在一通。原始代码受N + 1个查询限制,因为没有急切的加载。

顺便说一句,你可以让你的范围更简单,以及:

scope :yesterday, -> { where(:start => (Date.yesterday.to_time...Date.today.to_time)) } 
+0

谢谢!你能解释一下'Client.includes(:tasks).merge(Task.yesterday)'实际上在幕后做了些什么? – Andrew 2013-03-06 18:48:18

+0

['includes()'](http://guides.rubyonrails.org/active_record_querying.html#eager-loading-associations)用于加载关联。 ['merge()'](http://apidock.com/rails/ActiveRecord/SpawnMethods/merge)用于使用其他模型的属性修改查询。只要其他模型以前被包含或加入查询中,就应该正确解决。 – PinnyM 2013-03-06 18:59:48

相关问题