Как заставить рельсы возвращать атрибуты SUM (columnName) с правильным типом данных вместо строки? - PullRequest
3 голосов
/ 18 марта 2009

Предположим запрос следующей формы

operatingExpenses = Expense.find(:all,
      {:select=>"categories.activityType, categories.name heading, sum(amount) totalAmount",
      :joins => "inner join expense_categories categories on category_id = categories.id ",
      :group => "categories.activityType, categories.name",
      :order => "categories.activityType, totalAmount DESC"}
      )

Теперь сумма определяется как десятичное поле в схеме базы данных. например определение в Rails Migration будет

create_table :expenses do |table|
  table.column :created_at, :timestamp, :null=>false
  table.column :description, :string, :limit=>100, :null=>false
  table.column :amount, :decimal, :scale=>2, :precision=>10, :null=>false
  table.column :category_id, :integer, {:null=>false, :default =>1} 
end

Теперь набор записей, возвращаемых запросом, не соответствует следующему утверждению

assert_equal 800, operatingExpenses[1].totalAmount

<800> expected but was <"800.00">.

Почему столбец «Сумма / агрегат» возвращается в виде строки, а не того же типа данных, что и итоговый столбец базы данных? Я хотел бы избежать разбрызгивания .to_s или .to_f везде, чтобы обойти это. Есть идеи?

То, что я пытаюсь получить в конце этой радуги, - это перечисление денежных потоков, подобное этому - (для указанного диапазона дат .. дополнительное условие для запроса, указанного выше).

   cat Type      Heading      TotalAmount 
|  Operating |  ExpCatX001  | 4000 |
|            |  ExpCatX002  |  200 |
|  Financing |  ExpCatX003  | 1000 |
|  Investing |  ExpCatX004  | 4234 |
|            |  ExpCatX005  |  201 |

Ответы [ 4 ]

3 голосов
/ 20 марта 2009

Для пользовательских запросов, которые требуют в основном целого пользовательского оператора SQL (ваша находка выше не сильно абстрагируется от вас), я хотел бы создать небольшую быструю новую модель, которая представляет новую информацию. т.е.

class OperatingExpenseReportDatum
  attr_accessor :type, :heading, :total

  def initialize(row)
    # set values from row, like
    @total = row["total"].to_f
  end
end

, а затем запишите вспомогательный метод в модель, например:

class Expense < AR::Base
  ...
  def self.operating_expenses
    rows = connection.select_all "SQL STATEMENT HERE"
    rows.map { |row| OperatingExpenseReportDatum.new(row) }
  end
end

Тогда все в порядке генерации вашего отчета:

#controller
@expenses = Expense.operating_expenses

#view
<% @expenses.each do |expense| %>
  <%= expense.type %>: <%= expense.total %>
<% end %>

Или что-то подобное. :)

2 голосов
/ 18 марта 2009

Активная запись пытается вернуть объекты «Расходы», построенные по результатам вашего запроса. Но у объектов расходов нет поля totalAmount, так что это не запрашиваемые «бонусные данные», насколько может сказать ActiveRecord. Он может просто выбросить его, но вместо этого он хранит его для вас, с самыми простительными предположениями типа, какие только могут.

Мои предложения:

  • Не делай этого; Active Record не должен работать; общее количество деталей принадлежит тому, что их скручивает; это не сам объект детализации.
  • Если вы должны рассматривать это как деталь, назовите поле amount, чтобы ActiveRecord знал, что с ним делать.
  • Если в качестве собственного поля необходимо указать сумму, добавьте метод доступа в модель расходов следующим образом:

    def total_amount
        totalAmount.to_f
        end
    

    и используйте его так: operatingExpenses[1].total_amount в вашем коде.

1 голос
/ 18 марта 2009

Потому что с :select Rails не дает вам способа сообщить ему (или драйверу), к какому нативному типу привязывать поля результирующего набора (как только он будет получен - хотя большинство не всех драйверов будет внутренне знаю, что такое типы SQL столбцов) - поэтому все они записываются в виде строк, и информация о типах SQL обычно отбрасывается. Это относится ко всем полям все время. Вы должны выполнить преобразование вручную (to_f), точно так же, как метод Rails 'ActiveRecord.instantiate, внутренне, когда вы используете обычный ActiveRecord.find без :select или :joins (и он должен заполнять не строковый атрибут.)

Вы можете подтвердить это, посмотрев на метод select_raw драйвера вашей базы данных.

Cheers, V.

0 голосов
/ 18 марта 2013

Просто сделайте find_by_sql и примените SUM к десятичному столбцу:

operating_expenses = Expense.find_by_sql ['SELECT cat.activityType, cat.name heading, SUM(exp.amount) AS amount FROM expenses exp JOIN expense_categories cat ON exp.category_id = cat.id GROUP BY cat.id, cat.activityType, cat.name, exp.id ORDER BY cat.activityType, amount DESC']

Под столбцом суммы operating_expenses, вы получите ваше общее автоматически преобразованное в десятичное число.

N.B. : cat.id и exp.id необходимы для соблюдения стандарта SQL.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...