No description provided.
Hi Douwe! Could you describe for which scenarios this actually fixes the model hierarchy? I'm a bit worried about performance here. Calling initializeModelHierarchy from addReverseRelation shouldn't hurt too much, but from getCollection... that's called from all over the place.
Pfft, I should really have written down the exact circumstances when I ran into the issue, but I was able to retrace most of my my steps.
We have a Listing class defined like so:
class Listing extends Backbone.RelationalModel
Listing has a HasOne relation to ContentSectionVideo:
class ContentSectionVideo extends ContentSectionBase
ContentSectionVideo is a subclass (subtype) of ContentSectionBase:
class ContentSectionBase extends Backbone.RelationalModel
When we instantiate a new Listing model the_listing with id: 123, its #set method is called and because it's a new listing, this calls #initializeRelations, which ends up calling new Backbone.HasOne( listing, the_video_section_relation ). The Backbone.Relational constructor needs to know when new ContentSectionVideo objects are born so it can connect them to the listing when the listing_id field matches the listing's ID. It does this by calling this.listenTo( Backbone.Relational.store.getCollection( ContentSectionVideo ), 'relational:add relational:change:id', this.tryAddRelated ).
new Backbone.HasOne( listing, the_video_section_relation )
this.listenTo( Backbone.Relational.store.getCollection( ContentSectionVideo ), 'relational:add relational:change:id', this.tryAddRelated )
Because Backbone.Relational.store.getCollection is setup to return the collection for a model's root supermodel if we're dealing with a hierarchy, everything looks fine and dandy, except for the fact that at this point no ContentSectionVideo has been instantiated yet, so its .initializeModelHierarchy was never called, ContentSectionVideo._superModel is null rather than ContentSectionBase, and #getCollection returns a new collection for ContentSectionVideo rather than ContentSectionBase!
When, later on, a ContentSectionVideo is instantiated with listing_id: 123, its #set method is called, which does two things:
Backbone.Relational.store.register( the_video )
Backbone.Relational.store.getCollection( ContentSectionVideo )
the_listing.get( "video_section" ) should now equal the_video, but alas, the listing was never made aware of a new ContentSectionBase being born because it was listening to the wrong collection, and the_listing.get( "video_section" ) is still null.
the_listing.get( "video_section" )
The point being that getCollection is another possible entry point where _superModel needs to have been set.
When I had figured this out, I started looking for all code that uses _superModel and _subModels, and I found this was the case in addReverseRelation or more specifically _addRelation as well.
I came across the bug in #destroy at the same time, which has to do with the fact that this.getReverseRelations uses this.related, which had actually just been set to null, resulting in the reverse relations not being nullified.
And now my hands hurt from all the typing :) If you can think of a more performant implementation, go for it, but I hope you see the problem now.
#setup needs to be called on subclasses of models as well.
Make sure model hierarchy is determined when needed and actually null…
…ify reverse relations when model is destroyed.