Make sure model hierarchy is determined when needed and actually nullify reverse relations when model is destroyed #456

wants to merge 2 commits into


None yet
2 participants

DouweM commented Apr 28, 2014

No description provided.


PaulUithol commented May 21, 2014

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.


DouweM commented May 21, 2014

Yo Paul!

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
  relations: [
    type:           Backbone.HasOne
    key:            "video_section"
    keyDestination: "video_section_id"
    relatedModel:   ContentSectionVideo
    includeInJSON:  ContentSectionVideo::idAttribute
      type:           Backbone.HasOne
      key:            "listing"
      keySource: "listing_id"
      includeInJSON:  false


Listing has a HasOne relation to ContentSectionVideo:

class ContentSectionVideo extends ContentSectionBase
  @setup _super

ContentSectionVideo is a subclass (subtype) of ContentSectionBase:

class ContentSectionBase extends Backbone.RelationalModel
    text:  "ContentSectionText"
    video: "ContentSectionVideo"


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( ContentSectionVideo ), 'relational:add relational:change:id', this.tryAddRelated ).

Because 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:

  • it calls ContentSectionVideo.initializeModelHierarchy, which sets ContentSectionVideo._superModel to ContentSectionBase; and
  • it calls the_video ), which adds the_video to ContentSectionVideo ), which now does return ContentSectionBase, which results in relational:add being triggered on that collection for ContentSectionBase.

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 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.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment