4

私はちょっとした時間追跡アプリケーションに取り組んでいて、解決方法がわからない問題に遭遇しました。私はTaskモデルとモデルを持っていClientます。各タスクはクライアントに属します。

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

これを行うためのクリーンでシンプルなレールの方法があると確信していますが、その方法はわかりません。これはどのように行うことができますか?

4

1 に答える 1

8

次のように、データベースにこれを実行させることができます。

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

クリーンであることに加えて、すべてのクライアントとタスクを 1 回のパスで取得するため、より効率的です。熱心な読み込みがなかったため、元のコードは N+1 クエリの対象でした。

ところで、スコープを単純にすることもできます。

scope :yesterday, -> { where(:start => (Date.yesterday.to_time...Date.today.to_time)) }
于 2013-03-06T17:48:40.540 に答える