Skip to content

Migrating from 0.13.3 to 0.14.0

Ricardo Graça edited this page Dec 9, 2018 · 4 revisions

previous() and previousAttributes()

Related PR: #1848

The previous() and previousAttributes() methods were changed so they are now more useful. Whenever a model is saved or destroyed the previous attributes are no longer reset back to the current attributes, meaning you can access the previous value of an attribute before the save or destroy operation.

However this is a breaking change, but since the old behavior wasn't very useful it's likely this won't cause issues for many people. In case you were using these methods previously and expect the previous attributes to equal the current attributes after a save() or destroy() call, just replace:

  • model.previousAttributes() -> model.attributes
  • model.previous('attributeName') -> model.get('attributeName')

Passing merge: false, remove: false to collection.set() and default behavior with duplicates

Related PR: #1846

The .set() method determines if a model is duplicate by its primary key. If there's already another model in the collection with the same primary key then the model being added is a duplicate.

Previously setting both the merge and remove options of collection.set() to false would just cause any duplicate models passed in to be ignored. Now the duplicate models will be present which is useful if you have models in your database with the same primary key but different columns otherwise.

You can't really get the old behavior back, because there was a bug in collection.set() that caused duplicates to be present by default but each one had the exact same column values as the last one being added, even if they had different values in the database. However, if you pass the merge: true option, the behavior should be very similar with the exception that there will be a single model with the values obtained by merging all duplicates.

Now, if you want to have duplicate models in your collection you have to pass merge: false, remove: false to collection.set().


Return value for empty hasOne relation

Related PR: #1839

This shouldn't cause many issues but now, when serializing a model with an empty hasOne relation, the result will be null for consistency with belongsTo.

If you were using:

if (model.singleRelation && model.singeRelation.id) { ... }

before for checking if a hasOne relation was set, this is no longer necessary, although it will continue working. That code can now be simplified as:

if (model.singleRelation) { ... }

If you were depending on the return value always being an object you'll have to adapt your code to handle the possibility of a null relation in this case.


New error messages on bad or insufficient morphTo data

Related PR: #1824

Previously there was an error that was only thrown when the defined morph type couldn't be found in any of the possible target types, but the message was a bit ambiguous because it only said that the target model couldn't be found, which isn't very helpful.

Now when trying to eager load a row that doesn't have the type attribute set there will be a new error thrown:

The target polymorphic model could not be determined because it's missing the type attribute

For example, the above error will be shown if you have a model in your database like this:

const Photo = Bookshelf.Model.extend({
  tableName: 'photos',
  imageable() {
    return this.morphTo('imageable', Site, [Author, 'profile_pic'])
  }
})

new Photo({ name: 'something', imageable_id: 4, imageable_type: null }).save()

And you try to fetch it with its related imageable:

Photo.fetchAll({withRelated: ['imageable']})

Additionally in the following situation of an unrecognized morph type:

new Photo({ name: 'something', imageable_id: 4, imageable_type: 'bad-type' }).save()

where bad-type is not one of the types that the imageable expects now the error message also clearly says so:

The target polymorphic type "bad-type" is not one of the defined target types

instead of the previous generic message:

The target polymorphic model was not found

In this case if you were doing checks based on the error message you'll have to update your logic to deal with these two cases.


Saving a model that hasn't changed won't update updated_at

Related PR: #1798

Previously when saving a model that had no changes it would still update the updated_at column in case the model had the option hasTimestamps: true. If you were using this to only update the updated_at attribute of a model without doing any further changes you can now achieve the same thing by changing:

// Before
myModel.save() // updated_at got updated to the current date

// Now
myModel.save({updated_at: new Date()})