Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Rails 4: Difference between validates presence on id or association

If I have a 'belongs_to' association in a model, I'd like to know the notional difference between validating an association:

  class Topping  < ActiveRecord::Base
    belongs_to :pancake
    validates  :pancake, presence: true
    ...

and validating the associated model id:

  class Topping  < ActiveRecord::Base
    belongs_to :pancake
    validates  :pancake_id, presence: true
    ...

Motivation:

Some code which assigned a topping to a pancake stopped working at some time in the past. Changing the validation from association to id 'fixed' the problem, but I'd like to know the deeper reason why.

(FYI, when stepping into the code the pancake was valid and in the database and the topping responded to both .pancake and .pancake_id appropriately. Both the push operator (pancake.toppings << topping) and manual assignment and save (topping.pancake = pancake; topping.save) failed with a pancake missing validation error.)

like image 521
A Fader Darkly Avatar asked Mar 12 '15 14:03

A Fader Darkly


1 Answers

Investigating further, I found that the 'presence' validator resolves to 'add_on_blank':

http://apidock.com/rails/ActiveModel/Errors/add_on_blank

def add_on_blank(attributes, options = {})
  Array(attributes).each do |attribute|
    value = @base.send(:read_attribute_for_validation, attribute)
    add(attribute, :blank, options) if value.blank?
  end
end

This does what it says: adds a validation error if the property in question is blank?

This means it's simply an existence check. So if I validate an id, that id has to exist. That means:

topping.pancake = Pancake.new
topping.valid?

would return false. However:

topping.pancake_id = -12
topping.valid?

would return true. On the other hand, if I validate the object the exact opposite would be true. Unless -12 is a valid index, in which case ActiveRecord would automatically load it from the database on receipt of the 'pancake' message.

Moving on to my issue, further investigation showed that blank? delegates to empty?, and indeed someone had defined empty? on the pancake, returning true if there are no toppings.

Culprit found, and something about Rails learned.

like image 157
A Fader Darkly Avatar answered Sep 30 '22 18:09

A Fader Darkly