如何重构3种视图方法使用的大量查询?

时间:2011-12-23 13:41:34

标签: ruby-on-rails

Rails 2.3.5

我在3个不同的视图上创建了一个报告(主要,预览和自动电子邮件的无布局纯HTML版本)。该报告使用了大约28个查询,对于其中一些查询,我在查询之前做了一些逻辑:

  sunday_this_week = (Time.now.beginning_of_week - 1.days).strftime("%Y-%m-%d")
  sunday_1_week_ago = (Time.now.beginning_of_week - 8.days).strftime("%Y-%m-%d")
  sunday_2_week_ago = (Time.now.beginning_of_week - 15.days).strftime("%Y-%m-%d")
  sunday_3_week_ago = (Time.now.beginning_of_week - 22.days).strftime("%Y-%m-%d")
  sunday_4_week_ago = (Time.now.beginning_of_week - 29.days).strftime("%Y-%m-%d")
  @sunday_dates = [sunday_this_week,sunday_1_week_ago,sunday_2_week_ago,sunday_3_week_ago,sunday_4_week_ago ]

  sql = %Q{
           SELECT * FROM report_notes
           WHERE week_of IN(?)
           ORDER BY week_of ASC, market ASC, measure ASC
          }
  @performance_metric_notes = RptNote.find_by_sql([ sql, @sunday_dates ])

有些查询非常大,而且由于3个视图需要它们,因此我在三个不同的控制器方法下列出了相同的查询。控制器中有很多行(约1400行)。

你会做什么来重构一些东西(是否可能有一个带有所有查询设置逻辑和查询的单一控制器方法,3种不同的视图方法可以调用而不是每个具有副本的视图方法)?

谢谢!

4 个答案:

答案 0 :(得分:2)

我会在单独的Report课程中提取所有这些代码 将它移动到另一个控制器动作仍然会导致一个巨大的控制器。另外,如果生成报告有很多逻辑,那么控制器操作就不适合它。如果您可以保持控制器清洁,只需为视图准备变量,那就好多了。

只需将您的课程放在/ lib中,并在您的控制器操作中,您可以调用类似

的内容
report = Report.new params
@performance_metric_notes = report.performance_metric_notes
@other_performances = report.other_performances
...

你明白了。

答案 1 :(得分:1)

为什么不把它作为方法放在报告模型中?

@performance_metric_notes = RptNote.get_my_custom_report

答案 2 :(得分:1)

正如@Christoph所说,如果涉及到很多逻辑,那么将其作为模型中的一种方法来实现;此外,您可以将其实现为scope

class RptNote < ActiveRecord::Base

  def self.performance_metric_notes( past_weeks = 4 )
    # this will calculate the last sundays for this week + 
    # the nth past weeks, defaults to 4 :
    last_sundays = (0..past_weeks).map do |n|
      (Time.now.beginning_of_week - (1+n*7).days).strftime("%Y-%m-%d")
    end
    # this will return a relation. Using an array in a where clause 
    # renders a SQL IN selector.
    self
      .where( week_of: last_sundays )
      .order( :week_of, :market, :measure )
  end

end

...这甚至可以通过添加其他子句来优化结果:

RptNote.performance_metric_notes.where( measure: something )

有关rails 3中关系和查询的详细信息,请参阅Railscasts #215

编辑: wooops,我的不好,只是看到你的问题在轨道上2.逻辑保持不变,除了查询。

还有更多:如果您的控制器操作经常使用它,您可以在控制器上设置before_filter以自动获取记录:

before_filter :fetch_notes

def fetch_notes
  @notes = RptNote.performance_metric_notes
end

答案 3 :(得分:0)

在你的模特中:

scope :by_dates, lambda { |dates|
  where("week_of IN (#{sunday_dates.join(,)})").
  order_by("week_of ASC, market ASC, measure ASC")
}

def self.sunday_dates
  sunday_this_week = (Time.now.beginning_of_week - 1.days).strftime("%Y-%m-%d")
  sunday_1_week_ago = (Time.now.beginning_of_week - 8.days).strftime("%Y-%m-%d")
  sunday_2_week_ago = (Time.now.beginning_of_week - 15.days).strftime("%Y-%m-%d")
  sunday_3_week_ago = (Time.now.beginning_of_week - 22.days).strftime("%Y-%m-%d")
  sunday_4_week_ago = (Time.now.beginning_of_week - 29.days).strftime("%Y-%m-%d")
  [sunday_this_week,sunday_1_week_ago,sunday_2_week_ago,sunday_3_week_ago,sunday_4_week_ago]
end

在你的控制器中:

RptNote.by_dates(RptNote.sunday_dates)