保存主对象前会触发Rails HABTM after_add回调 [英] Rails HABTM after_add callback fires before saving primary object

查看:91
本文介绍了保存主对象前会触发Rails HABTM after_add回调的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有两个相互之间具有HABTM关系的ActiveRecord模型. 当我通过允许通过选中复选框添加区域的表单添加AccessUnit时,我收到一个例外,即AccessUnitUpdaterJob无法入队,因为传递的访问单元无法序列化(由于缺少标识符,因此) .手动在主要对象上调用save时,此问题已解决,但当然这是一种解决方法,而不是适当的解决方法.

I have two ActiveRecord models having a HABTM relationship with eachother. When I add an AccessUnit through a form that allows zones to be added by checking checkboxes I get an exception that the AccessUnitUpdaterJob can't be enqueued because the access unit passed can't be serialized (due to the fact that the identifier is missing). When manually calling save on the primary object, the issue is resolved but of course this is a workaround and not a proper fix.

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

TLDR; it seems the after_add callback is triggered before the main object is saved. I'm actually unsure if this is a bug in Rails or expected behavior. I'm using Rails 5.

我遇到的确切错误是:

ActiveJob::SerializationError in AccessUnitsController#create

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

下面是一些代码,因此您可以查看问题的上下文:

Here's some code so you can see the context of the issue:

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

推荐答案

在我看来,这不是错误.一切都按预期进行.您可以先创建一个复杂的对象图,然后再保存该图.在此创建阶段,您可以将对象添加到关联中.这是您要触发此回调的时间点,因为它说的是after_add而不是after_save.

In my point of view it is not a bug. Every thing works as expected . You can create a complex graph of objects before you save this graph. During this creation phase, you can add objects to an association. This is the point in time where you want fire this callback, because it says after_add and not 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回调更有意义:

Maybe with a before_add callback it makes more sense:

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

您可以在《 The Rails 4 Way》一书中读到一些有关这些回调的信息.

You can read a little bit about these callback within the book "The Rails 4 Way"

在您的情况下,您必须重新考虑您的逻辑.也许您可以使用after_save回调. 我的2美分:您考虑从回调切换到服务对象. 回调并非没有代价.它们并不总是易于调试和测试的.

In your case you have to rethink your logic. Maybe you can use an after_savecallback. My 2 cents: You consider switching from callbacks to service object. Callbacks don't come without a cost. They are not always easy to debug and test.

这篇关于保存主对象前会触发Rails HABTM after_add回调的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆