Under some special circumstances an after_initialize callback
may lead to a missing attribute exception when combined with a
uniqueness validation.
A not so uncommon scenario:
class MyModel < ActiveRecord::Base
after_initialize :init_token
validates :token, :uniqueness => true
def init_token
if self.token.blank?
self.token = rand(999999) # just for demo
end
end
end
MyModel has only a token column (String) in the database.
Now when doing the following:
m1 = MyModel.create
m2 = MyModel.new
m2.token = m1.token
m2.valid? # calls init_token again and throws the below exception.
missing attribute: token is because of the last
find_by_sql that does a
SELECT grids.id FROM grids WHERE (grids.token = BINARY 'mkp8ae2qbq') LIMIT 1
where token is not selected.
I am not sure if it is that good at all that a
valid? call ends up in a instantiation of a new record
and calling the initialize callbacks.
Could it not just check the row without a new instantiation?
ActiveModel::MissingAttributeError: missing attribute: token
from /home/zeus/projects/myproject/app/models/my_model.rb:9:in `init_token'
from /home/zeus/.rvm/gems/ruby-1.8.7-p302@rails/gems/activesupport-3.0.3/lib/active_support/callbacks.rb:415:in `_run_initialize_callbacks'
from /home/zeus/.rvm/gems/ruby-1.8.7-p302@rails/gems/activerecord-3.0.3/lib/active_record/base.rb:1453:in `init_with'
from /home/zeus/.rvm/gems/ruby-1.8.7-p302@rails/gems/activerecord-3.0.3/lib/active_record/base.rb:909:in `instantiate'
from /home/zeus/.rvm/gems/ruby-1.8.7-p302@rails/gems/activerecord-3.0.3/lib/active_record/base.rb:467:in `find_by_sql'
from /home/zeus/.rvm/gems/ruby-1.8.7-p302@rails/gems/activerecord-3.0.3/lib/active_record/base.rb:467:in `collect!'
from /home/zeus/.rvm/gems/ruby-1.8.7-p302@rails/gems/activerecord-3.0.3/lib/active_record/base.rb:467:in `find_by_sql'
from /home/zeus/.rvm/gems/ruby-1.8.7-p302@rails/gems/activerecord-3.0.3/lib/active_record/relation.rb:64:in `to_a'
from /home/zeus/.rvm/gems/ruby-1.8.7-p302@rails/gems/activerecord-3.0.3/lib/active_record/relation/finder_methods.rb:333:in `find_first'
from /home/zeus/.rvm/gems/ruby-1.8.7-p302@rails/gems/activerecord-3.0.3/lib/active_record/relation/finder_methods.rb:122:in `first'
from /home/zeus/.rvm/gems/ruby-1.8.7-p302@rails/gems/activerecord-3.0.3/lib/active_record/relation/finder_methods.rb:180:in `exists?'
from /home/zeus/.rvm/gems/ruby-1.8.7-p302@rails/gems/activerecord-3.0.3/lib/active_record/validations/uniqueness.rb:39:in `validate_each'
from /home/zeus/.rvm/gems/ruby-1.8.7-p302@rails/gems/activemodel-3.0.3/lib/active_model/validator.rb:154:in `validate'
from /home/zeus/.rvm/gems/ruby-1.8.7-p302@rails/gems/activemodel-3.0.3/lib/active_model/validator.rb:151:in `each'
from /home/zeus/.rvm/gems/ruby-1.8.7-p302@rails/gems/activemodel-3.0.3/lib/active_model/validator.rb:151:in `validate'
from /home/zeus/.rvm/gems/ruby-1.8.7-p302@rails/gems/activesupport-3.0.3/lib/active_support/callbacks.rb:314:in `send'
from /home/zeus/.rvm/gems/ruby-1.8.7-p302@rails/gems/activesupport-3.0.3/lib/active_support/callbacks.rb:314:in `_callback_before_1265'
from /home/zeus/.rvm/gems/ruby-1.8.7-p302@rails/gems/activesupport-3.0.3/lib/active_support/callbacks.rb:414:in `_run_validate_callbacks'
from /home/zeus/.rvm/gems/ruby-1.8.7-p302@rails/gems/activemodel-3.0.3/lib/active_model/validations.rb:212:in `run_validations!'
from /home/zeus/.rvm/gems/ruby-1.8.7-p302@rails/gems/activemodel-3.0.3/lib/active_model/validations/callbacks.rb:67:in `run_validations!'
from /home/zeus/.rvm/gems/ruby-1.8.7-p302@rails/gems/activesupport-3.0.3/lib/active_support/callbacks.rb:413:in `_run_validation_callbacks'
from /home/zeus/.rvm/gems/ruby-1.8.7-p302@rails/gems/activemodel-3.0.3/lib/active_model/validations/callbacks.rb:67:in `run_validations!'
from /home/zeus/.rvm/gems/ruby-1.8.7-p302@rails/gems/activemodel-3.0.3/lib/active_model/validations.rb:179:in `valid?'
from /home/zeus/.rvm/gems/ruby-1.8.7-p302@rails/gems/activerecord-3.0.3/lib/active_record/validations.rb:55:in `valid?'
from (irb):103