我想计算每种货币的总金额。
我的应用图片如下:
event1 USD 1.23
event2 EUR 2.34
event3 JPY 100
event4 USD 4.56
Total
USD 5.79
EUR 2.34
JPY 100
虽然无论货币如何,我都可以使用sum(:amount)
计算金额,但我想知道如何计算和显示每种货币。
时间表\ show.html.erb
<%= render @schedules %>
安排\ _schedules.html.erb
<% schedule.rooms.each_with_index do |a, idx| %>
<% a.events.each do |e| %>
...
<%= l(e.start_at) %> - <%= l(e.end_at) %>
...
<%= e.title %>
<%= e.ccy %> <%= e.amount %>
<% end %>
<%= a.events.sum(:amount) %> # 108.13 regardless of currency (1.23 + 2.34 + 100 + 4.56)
我的模特
class Schedule
has_many :rooms, inverse_of: :schedule, dependent: :destroy
end
class Room
belongs_to :schedule, inverse_of: :rooms
has_many :events, inverse_of: :room, dependent: :destroy
end
class Event
belongs_to :room, inverse_of: :events
has_one :schedule, autosave: false, through: :room
end
schedules_controller.rb
def show
@schedules = Schedule.find(params[:id])
end
schema.rb
create_table "rooms", force: :cascade do |t|
t.integer "schedule_id"
t.integer "day", default: 1
end
create_table "events", force: :cascade do |t|
t.time "start_at"
t.time "end_at"
...
t.string "title"
t.integer "room_id"
t.string "ccy"
t.decimal "amount"
end
如果你能给我任何建议,我将不胜感激。
更新
当我尝试添加<%= room.events.group(:currency).count(:amount) %>
@TheCha͢mp回答时,出现以下错误。 (变量的名称是原始的。我稍后会重命名。)
ActiveRecord::StatementInvalid in SchedulesController#show
PG::GroupingError: ERROR: column "events.start_at" must appear in the GROUP BY clause or be used in an aggregate function LINE 1: ...dation_id" = $1 GROUP BY "events"."ccy" ORDER BY "events"."... ^ : SELECT COUNT("events"."amount") AS count_amount, ccy AS ccy FROM "events" WHERE "events"."room_id" = $1 GROUP BY "events"."ccy" ORDER BY "events"."start_at" ASC
我更新了_schedules.html.erb
和schema.rb
解决
使用unscoped
。
我在这篇文章https://makandracards.com/bitcrowd/32883-pg-groupingerror-error-column-must-appear-in-the-group-by-clause-or-be-used-in-an-aggregate-function-in-rails-while-trying-group中找到了它。
我添加了以下代码。
<% a.events.unscoped.where(room_id: a.id).group(:ccy).sum(:amount).each do |ccy, amount| %>
答案 0 :(得分:1)
首先,您的命名很糟糕:为什么要为房间a
和事件e
命名。到底是什么ccy
?我不得不谷歌那个......这让其他开发者难以阅读。我在我的解决方案中重命名了变量。
关于您的问题,您可以在数据库级别轻松执行此操作:
Event.group(:currency).count(:amount)
# => {usd: 145, eur: 2289, ...}
因此,在您看来,您可以使用
<% schedule.rooms.each_with_index do |room, index| %>
<% room.events.each do |e| %>
<%= event.title %>
<%= event.ccy %> <%= event.amount %>
<% end %>
<%= room.events.group(:currency).count(:amount) %>
<% end %>
但是这会打到每个房间对象的数据库,效率很低。如果要阻止此操作,请将事件加入控制器中的日程表中。这是一项完全不同的任务,但我相信你可以搞清楚。
答案 1 :(得分:0)
我建议编写一个执行处理的方法,并将其放在Ruby源文件而不是视图模板中。然后只需在模板中调用该方法。这是一个示例脚本,说明了应该起作用的方法:
#!/usr/bin/env ruby
require 'awesome_print'
def totals_by_currency(input_lines)
totals = Hash.new { |h, k| h[k] = 0.0 } # initialize new values to 0.0
input_lines.each_with_object(totals) do |line, totals|
_, currency, amount_as_string = line.split
totals[currency] += amount_as_string.to_f
totals[currency] = totals[currency].round(2)
end
end
input =
"event1 USD 1.23
event2 EUR 2.34
event3 JPY 100
event4 USD 4.56
"
ap totals_by_currency(input.split("\n"))
=begin
Outputs:
{
"USD" => 5.79,
"EUR" => 2.34,
"JPY" => 100.0
}
=end
此外,如果您想确保2个小数位,那么您可以使用''%。2f'将数字转换为字符串%:
'%.2f' % 1 # => "1.00"
此可以进入视图,因为它只是如何向用户显示数据的说明。