如何确定在after_save中是否只是创建或更新记录


95

#new_record?函数确定记录是否已保存。但这总是错误的after_save。有没有一种方法可以确定记录是新创建的记录还是更新后的旧记录?

我希望不要使用其他回调,例如before_create在模型中设置标志或要求对数据库进行其他查询。

任何建议表示赞赏。

编辑:需要确定它after_save,对于我的特定用例,没有updated_at或没有updated_on时间戳


1
嗯,也许在before_save中传递了一个参数?只是大声思考
旅行

Answers:


167

我一直在寻找将其用于after_save回调。

使用更简单的解决方案id_changed?(因为它不会在上更改update),或者即使created_at_changed?存在时间戳列也可以使用。

更新:@mitsy指出,如果在回调之外需要此检查,请使用id_previously_changed?。参见docs



6
最好用after_update和after_create来区别。回调可以共享一个通用方法,该方法采用一个参数来指示它是创建还是更新。
matthuhiggins

2
这可能已经改变。至少在Rails 4中,after_save回调在after_create或after_update回调之后运行(请参阅guides.rubyonrails.org/active_record_callbacks.html)。
2014年

3
检查这两个字段都不在之外工作after_save
fatuhoku

3
id_changed?保存记录后(至少在挂钩之外)将为false。在这种情况下,您可以使用id_previously_changed?
mltsy

30

据我所知,这里没有轨魔术,您必须自己做。您可以使用虚拟属性来清理它...

在您的模型课中:

def before_save
  @was_a_new_record = new_record?
  return true
end

def after_save
  if @was_a_new_record
    ...
  end
end

26

另一种选择,对于那些谁有一个updated_at时间戳:

if created_at == updated_at
  # it's a newly created record
end

这不是一个坏主意,但似乎在某些情况下可能适得其反(不一定是防弹)。
Ash Blue

根据运行迁移的时间,created_at和updated_at记录可能会关闭。同样,您总是有机会在最初保存记录后立即更新记录,这可能会使时间不同步。这不是一个坏主意,只是觉得可以添加更多的防弹实施。
Ash Blue

同样,在最初创建记录之后,它们可能相等。如果某条记录几个月没有更新,则看起来就像刚刚创建的一样
bschaeffer 2014年

1
@bschaeffer对不起,我的问题是“是否有可能在首次创建回调函数的任何时候都created_at等于它?” updated_atafter_save
colllin 2014年

1
@colllin:当创建一个记录,created_at并且updated_at将在平等的after_save回调。在所有其他情况下,它们在after_save回调中将不相等。
bschaeffer 2014年

22

保存后,after_create只有在记录为新记录时才有一个回调。如果这是已更改并保存的现有记录,则还有一个回调供使用。该回调被称为在这两种情况下,无论是后还是被调用。after_updateafter_saveafter_createafter_update

after_create如果在保存新记录后需要进行某些操作,请使用此选项。

此处提供更多信息:http : //api.rubyonrails.org/classes/ActiveRecord/Callbacks.html


1
嘿,谢谢你的回答,这对我很有帮助。干杯,+ 10 :)
亚当·麦克阿瑟

1
实际上这可能不正确。如果您在创建中具有关联,则在创建关联之前将after_create称为,因此,如果您需要确保创建所有内容,则需要使用after_save
Niels Kristian

18

由于对象已经保存,因此您需要查看之前的更改。该ID仅应在创建后更改。

# true if this is a new record
@object.previous_changes[:id].any?

还有一个实例变量@new_record_before_save。您可以通过执行以下操作来访问它:

# true if this is a new record
@object.instance_variable_get(:@new_record_before_save)

两者都很丑陋,但是它们会让您知道对象是否是新创建的。希望有帮助!


我目前在after_save使用Rails 4 的回调中处于byebug中,但这些都不在识别该新记录。
MCB

我会说这@object.previous_changes[:id].any?很简单而优雅。记录更新后,它对我有用(我不从中调用它after_save)。
thekingoftruth '16

1
@object.id_previously_changed?有点不那么丑。
aNoble

after_save您想查看的是Rails 4上的@MCB changes[:id]而不是previous_changes[:id]。这是改变Rails5.1但是(见讨论github.com/rails/rails/pull/25337
gmcnaughton

previous_changes.key?(:id)以获得更好的理解。
塞巴斯蒂安·帕尔马

2

Rails 5.1+方式:

user = User.new
user.save!
user.saved_change_to_attribute?(:id) # => true

1

对于Rails 4(在4.2.11.1上选中),结果changesprevious_changes方法{}在创建对象内部时都是空散列after_save。因此attribute_changed?类似的方法id_changed?将无法按预期工作。

但是您可以利用此知识,并且-知道至少1个属性必须在changes更新中-检查是否changes为空。一旦确认它为空,就必须在对象创建过程中:

after_save do
  if changes.empty?
    # code appropriate for object creation goes here ...
  end
end

0

我很想具体一点,即使我知道:id正常情况下也不应改变,但是

(byebug) id_change.first.nil?
true

总是很便宜,而不是发现非常奇怪的意外错误。

如果我期望true来自不可信参数的标记的方式相同

def foo?(flag)
  flag == true
end

这样可以节省大量时间,避免坐在怪异的bug上。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.