Rails accepts_nested_attributes_for 的验证问题

xiaoronglv · 2014年03月11日 · 最后由 lionzixuanyuan 回复于 2014年03月12日 · 4026 次阅读

出于性能的考虑,对商品表做了垂直分割,用两张表来保存商品的信息。

  • 商品的概览(products)

    id title price

  • 商品的图文介绍(products_details)

    id product_id description

为了在一个表单中保存编辑两个对象,我使用了 Rails 的 nested attributes。

Model

class Product < ActiveRecord::Base
  has_one :product_detail, dependent: :destroy
  attr_accessible :title, :price, product_detail_attributes
  accepts_nested_attributes_for :product_detail
end

class ProductDetail < ActiveRecord::Base
  belong_to :product

  #此处是个坑
  #validates :product_id, presence: true
end

View

<%= simple_form_for [:admin, product] do |f| %>
  <%= f.input :title, label: "产品名"%>
  <%= f.input :price, label: "市场价" %>
  <%= f.simple_fields_for :product_detail do |detail| %>
    <%= detail.input :markdown_description, label: "产品描述", as: :text %>
  <% end -%>
<% end -%>

Controller

def  new
  @product = Product.new

  # 如果是 has one,则使用 build_** 初始化
  @product_detail = @product.build_product_detail

  # 如果是 has many,则使用 *s.build 初始化
  # @promotion = @product.promotions.build
end

def create
  @product = Product.new(params[:product])

  if @product.save
    redirect_to @product
  else
    render ‘edit’
  end
end

事务

@product.save 时,二个对象的创建被包裹在一个事务中,同时创建。

part1. 创建 product

part2. 创建 product_detail

log

(0.2ms)  BEGIN
SQL (0.3ms)  INSERT INTO `products` ...
SQL (0.4ms)  INSERT INTO `product_details` ...
(206.7ms)  COMMIT

一个和验证相关的 bug

一开始的时候 product 和 product_detail 总是无法同时创建,log 打印出错误信息 'product_detail 的 product_id 不能为空'。

花了 2h 排插各种错误,无果。

class ProductDetail < ActiveRecord::Base
  belong_to :product

  # 注释掉后就可以保存 product_detail了
  validates :product_id, presence: true
end

后来发现是一个狗血的验证导致的。

为什么这个验证会导致创建失败呢? product_detail 有 product_id 这个属性啊?

  1. product 和 product_detail 创建时,会整体上检查对象是否有效(valid?)

  2. 此时 product 还没有创建,product_detail 无法获得 prodcut_id

  3. 所以 product_detail 验证失败。

如果两个对象的创建包裹在一个事务中时,第二个对象的某些属性可能暂时为空,导致验证失败?

现在能找到会拼写 accepts_nested_attributes_for 单词的人都不多

正经回答 validates_asociated . Do NOT validate foreign key id.

哈哈,楼上吐槽别人的拼写之前先检查检查自己的 ....

#3 楼 @swordray

谢楼上

validates_associated(*attr_names)

Validates whether the associated object or objects are all valid. Works with any kind of association.

问个不相关的问题,为这么这里是 goods_detail

#7 楼 @lionzixuanyuan

不要意思,写错了。

已经修改了,感谢你的提醒。

#8 楼 @xiaoronglv 客气了,前段时间刚刚研究了下 nested_attributes,看到你的帖子正好温故知新了

需要 登录 后方可回复, 如果你还没有账号请 注册新账号