我有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
答案 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
在一个单独的对象上。