我在Events
模型中列出了一系列事件。我需要做的是按城市/州的视觉分组活动。
从视觉上看,这将是这样的:
在这里,我想看看我正在做些什么来实现这一目标:
<% @number.events.locations.each do |location| %>
<li>
<h4><%= "#{location.city}, #{location.state}" %></h4>
<ol>
<% @number.events.city_state(location).each do |event| %>
<li><%= event.status %></li>
<% end %>
</ol>
</li>
<% end %>
以及相应的范围:
scope :locations, :group => 'city, state'
scope :city_state, lambda {|location| {:conditions => ["city = ? AND state = ?", location.city, location.state] }}
问题是,我正在为每个位置进行额外的数据库调用。
我可以将其归结为一个查询吗?
答案 0 :(得分:1)
使用预先加载来预取数据库中的关联:
如果您同时提取所有活动,可以将其更改为预先加载活动的位置关联:
Event.all # only loads Event objects from the database
#maybe try...
Event.includes(:locations) # loads Event objects and their Locations
如果您需要某些其他对象的事件(例如您的@number
对象),您可能希望在控制器中执行类似的操作,例如:
@number = Number.where(:id => whatever).includes(:events => :locations)
现在,在此示例中,您的@number
对象将从数据库中提取,同时包含所有事件和所有这些事件的位置。
有关详细信息,请参阅Rails指南:http://guides.rubyonrails.org/active_record_querying.html#eager-loading-associations
更新了我在评论中的回答:
根本不使用范围,您可以简单地使用Ruby为您完成工作,并将ActiveRecord和额外的数据库调用从循环中删除。
Enumerable#group_by
将根据您提供的某个属性获取集合和分组。在这种情况下,您可以将所有事件,按城市和州分组,然后迭代它返回的哈希:
events = Event.all
events.group_by { |e| "#{e.city}, #{e.state}" }
# => {"Birmingham, AL" => [<event>, <event>, <event>], "Nashville, TN" => [<event>, <event>]}
这样做可能不如使用范围那么漂亮,但是它允许您进行一次数据库调用并将其留在那里。
答案 1 :(得分:0)
这是一个O(n ^ 2)解决方案,假设您的locations.each do |location|
循环按宣传方式工作。应该将它降低到2分贝命中。
<% @number.events.locations.each do |location| %>
<li>
<h4><%= "#{location.city}, #{location.state}" -%></h4>
<ol>
<% @number.events.each do |event| %>
<% if event.city == location.city and event.state == location.state %>
<li><%= event.status -%></li>
<% end %>
<% end %>
</ol>
</li>
<% end %>
尚未测试,稍后会再次查看。