-
Type: Bug
-
Resolution: Fixed
-
Priority: Critical - P2
-
Affects Version/s: None
-
Component/s: Associations
-
None
-
Minor Change
The logic to raise MissingAttributeError added in https://jira.mongodb.org/browse/MONGOID-5034 for associations that were not projected appears to be triggered when accessing associations that are not set (e.g. because the host object is a brand new, empty object). The issue here is that the host object was not obtained from the database using a query that employed projection, therefore the projection checks shouldn't be performed on it and the fact that an association is missing shouldn't cause an exception - in this case Mongoid should in fact return nil for the association.
Sample code reproducing this is in https://github.com/p-mongo/tests/blob/master/ma/lib/tasks/test_5306.rake:
class Company include Mongoid::Document has_many :users has_many :shops end class Shop include Mongoid::Document embeds_one :address belongs_to :company after_initialize :build_address1 def build_address1 self.address ||= Address.new end end class Address include Mongoid::Document embedded_in :shop end class User include Mongoid::Document belongs_to :company validate :break_mongoid def break_mongoid company.shop_ids end end company = Company.create! shop = Shop.create!(company: company) user = User.new user.company = company user.save!
Result:
ActiveModel::MissingAttributeError: Missing attribute: 'address' raise ActiveModel::MissingAttributeError, "Missing attribute: '#{field_name}'" ^^^^^ /home/w/apps/mongoid/lib/mongoid/association/accessors.rb:118:in `get_relation' /home/w/apps/mongoid/lib/mongoid/association/accessors.rb:300:in `block (2 levels) in define_getter!' /home/w/apps/tests/ma/lib/tasks/test_5306.rake:19:in `build_address1' /home/w/apps/mongoid/lib/mongoid/interceptable.rb:128:in `run_callbacks' /home/w/apps/mongoid/lib/mongoid/document.rb:321:in `instantiate_document' /home/w/apps/mongoid/lib/mongoid/factory.rb:109:in `execute_from_db' /home/w/apps/mongoid/lib/mongoid/factory.rb:82:in `from_db' /home/w/apps/mongoid/lib/mongoid/contextual/mongo.rb:683:in `yield_document' /home/w/apps/mongoid/lib/mongoid/contextual/mongo.rb:154:in `block in each' /home/w/apps/mongoid/lib/mongoid/contextual/mongo.rb:153:in `each' /home/w/apps/mongoid/lib/mongoid/contextual/mongo.rb:331:in `map' /home/w/apps/mongoid/lib/mongoid/contextual/mongo.rb:331:in `map' /home/w/apps/mongoid/lib/mongoid/association/accessors.rb:322:in `block (2 levels) in define_ids_getter!' /home/w/apps/mongoid/lib/mongoid/association/proxy.rb:124:in `method_missing' /home/w/apps/tests/ma/lib/tasks/test_5306.rake:36:in `break_mongoid' /home/w/apps/mongoid/lib/mongoid/interceptable.rb:128:in `run_callbacks' /home/w/apps/mongoid/lib/mongoid/interceptable.rb:128:in `run_callbacks' /home/w/apps/mongoid/lib/mongoid/validatable.rb:88:in `valid?' /home/w/apps/mongoid/lib/mongoid/persistable/creatable.rb:104:in `prepare_insert' /home/w/apps/mongoid/lib/mongoid/persistable/creatable.rb:20:in `insert' /home/w/apps/mongoid/lib/mongoid/persistable/savable.rb:20:in `save' /home/w/apps/mongoid/lib/mongoid/persistable/savable.rb:39:in `save!' /home/w/apps/tests/ma/lib/tasks/test_5306.rake:48:in `block in <top (required)>' Tasks: TOP => test_5306 (See full trace by running task with --trace)
-------
Original report:
This code reproduces a bug which I believe was introduced as a result of MONGOID-5034. I've extracted this from my app test suite. It is not occurring on the v7.4.0 tag.
I'm seeing a number of related failures when running my suite against Mongoid master, so hopefully we can find a fundamental fix. (This one alone took ~3 hours to extract and reduce down to a reproducible test!)
class Company include Mongoid::Document has_many :users has_many :shops end class Shop include Mongoid::Document embeds_one :address belongs_to :company after_initialize :build_address def build_address self.address ||= Address.new end end class Address include Mongoid::Document embedded_in :shop end class User include Mongoid::Document belongs_to :company, validate: false, autosave: false validate :break_mongoid def break_mongoid company.shop_ids end end company = Company.new company.save! shop = Shop.new shop.company = company shop.save! user = User.new user.company = company user.save!
As a side note, it is very curious to me why Company.shop_ids would need to care anything about the embedded models of Shop (i.e. address.) One would think it would just run a pluck:
Shop.where(company_id: company._id).pluck(:_id)
Is it loading the full Shop models from the database?
- is caused by
-
MONGOID-5034 When excluded from query, embeds many relations are returned as empty array instead of raising MissingAttributeError
- Closed