Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

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

Open
wants to merge 2 commits into
base: master
Choose a base branch
from

Conversation

DouweM
Copy link
Collaborator

@DouweM DouweM commented Apr 28, 2014

No description provided.

@PaulUithol
Copy link
Owner

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
Copy link
Collaborator Author

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
    reverseRelation:
      type:           Backbone.HasOne
      key:            "listing"
      keySource: "listing_id"
      includeInJSON:  false
  ]

  @setup()

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
  subModelTypes: 
    text:  "ContentSectionText"
    video: "ContentSectionVideo"

  @setup()

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

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:

  • it calls ContentSectionVideo.initializeModelHierarchy, which sets ContentSectionVideo._superModel to ContentSectionBase; and
  • it calls Backbone.Relational.store.register( the_video ), which adds the_video to Backbone.Relational.store.getCollection( 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
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants