4

我有两个ActiveRecord模型与对方有HABTM关系。 当我通过一个允许通过检查复选框添加区域的窗体添加一个AccessUnit时,我得到一个异常,即AccessUnitUpdaterJob无法排入队列,因为传递的访问单元无法被序列化(由于缺少标识符)。手动调用主对象上的保存时,问题已解决,但当然这是一种解决方法,并非适当的修复方法。Rails HABTM after_add回调在保存主对象之前触发

TLDR;看起来after_add回调在主对象被保存之前被触发。我实际上不确定这是Rails中的错误还是预期的行为。我使用Rails 5.

我遇到确切的错误是:

ActiveJob::SerializationError in AccessUnitsController#create 

Unable to serialize AccessUnit without an id. (Maybe you forgot to call save?) 

下面是一些代码,所以你可以看到这个问题的背景:

class AccessUnit < ApplicationRecord 
    has_and_belongs_to_many :zones, after_add: :schedule_access_unit_update_after_zone_added_or_removed, after_remove: :schedule_access_unit_update_after_zone_added_or_removed 

    def schedule_access_unit_update_after_zone_added_or_removed(zone) 
    # self.save adding this line solves it but isn't a proper solution 
    puts "Access unit #{name} added or removed to zone #{zone.name}" 

    # error is thrown on this line 
    AccessUnitUpdaterJob.perform_later self 
    end 
end 

class Zone < ApplicationRecord 
    has_and_belongs_to_many :access_units 
end 
+0

至少看起来像http://api.rubyonrails.org/classes/ActiveRecord/Associations/ClassMethods.html应该在“关联扩展”部分中指定是否在保存之前触发after_add。 – kshikama

回答

2

在我的观点这不是一个错误。每件事情都按预期工作。在保存此图之前,您可以创建一个复杂的对象图。在创建阶段,您可以将对象添加到关联中。这是您想要触发此回调的时间点,因为它说after_add而不是after_save

例如:

@post.tags.build name: "ruby" # <= now you add the objects 
@post.tags.build name: "rails" # <= now you add the objects 
@post.save! # <= now it is to late, for this callback, you added already multiple objects 
before_add回调

也许更有意义:

class Post 
    has_many :tags, before_add: :check_state 

    def check_state(_tag) 
    if self.published? 
     raise CantAddFurthorTags, "Can't add tags to a published Post" 
    end 
    end 
end 

@post = Post.new 
@post.tags.build name: "ruby" 
@post.published = true 
@post.tags.build name: "rails" # <= you wan't to fire the before_add callback now, to know that you can't add this new object 
@post.save! # <= and not here, where you can't determine which object caused the error 

您可以阅读有关书籍中这些回调有点“钢轨4路”

在你的情况下,你必须重新思考你的逻辑。也许你可以使用after_save回调。 我的2美分:您考虑从回调切换到服务对象。 回调不是没有成本。他们并不总是很容易调试和测试。

相关问题