Ruby on Rails ноль не может быть приведен в BigDecimal - PullRequest
6 голосов
/ 15 октября 2011

Почему я получаю nil can't be coerced into BigDecimal, когда пытаюсь выполнить расчет: вот код:

модель / drink.rb

class Drink < ActiveRecord::Base
  belongs_to :menu 
  before_save :total_amount 

def total_amount
    self.total_amount = self.price * self.quantity
end 

модель / menu.rb

class Menu < ActiveRecord::Base
    has_many :drinks, :dependent => :destroy
    accepts_nested_attributes_for :drinks, :allow_destroy => true
    #Validations

end

* Напиток - это (вложенная) дочерняя модель и Меню родительской модели Когда я пытаюсь создать новый напиток, в браузере появляется следующее сообщение об ошибке nil can't be coerced into BigDecimal app/models/drink.rb:7:in 'total-amount' app/controllers/menus_controller.rb:47:in 'create' app/controllers/menus_controller.rb:46:in 'create'

Приложение / дб / миграция

class CreateDrinks < ActiveRecord::Migration
  def change
    create_table :drinks do |t|
      t.string :name
      t.decimal :quantity,:precision => 8, :scale => 2
      t.decimal :price, :precision => 8, :scale => 2
      t.decimal :vat, :precision => 8, :scale => 2
      t.references :menu

      t.timestamps
    end
    add_index :drinks, :menu_id
  end
end

Контроллеры / drinks_controller.rb

   class DrinksController < ApplicationController
      # GET /drinks
      # GET /drinks.json
      def index
        @drinks = Drink.all

        respond_to do |format|
          format.html # index.html.erb
          format.json { render :json => @drinks }
        end
      end

      # GET /drinks/1
      # GET /drinks/1.json
      def show
        @drink = Drink.find(params[:id])

        respond_to do |format|
          format.html # show.html.erb
          format.json { render :json => @drink }
        end
      end

      # GET /drinks/new
      # GET /drinks/new.json
      def new
        @drink = Drink.new

        respond_to do |format|
          format.html # new.html.erb
          format.json { render :json => @drink }
        end
      end

      # GET /drinks/1/edit
      def edit
        @drink = Drink.find(params[:id])
      end

      # POST /drinks
      # POST /drinks.json
      def create
        @article = Drink.new(params[:drink])

        respond_to do |format|
          if @drink.save
            format.html { redirect_to @drink, :notice => 'Drink was successfully created.' }
            format.json { render :json => @drink, :status => :created, :location => @article }
          else
            format.html { render :action => "new" }
            format.json { render :json => @drink.errors, :status => :unprocessable_entity }
          end
        end
      end

      # PUT /drinks/1
      # PUT /drinks/1.json
      def update
        @drink = Drink.find(params[:id])

        respond_to do |format|
          if @drink.update_attributes(params[:drink])
            format.html { redirect_to @drink, :notice => 'Drink was successfully updated.' }
            format.json { head :ok }
          else
            format.html { render :action => "edit" }
            format.json { render :json => @drink.errors, :status => :unprocessable_entity }
          end
        end
      end

      # DELETE /drinks/1
      # DELETE /drinks/1.json
      def destroy
        @drink = Drink.find(params[:id])
        @drink.destroy

        respond_to do |format|
          format.html { redirect_to drinks_url }
          format.json { head :ok }
        end
      end 
    end 

Пожалуйста, кто-нибудь может сказать мне, что не так с кодом?

Ответы [ 2 ]

18 голосов
/ 15 октября 2011

Если вы хотите, чтобы nil оценивался как 0.0, вы можете сделать что-то вроде этого:

def total_amount
    self.total_amount = self.price.to_s.to_d * self.quantity.to_s.to_d
end 

Или явно проверить на ноль

def total_amount
  if self.price && self.quantity
    self.total_amount = self.price * self.quantity
  else
    self.total_amount = "0.0".to_d
  end
end 

Проблема в том, что ваши поля записей не установлены так, как вы ожидаете. Вам нужно использовать проверки, чтобы убедиться, что поля price и quantity установлены?

class Drink
  validates :price, :presence => true      # Don't forget add DB validations, too :)
  validates :quantity, :presence => true
end

Таким образом вы гарантируете, что при вызове # total_amount вы не получите нулевое значение.

2 голосов
/ 15 октября 2011

Вы не устанавливаете значение quantity в любом месте, поэтому оно nil и nil не может быть приведено к числу для умножения.

Предположительно, ваш price является decimal(n,2)(для некоторых n) в базе данных, поэтому self.price представляется в Ruby как объект BigDecimal;вот почему вы получаете жалобы на неспособность принудительно применить BigDecimal.

Вы можете получить похожую ошибку из irb, например:

>> 11 * nil
TypeError: nil can't be coerced into Fixnum
    from (irb):7:in `*'

Ваш self.price - этоустановить хотя.Если это не так, вы получите один из них:

NoMethodError: undefined method `*' for nil:NilClass

Возможно, вы захотите добавить

validates_presence_of :quantity, :price

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

...