Как я могу сравнить BigDecimal с десятичным полем ActiveRecord? - PullRequest
2 голосов
/ 09 июня 2011

Предположим схему, подобную этой:

create_table "bills", :force => true do |t|
  t.decimal  "cost", :precision => 10, :scale => 5
end

Я хочу написать функцию, которая записывает новый счет в БД, если он уникален.Следующее не работает:

def load_bill_unless_exists(candidate)
  incumbents = Bill.scoped.where(:cost => candidate.cost)
  candidate.save unless incumbents.exists?
end

, поскольку действующие и потенциальные счета имеют разные ограничения в своем представлении BigDecimal, поэтому тест :cost => candidate.cost не пройден.То есть он сравнивает:

candidate: #<Bill id: nil, cost: #<BigDecimal:105e39850,'0.1670576666 6666666E4',27(27)>>

с

incumbent: #<ServiceBill id: 198449, cost: #<BigDecimal:105e35840,'0.167057667E4',18(18)>>

Обратите внимание, что BigDecimal кандидата представляет стоимость с большим количеством цифр, чем у действующего.

Таким образом, вопрос прост: Как правильно провести это сравнение?Я размышлял :cost => BigDecimal.new(candidate.cost.to_s, 18), но это не так - например, откуда взялась эта цифра 18?

Ответы [ 2 ]

1 голос
/ 10 июня 2011

Попробуйте использовать BigDecimal#round:

def load_bill_unless_exists(candidate)
  incumbents = Bill.scoped.where(:cost => candidate.cost.round(5))
  candidate.save unless incumbents.exists?
end

Из документов:

Округлить до ближайшего 1 (по умолчанию), возвращая результат в виде BigDecimal.Если n задано и положительно, дробная часть результата имеет не более, чем столько цифр.

Учитывая, что вы указали точность 5 в вашей схеме, это то, что вы должны округлятькогда делать сравнения.

1 голос
/ 09 июня 2011

Если кастование похоже на то, что вы рассматриваете работы, вам, вероятно, придется пойти на это. Используемый вами запрос просто строит «WHERE cost = number», и если база данных не может правильно сравнить с переданным числом, вам нужно передать его по-другому. Похоже, что это база данных, которая останавливает вас, а не что-то в Rails обязательно.

Если вам просто не нравится приведение в запросе, вы всегда можете сделать это в модели:

def cost_with_incumbent_precision
  BigDecimal.new(cost.to_s, 18)
end
...