3

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 テーブルをレンダリングします。これは非常に悪いコードなので、共有するのが恥ずかしいです。

確かに、この一般的な問題に対する、よりクリーンでオブジェクト指向と Rails に適したアプローチがあります。

何かアドバイス?

4

1 に答える 1

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)

列を別のDBテーブルに格納し、それらを現在のモデルに含めると、非常にクリーンになります。そのため、インデックスの位置をその場で計算する必要がなく、順序を適切に制御する必要がなく、オブジェクト上にインデックスの位置を格納できます。 。1つの余分なクエリは、アプリのパフォーマンスを実際に損なうことはありません。

于 2012-07-17T19:51:33.847 に答える