2013-03-06 54 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