在一对多关联中,Model.new和models.new之间的区别是什么?

时间:2017-05-13 10:36:23

标签: ruby-on-rails

我有3个型号,Cart,CartItem,Product。在每一个中我都定义了这样的关联:

cart.rb

has_many :cart_items
has_many :products, through: :cart_items, source: :product

def add_product_to_cart(product)
  ci = cart_items.new
  ci.product = product
  ci.quantity = 1
  ci.save
end

cart_item.rb

belongs_to :cart
belongs_to :product

这里是cart_item的相关列, db / schema.rb

create_table "cart_items", force: :cascade do |t|

   t.integer  "cart_id"
   t.integer  "product_id"
   t.integer  "number",     default: 1
   .
   .
   .

end

然后我在控制台中执行了此操作:

>> p = Product.first
>> c = Cart.create
>> c.add_product_to_cart(p)
>> CartItem.last

我的问题是:

在cart.rb第一行add_product_to_cart(product)

当我使用ci = cart_items.new时,它运作正常,新的cart_item有一个:cart_id = 2

但是当我使用ci = CartItem.new时,新的cart_item :cart_id =>没有,这次cart_item没有得到cart_id。

为什么会这样?在controller.rb中,new方法似乎遵循大写Class_name。

区别是什么?我应该何时使用cart_items.new,何时应使用CartItem.new

2 个答案:

答案 0 :(得分:2)

我不知道models.new,但我可以验证它是否有效。它是构建的别名,您可以在此处看到:https://github.com/rails/rails/blob/5-0-stable/activerecord/lib/active_record/associations/collection_proxy.rb#L292

我认为Rails在这里容易混淆,因为我从未见过new的实例方法实现,这就是为什么我最初认为这是你的写错误。

对我来说,new与一个保留字接壤,用于构造类的实例。

那么我在说什么?

在Ruby中,没有用于实例化类的保留关键字,例如在Java中编写new CartItem()来构建实例。相反,所有类都提供一个名为new的(类/静态)方法,该方法将构建一个新对象并从类中调用initialize实例方法来设置实例。

ci = CartItem.new无法按预期工作的原因是因为您在没有任何上下文的情况下初始化CartItem的新实例。你从cart.rb内部调用它的事实并不重要。无法推断您希望这个新的CartItem属于特定的购物车记录。

当您调用cart.cart_items时,您会得到一个名为ActiveRecord Association Collection Proxy的特殊对象,该对象不是它自己的实体,而是一个带有调用下一个方法的必要上下文的对象。链条。

此对象知道您的上下文是CartItems,它知道它是在特定Cart的上下文中构建的,在您的情况下,cart_id为2。

在集合代理上调用build时,例如

my_new_cart_item = cart.cart_items.build

它会自动将新购物车项目实例的cart_id设置为其构建的购物车的ID。

或者,你可以调用CartItem.new(cart_id:2)来获得相同的结果。

总之:

我认为你应该从不使用cart_items.new,因为它的反ruby约定应该真的只是抛出一个NoMethodError。这似乎是Rails团队合并它的脑力,但是当Rails年轻时,为方便起见,添加了许多聪明的东西。我建议您使用cart_items.build,它执行相同的操作,但其他rails开发人员可以清楚地识别它。

如果要构建应属于现有购物车的新实例,请使用cart_items.build。当您想要构建CartItem而不将其设置为属于特定购物车时,请使用CartItem.new

在这种情况下,听起来后者并没有多大意义,因为没有购物车的购物车项目并不是真的。但是如果你想象你有一辆汽车和一辆汽车有很多车轮,那么拥有一个不属于特定汽车的车轮可能是有意义的,因为它可能只是存放在库存中。

答案 1 :(得分:0)

在ActiveRecord对象上调用.create方法时,您将从头开始对象,实现它,并将其全部保存在一个步骤中。由于它正在保存,它将进入数据库并被分配:cart_id。如果您的模型中有AR对象的验证,那么如果您未通过这些验证,则会看到调用.create方法将失败。

或者,在AR对象上调用.new方法只会启动该对象,允许您在必须专门调用该对象上的.save方法之前以您认为合适的方式实现它。在您致电.save之前,您实际上并未创建任何可插入数据库的内容,因此您的:cart_id将为nil

进入控制台并尝试不同的方法,包括.create在一个对象上,.new.save在一个单独的对象上。