将Rails的ActiveRecord中的稀疏数据按摩到HTML表的格式友好?

时间:2012-07-17 18:47:59

标签: ruby-on-rails html-table

我正在寻找一个常见场景的最佳实践:将从Rails(ActiveRecord,SQL)db表中提取的稀疏记录按摩到一个友好的结构中,以便在HTML中呈现表格。

出于性能原因,我做了一个返回这样的数据的查询(为了清楚起见,我简化了示例):

Lineitem.all
=> [#<Lineitem id: 1, username: "Bob", category: "A", amount: 10>, 
    #<Lineitem id: 2, username: "Bob", category: "C", amount: 20>, 
    #<Lineitem id: 3, username: "Jen", category: "A", amount: 30>, 
    #<Lineitem id: 4, username: "Ken", category: "B", amount: 40>,
    #<Lineitem id: 5, username: "Ken", category: "E", amount: 50>]

我的目标是这样的HTML表格:

             A     B     C     D     E
           ---   ---   ---   ---   ---
  Bob       10          20
  Jen       30   
  Ken             40                50
  Sam

如果每个类别都作为单独的列存储在db中(或者如果我使用NoSQL ......?!)或者我不关心db性能,那么这将是微不足道的。

为了解决这个问题,我一直在编写像这样的恶臭助手代码:

# create hash lookup, index first by username then by category, eg:
#   ["Bob"]["A"] = #<Lineitem id: 1, ...>
#   ["Bob"]["C"] = #<Lineitem id: 2, ...>
#   ["Jen"]["A"] = #<Lineitem id: 3, ...>  ...
def index_lineitems(lineitems)
  h = {}
  lineitems.each do |li|
    h[li.username] = {} unless h.key? li.username
    h[li.username][li.category] = li
  end
  h
end

# look up value from indexed hash
def get_lineitem_amount(indexed_lineitems, username, category)
  if indexed_lineitems.key?(username) && indexed_lineitems[username].key?(category)
    indexed_lineitems[username][category].amount
  else
    ""
  end
end

或其中的一些变体。然后我确定行和列的最终列表将是什么(注意“Sam”行...)并通过循环并每次调用get_lineitem_amount来呈现HTML表。这是一个糟糕的代码,我很尴尬地分享它。

当然,对于这个常见问题,有一种更清洁,更OO和Rails友好的方法。

有什么建议吗?

1 个答案:

答案 0 :(得分:1)

我正在做一些非常相似的事情:

让我们说这是在控制器中:

@data = LineItem.all

这是视图

columns = @data.map(&:category).uniq

%table
  %thead
    %tr
      %th &nbsp;
      - columns.each do |column|
        %th= column
  %tbody
    - @data.group_by(&:username).each do |username, rows|
      %tr
        %td= username
        - cursor = 0
        - rows.group_by(&:category).sort_by{|cat,rows| columns.index(cat)}.each do |category, rows|
          - until cursor == columns.index(category) do
            - cursor += 1
            %td &nbsp;
          %td= rows.sum(&:amount)

如果将列存储在单独的数据库表中并将它们包含到当前模型中,那么您可以将索引位置直接存储在对象上,而不是需要动态计算它们并且没有良好的控制权的订单。一个额外的查询并不会破坏应用程序的性能。