Rails ApplicationRecord 对象之间是如何关联的?临时修改保存在哪里?谁能讲解一下

Magic_0919 · 2018年07月31日 · 最后由 breeze 回复于 2018年07月31日 · 956 次阅读

我有这么一个问题,采用嵌套表单接收了表单数据。按照 Rails 文档可以知道关联对象会自动建立 Record 对象,

# class OrderController < BaseController
@order = Settlement.find(params[:id])
@order.assign_attributes(order_params)

if @order.update_actual_amount and @order.save
  redirect_to order_path(@order)
else
  redirect_to action: 'edit'
end

Save 方法会更新 order 及其关联付款单对象 payment,update_actual_amount 则负责将各个 payment 的实际付款金额加和,保存到 order 对象中。

# class Order < ApplicationRecord
def update_actual_amount
    actual_amount = 0.0
    self.payments.each do |payment|
      actual_amount += payment.actual_amount
    end
    self.actual_amount = actual_amount
  end

但实际情况是,上面对 actual_amount 的更新没有正确执行,因为获取到的 payment.actual_amount 是数据库查出来的旧数据,而我需要从表单接受来的“临时”数据

也就是说,如果我的代码没错的话,order 在控制器中关联的 payment 是有修改的 Record 对象(不然不可能发生更新),但在执行内部方法时候关联的 payments 却是重新查询出来的新数据。

为什么会发生这种情况?Record 之间的关联是怎么设计的?为什么会有这种设计?如果我需要完成上述功能,推荐的做法是什么?

P.S. Rails 关于嵌套表单的表述:https://ruby-china.github.io/rails-guides/form_helpers.html#building-complex-forms P.P.S 上文所有代码是根据实际情况改编得来,如果有拼写错误请忽略。

2 楼 已删除
accepts_nested_attributes_for :payments  # 将动态创建 payments_attributes=  方法

# 假设order_params如下
order_params = {
  payments_attributes: { id: 1, actual_amount: 88 }
}

# assign_attributes 将触发 payments_attributes= 方法,找到相应payments,并修改。
@order.assign_attributes(order_params)

具体的源码看 accepts_nested_attributes_for 是如何动态创建 payments_attributes= 方法

按顺序查看这三个关键方法就明白了

https://github.com/rails/rails/blob/master/activerecord/lib/active_record/nested_attributes.rb#L333

accepts_nested_attributes_for(*attr_names)

https://github.com/rails/rails/blob/master/activerecord/lib/active_record/nested_attributes.rb#L369

generate_association_writer(association_name, type)

https://github.com/rails/rails/blob/master/activerecord/lib/active_record/nested_attributes.rb#L466

assign_nested_attributes_for_collection_association(association_name, attributes_collection)

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