首页 > 解决方案 > @order.save 的未定义方法“item_id”

问题描述

我正在使用关系数据库创建一个 Rails 应用程序,当我尝试将出现 NoMethodError 的订单保存到我传递给 order.new 的字符串时,我是 rails 的新手,可能不完全了解如何继续处理记录。但是调试器无法告诉我在哪里搜索错误。

控制器

class OrdersController

  def create
    #render plain: params
    a = {amount: params[:amount], user_id: current_user.id, id: 1}
    @order = Order.new a

    a = {quantity: params[:quantity], item_id: params[:item], order_id: 1}
    @order_description = OrderDescription.new a
    @order.save
    @order_description.save
  end

错误

undefined method `item_id' for #<Order id: 1, amount: 2222, created_at: nil, updated_at: nil, user_id: 19>

D b

  create_table "items", force: :cascade do |t|
    t.string "name"
    t.string "description"
    t.integer "price"
    t.datetime "created_at", precision: 6, null: false
    t.datetime "updated_at", precision: 6, null: false
  end

  create_table "order_descriptions", force: :cascade do |t|
    t.integer "quantity"
    t.datetime "created_at", precision: 6, null: false
    t.datetime "updated_at", precision: 6, null: false
    t.bigint "item_id", null: false
    t.bigint "order_id", null: false
    t.index ["item_id"], name: "index_order_descriptions_on_item_id"
    t.index ["order_id"], name: "index_order_descriptions_on_order_id"
  end

  create_table "orders", force: :cascade do |t|
    t.integer "amount"
    t.datetime "created_at", precision: 6, null: false
    t.datetime "updated_at", precision: 6, null: false
    t.bigint "user_id", null: false
    t.index ["user_id"], name: "index_orders_on_user_id"
  end

  create_table "users", force: :cascade do |t|
    t.datetime "created_at", precision: 6, null: false
    t.datetime "updated_at", precision: 6, null: false
    t.string "email", default: "", null: false
    t.string "encrypted_password", default: "", null: false
    t.string "reset_password_token"
    t.datetime "reset_password_sent_at"
    t.datetime "remember_created_at"
    t.string "last_name"
    t.string "first_name"
    t.boolean "admin", default: false
    t.index ["email"], name: "index_users_on_email", unique: true
    t.index ["reset_password_token"], name: "index_users_on_reset_password_token", unique: true
  end

  add_foreign_key "order_descriptions", "items"
  add_foreign_key "order_descriptions", "orders"
  add_foreign_key "orders", "users"

我试图删除 ruby​​ 模型中的关系,但没有帮助。无论我传递给它的数据如何,都会发生此错误。

真正解决

在文件夹 test/fixters 中,我找到了一个名为 order.yml 的文件,其中包含 item_id 字段(天知道它是如何出现的),并且由于这些字段 order.save 没有通过验证测试。

标签: ruby-on-railsrubydatabasenomethoderror

解决方案


您似乎正在尝试建立has_many through:关联。Whereorder_descriptions用作订单的行项目

class Order < ApplicationRecord
  belongs_to :user
  has_many :order_descriptions
  has_many :items, through: :order_descriptions
end

# very vague name - you could do better
class OrderDescription < ApplicationRecord
  belongs_to :item
  belongs_to :order
end

class Item < ApplicationRecord
  has_many :order_descriptions
  has_many :orders, through: :order_descriptions
end

如果您想创建一个经典的订单表单,您可以在其中创建多行订单,您可以使用accepts_nested_attributes

class Order < ApplicationRecord
  has_many :order_descriptions
  has_many :items, through: :order_descriptions
  accepts_nested_attributes_for :order_descriptions, reject_if: :all_blank?
end

这使您可以传递一组属性(哈希)来同时创建父记录和子记录:

Order.create(
  user: User.first,
  order_descriptions_attributes: [
    { item_id: 1, quantity: 10 },
    { item_id: 2, quantity: 20 }
  ]
)

您可以使用以下形式处理此问题fields_for

<%= form_with(model: @order) do |form| %>
  <%= form.fields_for(:order_descriptions) do |ff| %>
    <div class="field">
      <%= ff.label :item_id %>
      <%= ff.collection_select :item_id, @items, :id, :name %>
    </div>
    <div class="field">
      <%= ff.label :quantity %>
      <%= ff.number_field :quantity, step: 1 %>
    </div>
  <% end %>
<% end %>

在你的控制器中:

class OrdersController
  before_action :set_order, only: [:show, :edit, :update, :destroy]

  def show
  end

  def new
    @order = Order.new
    @items = Item.all
    5.times { @order.order_descriptions.new } 
  end

  def create
    @order = Order.new(order_params) do |order|
      order.user = current_user
    end
    if @order.save
      redirect_to @order 
    else
      @items = Item.all
      5.times { @order.order_descriptions.new } 
      render :new
    end
  end

  private

  def set_order
     @order = Order.find(params[:id])
  end
  
  def order_params
    params.require(:order)
          .permit(
            order_descriptions_attributes: [:id, :item_id, :quantity]
          )
  end
end

order_descriptions_attributes: [:item_id, :quantity]允许带有键 和 的:id散列:item_id数组:quantity

就用户体验而言,它确实远非理想 - 对于您的典型网店,您需要预先创建订单(或其他术语购物车),然后设置嵌套路线,用户可以在其中将订单项添加到订单中。


推荐阅读