Handling cascade / nullification of children on parent record deletion


#1

When a parent record is deleted, what should happen to the children records?

Usually, one of two things happen, either the children are destroyed, or the relationship is nullified. With ember data it is a bit trickier, because children can be destroyed / nullified either on the client, or (optimally) on the server.

Ember Data currently nullifies the relationship by updating all the children with parent null.

Say we have the following models:

App.Post = DS.Model.extend({
  title: DS.attr('string'),
  body: DS.attr('string'),
  comments: DS.hasMany('App.Comment');
});

App.Comment = DS.Model.extend({
  body: DS.attr('string'),
  post: DS.belongsTo('App.Post')
});

Assuming RestAdapter, with bulkCommit set to false.

Case 1: Parent destroyed on the client, children nullified on the client and committed (What ED currently does by default)

Given a post record with 5 comments.

Calling post.deleteRecord(); this.store.commit(); will perform 5 PUT requests (1 for each comment) that will set post_id to null. Then one DELETE request to delete the post record.

However there are actually 3 other possible ways that one might want to handle it, and currently, it’s very hard to escape ED’s default behavior described above.

Case 2: All children to be destroyed on the client and committed before the parent is destroyed (not Supported).

Comments have no reason to exist if their post does not exist. So I don’t want comments to be updated to have no post, but instead delete them when the post is deleted. This should result in the following:

Calling post.deleteRecord(); this.store.commit(); would perform 5 DELETE requests (1 for each comment) and one DELETE request to delete the post record.

This however has many disadvantages, such as the number of request required to do that, and the inability to rollback in case one of the DELETE requests fails.

Case 3: Parent destroyed on the client, and children cascaded on the server (not Supported).

The most optimal scenario would be to have the server cascade the children on the server.

This would result in one DELETE request on the server to delete the post, and ED would understand to unload the children if the request succeeds.

This is also 100% safe in terms of rollback on failure.

Case 4: Parent destroyed on the client, children relationship nullified on the server (not Supported).

This is similar to Case 3, only instead of the server destroying the children, it would nullify the relationship. ED would perform one DELETE request to the server to delete the post record, and if the delete succeeds, it would know to nullify the relationship of the children, while keeping them in a saved state so they don’t need to be re-committed.

If I were to suggest an API, I would add two things to ED:

  • In DS.Model definition, the ability to configure the nature of the relationship w.r.t destruction (destroy vs nullify).

    Example:

  App.Post = DS.Model.extend({
    title: DS.attr('string'),
    comments: DS.hasMany('App.Comment', { dependent: 'destroy' })
  });

This will tell ED whether children should be destroyed, or the relationship nullified. This however does not tell ED whether this should be done on the client and then committed, or will be done by the server, as this is adapter specific, which brings us to step 2.

  • In DS.Adapter definition, tell ED whether to delete/nullify the records on the client and then commit, or to expect the server to do that when DELETE parent is successful.

In order to temporarily accomplish case 3, with the help of @sly7_7 I came up with this gist. (maybe it would help someone trying to do the same thing). Another way that might work (which I haven’t tried) would be to use embedded: 'always', but that means parent and children need to always be embedded on find / create / update / delete. cc: @tom


#2

Is this still the current state of record deletion? I have quite a few places in an app I’m working on where I would prefer to use case 2 or 3. Or is there now a better way to cascade deletions?