我正在开发一个 Rails 3.2.8 应用程序,它使用自主开发的 REST API 来插入新数据。插入逻辑对每个端点都是通用的,结果非常简单地调用 Model.save。
对于其中一种模型类型,我想首先检查是否存在预先存在的记录,如果存在,则更新而不是插入。如果代码允许我在 Controller 级别进行交互,这将通过使用 find_or_create_by 很容易,但是(我认为)我唯一的选择是覆盖模型中的保存方法或使用 before_save 回调。
我正在努力寻找一种方法来完成这项工作,因为在模型中调用 save 或 update_attributes 只会导致无限循环(原因很明显)。
有没有一种方法可以利用 before_save 或覆盖保存,这样我可以首先检查是否存在具有属性 x 和 y 的记录,如果存在,则检索该记录并执行更新,否则继续使用标准ActiveRecord 保存?
这是我的代码,因为它目前位于 Activity 模型中,由于无限循环问题而无法运行:
def save
a = UserActivity.find_or_initialize_by_user_id_and_activity_id(user_id: user_id, activity_id: activity_id)
a.update_attributes start_at: start_at, end_at: end_at.....
end
最佳答案
您似乎需要 find_or_create_by_*
方法。
为避免循环,您不应将其放在save
方法中,而应放在以下两个位置之一:
选项 1: Controller 级别
在实例化此 UserActivity
实例的 Controller 中,您改为编写:
a = UserActivity.find_or_create_by_user_id_and_activity_id(user_id: user_id, activity_id: activity_id)
a.update_attributes start_at: start_at, end_at: end_at.....
选项2:类方法
如果您发现自己将上述代码添加到多个 Controller 中,更好的方法是在 UserActivity
中定义一个新的类方法:
class UserActivity
def self.create_or_update_from_attrs(user_id, activity_id, start_at, end_at...)
a = UserActivity.find_or_create_by_user_id_and_activity_id(user_id: user_id, activity_id: activity_id)
a.update_attributes start_at: start_at, end_at: end_at.....
end
end
显然在 Controller 中:
UserActivity.create_or_update_from_attrs(...)
覆盖保存
当然,您也可以覆盖 save
方法,但这会重复 Rails 功能(find_or_create_by...
),因此违反了 DRY 和 一段时间后,当这与您遇到的其他情况发生冲突时,您可能会搬起石头砸自己的脚,所以我不鼓励使用它:
编辑:更新以避免无限循环
class UserActivity
def save
# If this is new record, check for existing and update that instead:
if new_record? && a = UserActivity.where(user_id: user_id, activity_id: activity_id).first
a.update_attributes start_at: start_at, end_at: end_at ...
return true # just to comply with Rails conventions
else
# just call super to save this record
super
end
end
end
关于ruby-on-rails - Rails 覆盖保存以执行选择性更新,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/13238616/