#ruby-on-rails #ruby #null #actioncontroller
#ruby-on-rails #ruby #null #actioncontroller
Вопрос:
Я пытаюсь добавить строку в свой orders_items
контроллер, чтобы, если это новый порядок, увеличить счетчик с нуля до единицы. итак, я создал действие, которое выполняет это до вызова сохранения, но когда я пытаюсь это сделать, я получаю:
undefined method ` ' for nil:NilClass
def create
@order_item = @order.order_items.find_or_initialize_by_product_id(params[:product_id])
# Error below
@order_item.quantity = 1
respond_to do |format|
if @order_item.save
end
end
order_item.rb
:
class OrderItem < ActiveRecord::Base
belongs_to :order
belongs_to :product
validates :order_id, :product, presence: true
validates :quantity, numericality: { only_integer: true, greater_than: 0 }
def subtotal
quantity * product.price
end
end
order_items_controller.rb
:
class OrderItemsController < ApplicationController
before_action :set_order_item, only: [:show, :edit, :destroy]
before_action :load_order, only: [:create]
# GET /order_items
# GET /order_items.json
def index
@order_items = OrderItem.all
end
# GET /order_items/new
def new
@order_item = OrderItem.new
end
# POST /order_items
# POST /order_items.json
def create
@order_item = @order.order_items.find_or_initialize_by_product_id(params[:product_id])
@order_item.quantity = 1
respond_to do |format|
if @order_item.save
format.html { redirect_to @order, notice: 'Successfully added product to cart.' }
format.json { render action: 'show', status: :created, location: @order_item }
else
format.html { render action: 'new' }
format.json { render json: @order_item.errors, status: :unprocessable_entity }
end
end
end
# PATCH/PUT /order_items/1
# PATCH/PUT /order_items/1.json
def update
@order_item = OrderItem.find(params[:id])
respond_to do |format|
if order_item_params[:quantity].to_i == 0
@order_item.destroy
format.html { redirect_to @order_item.order, notice: 'Order item was successfully updated.' }
format.json { head :no_content }
elsif @order_item.update(order_item_params)
format.html { redirect_to @order_item.order, notice: 'Successfully updated the order item.' }
format.json { head :no_content }
else
format.html { render action: 'edit' }
format.json { render json: @order_item.errors, status: :unprocessable_entity}
end
end
end
# DELETE /order_items/1
# DELETE /order_items/1.json
def destroy
@order_item.destroy
respond_to do |format|
format.html { redirect_to @order_item.order }
format.json { head :no_content }
end
end
private
# Use callbacks to share common setup or constraints between actions.
def load_order
@order = Order.find_or_initialize_by_id(session[:order_id], status: "unsubmitted")
if @order.new_record?
@order.save!
session[:order_id] = @order.id
end
end
def set_order_item
@order_item = OrderItem.find(params[:id])
end
# Never trust parameters from the scary internet, only allow the white list through.
def order_item_params
params.require(:order_item).permit(:product_id, :order_id, :quantity)
end
end
Файл миграции:
class AddDefaultQuantityToOrderItems < ActiveRecord::Migration
def change
change_column :order_items, :quantity, :integer, default: 0
end
end
rails console
:
@order.order_items.find_or_initialize_by_product_id(params[:product_id])
NoMethodError: undefined method `order_items' for nil:NilClass
from (irb):1
from /usr/local/rvm/gems/ruby-2.0.0-p247/gems/railties-4.0.4/lib/rails/commands/console.rb:90:in `start'
from /usr/local/rvm/gems/ruby-2.0.0-p247/gems/railties-4.0.4/lib/rails/commands/console.rb:9:in `start'
from /usr/local/rvm/gems/ruby-2.0.0-p247/gems/railties-4.0.4/lib/rails/commands.rb:62:in `<top (required)>'
from bin/rails:4:in `require'
from bin/rails:4:in `<main>'
Модель order.rb
:
class Order < ActiveRecord::Base
has_many :order_items, dependent: :destroy
def total
order_items.map(amp;:subtotal).sum
end
end
orders_controller.rb
:
class OrdersController < ApplicationController
before_action :set_order, only: [:show, :edit, :update, :destroy]
# GET /orders
# GET /orders.json
def index
@orders = Order.all
end
# GET /orders/1
# GET /orders/1.json
def show
end
# GET /orders/new
def new
@order = Order.new
end
# GET /orders/1/edit
def edit
end
# POST /orders
# POST /orders.json
def create
@order = Order.new(order_params)
respond_to do |format|
if @order.save
format.html { redirect_to @order, notice: 'Order was successfully created.' }
format.json { render action: 'show', status: :created, location: @order }
else
format.html { render action: 'new' }
format.json { render json: @order.errors, status: :unprocessable_entity }
end
end
end
# PATCH/PUT /orders/1
# PATCH/PUT /orders/1.json
def update
respond_to do |format|
if @order.update(order_params)
format.html { redirect_to @order, notice: 'Order was successfully updated.' }
format.json { head :no_content }
else
format.html { render action: 'edit' }
format.json { render json: @order.errors, status: :unprocessable_entity }
end
end
end
# DELETE /orders/1
# DELETE /orders/1.json
def destroy
@order.destroy
respond_to do |format|
format.html { redirect_to products_path }
format.json { head :no_content }
end
end
private
# Use callbacks to share common setup or constraints between actions.
def set_order
@order = Order.find(params[:id])
end
# Never trust parameters from the scary internet, only allow the white list through.
def order_params
params.require(:order).permit(:user_id, :status)
end
end
Update
Rake routes shows route call for /:product_id
product.rb
class Product < ActiveRecord::Base
validates_numericality_of :price
validates :stock ,numericality: { greater_than_or_equal_to: 0 }
end
order.rb
# @Chiperific added proper 4-line indentation for SO viewing.
class OrderItem < ActiveRecord::Base
belongs_to :order
belongs_to :product
validates :order_id, :product, presence: true
validates :quantity, numericality: { only_integer: true, greater_than: 0 }
def subtotal
quantity * product.price
end
end
заказать элементы.rb
class OrderItem < ActiveRecord::Base
belongs_to :order
belongs_to :product
validates :order_id, :product, presence: true
validates :quantity, numericality: { only_integer: true, greater_than: 0 }
def subtotal
quantity * product.price
end
end
когда я добавляю
def create
@order_item = OrderItems.find_or_initialize_by_product_id(params[:product_id])
@order_item.quantity =1
я получаю NameError в OrderItemsController#create — неинициализированная константа OrderItemsController::Orderitems
определить create @order_item = Orderitems.find_or_initialize_by_product_id(параметры[:product_id]) <-ошибка @order_item.количество = 1
если я попытаюсь
@order = Order.find(params[:order_id])
@order_item = @order.order_items.find_or_initialize_by_product_id(params[:product_id])
@order_item.quantity = 1
я получаю ActiveRecord::RecordNotFound в OrderItemsController#create — не удалось найти порядок без идентификатора
def create
@order = Order.find(params[:order_id])
@order_item = @order.order_items.find_or_initialize_by_product_id(params[:product_id])<-error
@order_item.quantity = 1
Комментарии:
1. Я предполагаю, что ваше количество не начинается с 0. Попробуйте использовать отладчик и выполнить пошаговое выполнение или создать элемент заказа из консоли, чтобы увидеть, начинается ли количество с 0 с самого начала. Просто чтобы перепроверить, ваша миграция была запущена и сервер был перезапущен?
2.
@order_item.quantity
не определен. Возможно, вам потребуется его инициализировать3. Есть ли вероятность, что вы
@order_item
существовали ДО того, как в вашей схеме базы данных было установлено значение по умолчанию? Я предполагаю, что это произойдет, если вы запустите консоль rails@order_item.quantity
, это приведет кnil
и не0
4. @Anthony: По сути, с миграцией все должно быть в порядке. Когда миграция завершена, все
quantity
присутствующие в вашей таблицеorder_items
были инициализированы в 0. Я думаю, что проблема возникает сама по@order_item
себе. Вы пробовали@order.order_items.find_or_initialize_by_product_id(params[:product_id])
прямо на своей консоли?5. @bobbystouket Некоторые базы данных (например, Postgres) не присваивают автоматически обновленные значения по умолчанию существующим записям столбцов. Ему нужно было бы сделать
OrderItem.all
и посмотреть, не были ли какие-либо величины установлены на ноль после этой миграции, и вручную исправить их.
Ответ №1:
Я не вижу, где @order
определено. Поскольку @order
равно null, @order_items
также будет.
Я предполагаю, что ваш URL-адрес просмотра выглядит примерно так /orders/:order_id/order_items/:id
, поэтому давайте используем параметр URL, чтобы убедиться, что вы получили Order
Order_ID
инициированный or .
Вариант 1: не беспокойтесь о @order
def create
@order_item = OrderItem.find_or_initialize_by_product_id(params[:product_id])
@order_item.quantity =1
...
end
Вариант 2: сначала установите `@order`
def create
@order = Order.find(params[:order_id])
@order_item = @order.order_items.find_or_initialize_by_product_id(params[:product_id])
@order_item.quantity = 1
...
end
Другая потенциальная проблема:
.find_or_initialize_by_product_id(params[:product_id])
на самом деле не выполняется поиск или инициализация.
Похоже, это ваше отношение к таблице:
[Orders]`````
|--> [Order_Items]
[Products]___/
- Я не понимаю, где
accepts_nested_attributes_for :order_items
у вас естьorders.rb
- Вероятно, вам также нужно
accepts_nested_attributes_for :order_items
products.rb
- Есть ли у
Order_Item
записиProduct_id
поле? Я вижу, где вы проверяете наличие:product
, но нет:product_id
, как выглядит ваша схема? Что произойдет, если вы попытаетесь `find__или_initialize_by_product(параметры[:product_id]) - Ваш маршрут требует
/:product_id
или/:product
или ни того, ни другого? (он же есть ?)params[:product_id]
Обновить:
Спасибо за обновленные комментарии. Пожалуйста, добавьте весь используемый URI, всю строку Rake Routes или файл Routes.rb. Или, хотя я знаю, что это не лучшая практика, вы можете опубликовать ссылку на проект на Github, и я посмотрю там.
Я все еще убежден, что (по крайней мере, одна из) проблем заключается в том, что @order не устанавливается.
Попробовать его в консоли Rails — отличный способ узнать .find_or_initialize_by_product_id
, работает ли
Но консоль rails не может извлекать параметры, и вы все еще не назначаете @order первым. Для тестирования в консоли rails попробуйте:
@order = Order.first #<----(just so you get @order assigned)
@order.order_items.find_or_initialize_by_product_id(1)
Также:
@order_item = OrderItems.find_or_initialize_by_product_id(params[:product_id])
OrderItems
должно быть в единственном числе (мой плохой !!):
@order_item = OrderItem.find_or_initialize_by_product_id(params[:product_id])
Я уделю более пристальное внимание этому сообщению и буду более своевременен в своих сообщениях, поскольку уверен, что вы расстраиваетесь. Держитесь, ваша методология хороша.
Но может быть и другой способ, если мы не сможем устранить эти проблемы. Можем ли мы найти количество элементов заказа, подсчитав количество записей с определенным идентификатором заказа вместо статического сохранения числа в базе данных? Просто мысль, если вы готовы отказаться от этой проблемы.
Ответ №2:
Вы можете инициализировать @order_item.quantity, поместив это в свой класс OrderItem:
def quantity
self.quantity ||= 0
end
Комментарии:
1. Проблема не в этом. Количество инициализируется в 0 из схемы.
2. @Chiperific вы ошибаетесь, это именно проблема. В показанном ruby-коде нет инициализации количества. Он применяется при сохранении и может быть применен к схеме, но при создании нового
OrderItem
quantity
значения равно нулю. Это также именно то, что указывает ошибка:is not defined for nil
таким образом, количество равно нулю. Это очень чистое решение, другой альтернативой было бы добавитьafter_initialize
в модель, чтобы явно установить количество равным 0, если nil . Это должно быть принятым ответом.3. Итак, почему OP получает эту ошибку независимо от того, является ли это новой записью или нет?
Ответ №3:
Перейдите в свой метод find_or_initialize_by_product_id
и убедитесь, что вы инициализируете атрибут quantity
равным 0.
Вот простое воспроизведение вашей ошибки — обратите внимание, что я никогда не инициализировал x
значение 0:
$ irb
irb(main):001:0> x = 5
NoMethodError: undefined method ` ' for nil:NilClass
from (irb):1
from /usr/bin/irb:11:in `<main>'
Комментарии:
1. @order_item определен в предыдущей строке.
2. Вы правы — я имел в виду атрибут «количество».
Ответ №4:
В Rails 4 вы можете использовать следующее:
@order_item = @order.order_items.where(product_id: params[:product_id]).first_or_create
вместо
@order_item = @order.order_items.find_or_initialize_by_product_id(params[:product_id])
и это:
@order = Order.where(session[:order_id], status: "unsubmitted").first_or_initialize
вместо
@order = Order.find_or_initialize_by_id(session[:order_id], status: "unsubmitted")