I ended up getting this to work the way I wanted to. The Many-to-Many relationship is maintained in the database but the relationships can only be accessed in my Backbone models in one direction. I decided that the Person models would be stand-alone and the PersonGroup models would have a collection of references to the Person models they are linked to.
The key points here that made everything work were to specify includeInJSON: "ID"
and to remove the reverseRelation
. This still lets you access the references to the models in JavaScript, but it correctly serializes and deserializes to JSON. The Person models simply don't have access to a navigation property to the Groups they are in, however they can exist in multiple groups just fine.
I simply assumed that using a list of IDs would mandate jumping through hoops to resolve references, but Backbone-relational seems to use the global Backbone model store to resolve references by ID without creating duplicate models. (eg. Three different groups can reference the same Person and only one model is ever created).
var Person = Backbone.RelationalModel.extend(
{
idAttribute: "ID",
});
var PersonGroup = Backbone.RelationalModel.extend(
{
idAttribute: "ID",
relations:
[
{
type: Backbone.HasMany,
key: "People",
relatedModel: "Person",
collectionType: "PersonCollection",
includeInJSON: "ID",
},
],
});
And here's the rest of the plumbing if it helps anyone.You can define the Backbone.Collections as follows and obtain them through separate API requests:
var PersonCollection = Backbone.Collection.extend(
{
model: Person,
url: "/api/person",
});
var PersonGroupCollection = Backbone.Collection.extend(
{
model: PersonGroup,
url: "/api/persongroup",
});
var PersonModels = new PersonCollection();
var GroupsModels = new PersonGroupCollection();
this.PersonModels.fetch();
this.GroupsModels.fetch();
this.People = kb.collectionObservable(
this.PersonModels,
{
factories:
{
"models": PersonViewModel,
},
}
);
this.PersonGroups = kb.collectionObservable(
this.GroupsModels,
{
factories:
{
"models": PersonGroupViewModel,
"models.People.models": PersonViewModel,
},
}
);
I included the Knockback.js specific collections for using Knockout.js bindings. Only one ViewModel is created per model so change tracking is propagated through the entire app.