Search code examples
ruby-on-rails

`belongs_to` and `before_create` order in Rails


Given a scenario where a book belongs to an author:

class Book < ApplicationRecord
  belongs_to :author
end

Now let's add a before_create :ensure_something to the book model. The ensure_something method needs to verify something, but we need to access the author for this verification. Inside the ensure_something method, can we be sure that the variable author exists, i.e. is not nil?


I've checked the belongs_to docs and the callback order (see also here). However, I don't see how the assocation belongs_to and the callbacks relate to each other in terms of ordering.

My expectation is that once we are in ensure_something the variable author is guaranteed to exist, but I'm not 100% sure based on the docs and would like to check this.

I know that we can pass optional to belongs_to and it states:

When set to true, the association will not have its presence validated.

As optional is by default set to false, I read this as "the association will have its presence validated by default". The question is if this actually happens before the before_create callback is called.


Solution

  • The validation added by belongs_to is just a plain old presence validation and behaves just like any other validation.

    if required
      if ActiveRecord.belongs_to_required_validates_foreign_key
        model.validates_presence_of reflection.name, message: :required
    

    The order of callbacks for creating a record is:

    • before_validation
    • after_validation
    • before_save
    • around_save
    • before_create
    • around_create
    • after_create
    • after_save
    • after_commit

    As you can see the validations happen at the very top of the chain.

    My expectation is that once we are in ensure_something the variable author is guaranteed to exist, but I'm not 100% sure based on the docs and would like to check this.

    author is not a variable. It's a method which is generated by the belongs_to macro.

    The validation should ensure that the subsequent callback doesn't fire if the validations fail but there are ways to suppress or bypass validations. It is also possible (but very unlikely) that a race condition could occur where the associated record is deleted after the validation is performed but before the record is saved. If author is a new record the validation could pass even if the author is not successfully saved later.

    So in other words the validation gives you a reasonable degree of certainty but the only thing that's actually certain in life is death and taxes.