不要怂,就是干,撸起袖子干!

Commit 194ff2c7 by Felix Becker Committed by Jan Aagaard Meier

ES6 refactor: Model (#6039)

* ES6 refactor of model.js

* ES6 refactor of attribute.js

classes, export default
1 parent 6177a95d
Showing with 545 additions and 655 deletions
'use strict';
var Utils = require('./utils')
, BelongsTo = require('./associations/belongs-to')
, BelongsToMany = require('./associations/belongs-to-many')
, InstanceValidator = require('./instance-validator')
, QueryTypes = require('./query-types')
, sequelizeErrors = require('./errors')
, Dottie = require('dottie')
, Promise = require('./promise')
, _ = require('lodash')
, Association = require('./associations/base')
, HasMany = require('./associations/has-many')
, DataTypes = require('./data-types')
, Hooks = require('./hooks')
, associationsMixin = require('./associations/mixin')
, defaultsOptions = { raw: true };
const Utils = require('./utils');
const BelongsTo = require('./associations/belongs-to');
const BelongsToMany = require('./associations/belongs-to-many');
const InstanceValidator = require('./instance-validator');
const QueryTypes = require('./query-types');
const sequelizeErrors = require('./errors');
const Dottie = require('dottie');
const Promise = require('./promise');
const _ = require('lodash');
const Association = require('./associations/base');
const HasMany = require('./associations/has-many');
const DataTypes = require('./data-types');
const Hooks = require('./hooks');
const associationsMixin = require('./associations/mixin');
const defaultsOptions = { raw: true };
/**
* A Model represents a table in the database. Instances of this class represent a database row.
......@@ -58,9 +58,9 @@ class Model {
// This should be handled before handling where conditions because of logic with returns
// otherwise this code will never run on includes of a already conditionable where
if (options.include) {
options.include.forEach(function(include) {
for (const include of options.include) {
this._paranoidClause(include.model, include);
}.bind(this));
}
}
if (!model.options.timestamps || !model.options.paranoid || options.paranoid === false) {
......@@ -68,10 +68,10 @@ class Model {
return options;
}
var deletedAtCol = model._timestampAttributes.deletedAt
, deletedAtAttribute = model.rawAttributes[deletedAtCol]
, deletedAtObject = {}
, deletedAtDefaultValue = deletedAtAttribute.hasOwnProperty('defaultValue') ? deletedAtAttribute.defaultValue : null;
const deletedAtCol = model._timestampAttributes.deletedAt;
const deletedAtAttribute = model.rawAttributes[deletedAtCol];
const deletedAtObject = {};
let deletedAtDefaultValue = deletedAtAttribute.hasOwnProperty('defaultValue') ? deletedAtAttribute.defaultValue : null;
deletedAtDefaultValue = deletedAtDefaultValue || { $or: { $gte: model.sequelize.literal('CURRENT_TIMESTAMP'), $eq: null } };
......@@ -87,14 +87,12 @@ class Model {
}
static _addOptionalClassMethods() {
var self = this;
Utils._.each(this.options.classMethods || {}, function(fct, name) { self[name] = fct; });
Utils._.each(this.options.classMethods || {}, (fct, name) => { this[name] = fct; });
}
static _addDefaultAttributes() {
var self = this
, tail = {}
, head = {};
const tail = {};
let head = {};
// Add id if no primary key was manually added to definition
// Can't use this.primaryKeys here, since this function is called before PKs are identified
......@@ -136,40 +134,40 @@ class Model {
};
}
var existingAttributes = Utils._.clone(self.rawAttributes);
self.rawAttributes = {};
const existingAttributes = Utils._.clone(this.rawAttributes);
this.rawAttributes = {};
Utils._.each(head, function(value, attr) {
self.rawAttributes[attr] = value;
Utils._.each(head, (value, attr) => {
this.rawAttributes[attr] = value;
});
Utils._.each(existingAttributes, function(value, attr) {
self.rawAttributes[attr] = value;
Utils._.each(existingAttributes, (value, attr) => {
this.rawAttributes[attr] = value;
});
Utils._.each(tail, function(value, attr) {
if (Utils._.isUndefined(self.rawAttributes[attr])) {
self.rawAttributes[attr] = value;
Utils._.each(tail, (value, attr) => {
if (Utils._.isUndefined(this.rawAttributes[attr])) {
this.rawAttributes[attr] = value;
}
});
if (!Object.keys(this.primaryKeys).length) {
self.primaryKeys.id = self.rawAttributes.id;
this.primaryKeys.id = this.rawAttributes.id;
}
}
static _findAutoIncrementField() {
var fields = this.QueryGenerator.findAutoIncrementField(this);
const fields = this.QueryGenerator.findAutoIncrementField(this);
this.autoIncrementField = null;
fields.forEach(function(field) {
for (const field of fields) {
if (this.autoIncrementField) {
throw new Error('Invalid Instance definition. Only one autoincrement field allowed.');
} else {
this.autoIncrementField = field;
}
}.bind(this));
}
}
static _conformOptions(options, self) {
......@@ -189,15 +187,11 @@ class Model {
}
// convert all included elements to { model: Model } form
options.include = options.include.map(function(include) {
include = this._conformInclude(include, self);
return include;
}.bind(this));
options.include = options.include.map(include => this._conformInclude(include, self));
}
static _conformInclude(include, self) {
var model;
let model;
if (include._pseudo) return include;
......@@ -208,7 +202,7 @@ class Model {
model = include.target;
}
include = { model: model, association: include, as: include.as };
include = { model, association: include, as: include.as };
} else if (include.prototype && include.prototype instanceof Model) {
include = { model: include };
} else if (_.isPlainObject(include)) {
......@@ -239,7 +233,7 @@ class Model {
static _expandIncludeAllElement(includes, include) {
// check 'all' attribute provided is valid
var all = include.all;
let all = include.all;
delete include.all;
if (all !== true) {
......@@ -247,7 +241,7 @@ class Model {
all = [all];
}
var validTypes = {
const validTypes = {
BelongsTo: true,
HasOne: true,
HasMany: true,
......@@ -256,14 +250,14 @@ class Model {
Many: ['HasMany']
};
for (var i = 0; i < all.length; i++) {
var type = all[i];
for (let i = 0; i < all.length; i++) {
const type = all[i];
if (type === 'All') {
all = true;
break;
}
var types = validTypes[type];
const types = validTypes[type];
if (!types) {
throw new Error('include all \'' + type + '\' is not valid - must be BelongsTo, HasOne, HasMany, One, Has, Many or All');
}
......@@ -272,7 +266,7 @@ class Model {
// replace type placeholder e.g. 'One' with it's constituent types e.g. 'HasOne', 'BelongsTo'
all.splice(i, 1);
i--;
for (var j = 0; j < types.length; j++) {
for (let j = 0; j < types.length; j++) {
if (all.indexOf(types[j]) === -1) {
all.unshift(types[j]);
i++;
......@@ -283,7 +277,7 @@ class Model {
}
// add all associations of types specified to includes
var nested = include.nested;
const nested = include.nested;
if (nested) {
delete include.nested;
......@@ -294,18 +288,18 @@ class Model {
}
}
var used = [];
const used = [];
(function addAllIncludes(parent, includes) {
Utils._.forEach(parent.associations, function(association) {
Utils._.forEach(parent.associations, association => {
if (all !== true && all.indexOf(association.associationType) === -1) {
return;
}
// check if model already included, and skip if so
var model = association.target;
var as = association.options.as;
const model = association.target;
const as = association.options.as;
var predicate = {model: model};
const predicate = {model};
if (as) {
// We only add 'as' to the predicate if it actually exists
predicate.as = as;
......@@ -322,7 +316,7 @@ class Model {
used.push(parent);
// include this model
var thisInclude = Utils.cloneDeep(include);
const thisInclude = Utils.cloneDeep(include);
thisInclude.model = model;
if (as) {
thisInclude.as = as;
......@@ -355,7 +349,7 @@ class Model {
options.topLimit = options.limit;
}
options.include = options.include.map(function (include) {
options.include = options.include.map(include => {
include = this._conformInclude(include);
include.parent = options;
......@@ -373,9 +367,9 @@ class Model {
options.hasWhere = options.hasWhere || include.hasWhere || !!include.where;
return include;
}.bind(this));
});
options.include.forEach(function (include) {
for (const include of options.include) {
include.hasParentWhere = options.hasParentWhere || !!options.where;
include.hasParentRequired = options.hasParentRequired || !!options.required;
......@@ -420,9 +414,7 @@ class Model {
if (include.association.isSingleAssociation || include.hasSingleAssociation) {
options.hasSingleAssociation = true;
}
return include;
});
}
if (options.topModel === options.model && options.subQuery === undefined) {
options.subQuery = false;
......@@ -442,9 +434,9 @@ class Model {
include.originalAttributes = include.attributes.slice(0);
if (include.attributes.length) {
_.each(include.model.primaryKeys, function (attr, key) {
_.each(include.model.primaryKeys, (attr, key) => {
// Include the primary key if its not already take - take into account that the pk might be aliassed (due to a .field prop)
if (!_.some(include.attributes, function (includeAttr) {
if (!_.some(include.attributes, (includeAttr) => {
if (attr.field !== key) {
return Array.isArray(includeAttr) && includeAttr[0] === attr.field && includeAttr[1] === key;
}
......@@ -465,10 +457,10 @@ class Model {
}
// check if the current Model is actually associated with the passed Model - or it's a pseudo include
var association = include.association || this.getAssociation(include.model, include.as);
const association = include.association || this.getAssociation(include.model, include.as);
if (!association) {
var msg = include.model.name;
let msg = include.model.name;
if (include.as) {
msg += ' (' + include.as + ')';
......@@ -485,7 +477,7 @@ class Model {
// If through, we create a pseudo child include, to ease our parsing later on
if (include.association.through && Object(include.association.through.model) === include.association.through.model) {
if (!include.include) include.include = [];
var through = include.association.through;
const through = include.association.through;
include.through = Utils._.defaults(include.through || {}, {
model: through.model,
......@@ -507,7 +499,7 @@ class Model {
}
// include.model may be the main model, while the association target may be scoped - thus we need to look at association.target/source
var model;
let model;
if (include.model.scoped === true) {
// If the passed model is already scoped, keep that
model = include.model;
......@@ -558,13 +550,13 @@ class Model {
}
static _expandIncludeAll(options) {
var includes = options.include;
const includes = options.include;
if (!includes) {
return;
}
for (var index = 0; index < includes.length; index++) {
var include = includes[index];
for (let index = 0; index < includes.length; index++) {
const include = includes[index];
if (include.all) {
includes.splice(index, 1);
......@@ -574,13 +566,12 @@ class Model {
}
}
Utils._.forEach(includes, function(include) {
Utils._.forEach(includes, include => {
this._expandIncludeAll.call(include.model, include);
}.bind(this));
});
}
static init(attributes, options, modelManager) {
var self = this;
this.options = Utils._.extend({
timestamps: true,
......@@ -603,7 +594,7 @@ class Model {
this.associations = {};
this.modelManager = null;
this.options.hooks = _.mapValues(this.replaceHookAliases(this.options.hooks), function (hooks) {
this.options.hooks = _.mapValues(this.replaceHookAliases(this.options.hooks), hooks => {
if (!Array.isArray(hooks)) hooks = [hooks];
return hooks;
});
......@@ -621,7 +612,7 @@ class Model {
this.$schemaDelimiter = this.options.schemaDelimiter;
// error check options
_.each(options.validate, function(validator, validatorType) {
_.each(options.validate, (validator, validatorType) => {
if (_.includes(Utils._.keys(attributes), validatorType)) {
throw new Error('A model validator function must not have the same name as a field. Model: ' + this.name + ', field/validation name: ' + validatorType);
}
......@@ -629,9 +620,10 @@ class Model {
if (!_.isFunction(validator)) {
throw new Error('Members of the validate option must be functions. Model: ' + this.name + ', error with validate member ' + validatorType);
}
}.bind(this));
});
this.attributes = this.rawAttributes = _.mapValues(attributes, (attribute, name) => {
attribute = this.sequelize.normalizeAttribute(attribute);
if (attribute.references && attribute.references.model && attribute.references.model.prototype instanceof Model) {
......@@ -667,13 +659,11 @@ class Model {
this._readOnlyAttributes = Utils._.values(this._timestampAttributes);
this._hasReadOnlyAttributes = this._readOnlyAttributes && this._readOnlyAttributes.length;
this._isReadOnlyAttribute = Utils._.memoize(function(key) {
return self._hasReadOnlyAttributes && self._readOnlyAttributes.indexOf(key) !== -1;
});
this._isReadOnlyAttribute = Utils._.memoize(key => this._hasReadOnlyAttributes && this._readOnlyAttributes.indexOf(key) !== -1);
if (this.options.instanceMethods) {
Utils._.each(this.options.instanceMethods, function(fct, name) {
self.prototype[name] = fct;
Utils._.each(this.options.instanceMethods, (fct, name) => {
this.prototype[name] = fct;
});
}
......@@ -688,11 +678,11 @@ class Model {
this._conformOptions(this.$scope, this);
}
_.each(this.options.scopes, function (scope) {
_.each(this.options.scopes, scope => {
if (_.isPlainObject(scope)) {
this._conformOptions(scope, this);
}
}.bind(this));
});
this.options.indexes = this.options.indexes.map(this.$conformIndex);
......@@ -713,18 +703,17 @@ class Model {
}
static refreshAttributes() {
var self = this
, attributeManipulation = {};
const attributeManipulation = {};
this.prototype._customGetters = {};
this.prototype._customSetters = {};
Utils._.each(['get', 'set'], function(type) {
var opt = type + 'terMethods'
, funcs = Utils._.clone(Utils._.isObject(self.options[opt]) ? self.options[opt] : {})
, _custom = type === 'get' ? self.prototype._customGetters : self.prototype._customSetters;
Utils._.each(['get', 'set'], type => {
const opt = type + 'terMethods';
const funcs = Utils._.clone(Utils._.isObject(this.options[opt]) ? this.options[opt] : {});
const _custom = type === 'get' ? this.prototype._customGetters : this.prototype._customSetters;
Utils._.each(funcs, function(method, attribute) {
Utils._.each(funcs, (method, attribute) => {
_custom[attribute] = method;
if (type === 'get') {
......@@ -739,7 +728,7 @@ class Model {
}
});
Utils._.each(self.rawAttributes, function(options, attribute) {
Utils._.each(this.rawAttributes, (options, attribute) => {
if (options.hasOwnProperty(type)) {
_custom[attribute] = options[type];
}
......@@ -756,7 +745,7 @@ class Model {
}
});
Utils._.each(funcs, function(fct, name) {
Utils._.each(funcs, (fct, name) => {
if (!attributeManipulation[name]) {
attributeManipulation[name] = {
configurable: true
......@@ -779,12 +768,12 @@ class Model {
this.fieldRawAttributesMap = {};
this.primaryKeys = {};
self.options.uniqueKeys = {};
this.options.uniqueKeys = {};
_.each(this.rawAttributes, function(definition, name) {
definition.type = self.sequelize.normalizeDataType(definition.type);
_.each(this.rawAttributes, (definition, name) => {
definition.type = this.sequelize.normalizeDataType(definition.type);
definition.Model = self;
definition.Model = this;
definition.fieldName = name;
definition._modelAttribute = true;
......@@ -793,57 +782,57 @@ class Model {
}
if (definition.primaryKey === true) {
self.primaryKeys[name] = definition;
this.primaryKeys[name] = definition;
}
self.fieldRawAttributesMap[definition.field] = definition;
this.fieldRawAttributesMap[definition.field] = definition;
if (definition.type instanceof DataTypes.BOOLEAN) {
self._booleanAttributes.push(name);
this._booleanAttributes.push(name);
} else if (definition.type instanceof DataTypes.DATE || definition.type instanceof DataTypes.DATEONLY) {
self._dateAttributes.push(name);
this._dateAttributes.push(name);
} else if (definition.type instanceof DataTypes.HSTORE || DataTypes.ARRAY.is(definition.type, DataTypes.HSTORE)) {
self._hstoreAttributes.push(name);
this._hstoreAttributes.push(name);
} else if (definition.type instanceof DataTypes.RANGE || DataTypes.ARRAY.is(definition.type, DataTypes.RANGE)) {
self._rangeAttributes.push(name);
this._rangeAttributes.push(name);
} else if (definition.type instanceof DataTypes.JSON) {
self._jsonAttributes.push(name);
this._jsonAttributes.push(name);
} else if (definition.type instanceof DataTypes.VIRTUAL) {
self._virtualAttributes.push(name);
this._virtualAttributes.push(name);
} else if (definition.type instanceof DataTypes.GEOMETRY) {
self._geometryAttributes.push(name);
this._geometryAttributes.push(name);
}
if (definition.hasOwnProperty('defaultValue')) {
self._defaultValues[name] = Utils._.partial(Utils.toDefaultValue, definition.defaultValue);
this._defaultValues[name] = Utils._.partial(Utils.toDefaultValue, definition.defaultValue);
}
if (definition.hasOwnProperty('unique') && definition.unique !== false) {
var idxName;
let idxName;
if (typeof definition.unique === 'object' && definition.unique.hasOwnProperty('name')) {
idxName = definition.unique.name;
} else if (typeof definition.unique === 'string') {
idxName = definition.unique;
} else {
idxName = self.tableName + '_' + name + '_unique';
idxName = this.tableName + '_' + name + '_unique';
}
var idx = self.options.uniqueKeys[idxName] || { fields: [] };
let idx = this.options.uniqueKeys[idxName] || { fields: [] };
idx = idx || {fields: [], msg: null};
idx.fields.push(definition.field);
idx.msg = idx.msg || definition.unique.msg || null;
idx.name = idxName || false;
idx.column = name;
self.options.uniqueKeys[idxName] = idx;
this.options.uniqueKeys[idxName] = idx;
}
if (definition.hasOwnProperty('validate')) {
self.prototype.validators[name] = definition.validate;
this.prototype.validators[name] = definition.validate;
}
if (definition.index === true && definition.type instanceof DataTypes.JSONB) {
self.options.indexes.push({
this.options.indexes.push({
fields: [definition.field || name],
using: 'gin'
});
......@@ -852,7 +841,7 @@ class Model {
}
});
// Create a map of field to attribute names
this.fieldAttributeMap = Utils._.reduce(this.fieldRawAttributesMap, function(map, value, key) {
this.fieldAttributeMap = Utils._.reduce(this.fieldRawAttributesMap, (map, value, key) => {
if (key !== value.fieldName) {
map[key] = value.fieldName;
}
......@@ -862,39 +851,25 @@ class Model {
this.uniqueKeys = this.options.uniqueKeys;
this._hasBooleanAttributes = !!this._booleanAttributes.length;
this._isBooleanAttribute = Utils._.memoize(function(key) {
return self._booleanAttributes.indexOf(key) !== -1;
});
this._isBooleanAttribute = Utils._.memoize(key => this._booleanAttributes.indexOf(key) !== -1);
this._hasDateAttributes = !!this._dateAttributes.length;
this._isDateAttribute = Utils._.memoize(function(key) {
return self._dateAttributes.indexOf(key) !== -1;
});
this._isDateAttribute = Utils._.memoize(key => this._dateAttributes.indexOf(key) !== -1);
this._hasHstoreAttributes = !!this._hstoreAttributes.length;
this._isHstoreAttribute = Utils._.memoize(function(key) {
return self._hstoreAttributes.indexOf(key) !== -1;
});
this._isHstoreAttribute = Utils._.memoize(key => this._hstoreAttributes.indexOf(key) !== -1);
this._hasRangeAttributes = !!this._rangeAttributes.length;
this._isRangeAttribute = Utils._.memoize(function(key) {
return self._rangeAttributes.indexOf(key) !== -1;
});
this._isRangeAttribute = Utils._.memoize(key => this._rangeAttributes.indexOf(key) !== -1);
this._hasJsonAttributes = !!this._jsonAttributes.length;
this._isJsonAttribute = Utils._.memoize(function(key) {
return self._jsonAttributes.indexOf(key) !== -1;
});
this._isJsonAttribute = Utils._.memoize(key => this._jsonAttributes.indexOf(key) !== -1);
this._hasVirtualAttributes = !!this._virtualAttributes.length;
this._isVirtualAttribute = Utils._.memoize(function(key) {
return self._virtualAttributes.indexOf(key) !== -1;
});
this._isVirtualAttribute = Utils._.memoize(key => this._virtualAttributes.indexOf(key) !== -1);
this._hasGeometryAttributes = !!this._geometryAttributes.length;
this._isGeometryAttribute = Utils._.memoize(function(key) {
return self._geometryAttributes.indexOf(key) !== -1;
});
this._isGeometryAttribute = Utils._.memoize(key => this._geometryAttributes.indexOf(key) !== -1);
this._hasDefaultValues = !Utils._.isEmpty(this._defaultValues);
......@@ -904,22 +879,20 @@ class Model {
this.prototype._hasCustomGetters = Object.keys(this.prototype._customGetters).length;
this.prototype._hasCustomSetters = Object.keys(this.prototype._customSetters).length;
Object.keys(attributeManipulation).forEach((function(key){
for (const key of Object.keys(attributeManipulation)) {
if (Model.prototype.hasOwnProperty(key)) {
this.sequelize.log('Not overriding built-in method from model attribute: ' + key);
return;
continue;
}
Object.defineProperty(this.prototype, key, attributeManipulation[key]);
}).bind(this));
}
this.prototype.rawAttributes = this.rawAttributes;
this.prototype.attributes = Object.keys(this.prototype.rawAttributes);
this.prototype._isAttribute = Utils._.memoize(function(key) {
return self.prototype.attributes.indexOf(key) !== -1;
});
this.prototype._isAttribute = Utils._.memoize(key => this.prototype.attributes.indexOf(key) !== -1);
// Primary key convenience variables
// Primary key convenience constiables
this.primaryKeyAttributes = Object.keys(this.primaryKeys);
this.primaryKeyAttribute = this.primaryKeyAttributes[0];
if (this.primaryKeyAttribute) {
......@@ -929,9 +902,7 @@ class Model {
this.primaryKeyCount = this.primaryKeyAttributes.length;
this._hasPrimaryKeys = this.options.hasPrimaryKeys = this.hasPrimaryKeys = this.primaryKeyCount > 0;
this._isPrimaryKey = Utils._.memoize(function(key) {
return self.primaryKeyAttributes.indexOf(key) !== -1;
});
this._isPrimaryKey = Utils._.memoize(key => this.primaryKeyAttributes.indexOf(key) !== -1);
}
......@@ -953,42 +924,37 @@ class Model {
options = _.extend({}, this.options, options);
options.hooks = options.hooks === undefined ? true : !!options.hooks;
var self = this
, attributes = this.tableAttributes;
const attributes = this.tableAttributes;
return Promise.try(function () {
return Promise.try(() => {
if (options.hooks) {
return self.runHooks('beforeSync', options);
return this.runHooks('beforeSync', options);
}
}).then(function () {
}).then(() => {
if (options.force) {
return self.drop(options);
return this.drop(options);
}
}).then(function () {
return self.QueryInterface.createTable(self.getTableName(options), attributes, options, self);
}).then(function () {
return self.QueryInterface.showIndex(self.getTableName(options), options);
}).then(function (indexes) {
})
.then(() => this.QueryInterface.createTable(this.getTableName(options), attributes, options, this))
.then(() => this.QueryInterface.showIndex(this.getTableName(options), options))
.then(indexes => {
// Assign an auto-generated name to indexes which are not named by the user
self.options.indexes = self.QueryInterface.nameIndexes(self.options.indexes, self.tableName);
indexes = _.filter(self.options.indexes, function (item1) {
return !_.some(indexes, function (item2) {
return item1.name === item2.name;
});
});
return Promise.map(indexes, function (index) {
return self.QueryInterface.addIndex(
self.getTableName(options),
_.assign({logging: options.logging,
benchmark: options.benchmark,
transaction: options.transaction}, index),
self.tableName);
});
}).then(function () {
this.options.indexes = this.QueryInterface.nameIndexes(this.options.indexes, this.tableName);
indexes = _.filter(this.options.indexes, item1 => !_.some(indexes, item2 => item1.name === item2.name));
return Promise.map(indexes, index => this.QueryInterface.addIndex(
this.getTableName(options),
_.assign({
logging: options.logging,
benchmark: options.benchmark,
transaction: options.transaction
}, index),
this.tableName
));
}).then(() => {
if (options.hooks) {
return self.runHooks('afterSync', options);
return this.runHooks('afterSync', options);
}
}).return(this);
}
......@@ -1031,7 +997,7 @@ class Model {
*/
static schema(schema, options) { // testhint options:none
var clone = class extends this {};
const clone = class extends this {};
Object.defineProperty(clone, 'name', {value: this.name});
clone.$schema = schema;
......@@ -1100,7 +1066,7 @@ class Model {
/**
* Apply a scope created in `define` to the model. First let's look at how to create scopes:
* ```js
* var Model = sequelize.define('model', attributes, {
* const Model = sequelize.define('model', attributes, {
* defaultScope: {
* where: {
* username: 'dan'
......@@ -1144,10 +1110,9 @@ class Model {
* @return {Model} A reference to the model, with the scope(s) applied. Calling scope again on the returned model will clear the previous scope.
*/
static scope(option) {
var self = class extends this {}
, options
, scope
, scopeName;
const self = class extends this {};
let scope;
let scopeName;
Object.defineProperty(self, 'name', {value: this.name});
self.$scope = {};
......@@ -1157,8 +1122,8 @@ class Model {
return self;
}
options = _.flatten(arguments);
options.forEach(function(option) {
const options = _.flatten(arguments);
for (const option of options) {
scope = null;
scopeName = null;
......@@ -1190,7 +1155,7 @@ class Model {
}
if (!!scope) {
_.assignWith(self.$scope, scope, function scopeCustomizer(objectValue, sourceValue, key) {
_.assignWith(self.$scope, scope, (objectValue, sourceValue, key) => {
if (key === 'where') {
return Array.isArray(sourceValue) ? sourceValue : _.assign(objectValue || {}, sourceValue);
} else if ( (['attributes','include'].indexOf(key) >= 0) && Array.isArray(objectValue) && Array.isArray(sourceValue)) {
......@@ -1202,7 +1167,7 @@ class Model {
} else {
throw new Error('Invalid scope ' + scopeName + ' called.');
}
}.bind(this));
}
return self;
}
......@@ -1320,8 +1285,8 @@ class Model {
if (arguments.length > 1) {
throw new Error('Please note that find* was refactored and uses only one options object from now on.');
}
var tableNames = {}
, originalOptions;
const tableNames = {};
let originalOptions;
tableNames[this.getTableName(options)] = true;
options = Utils.cloneDeep(options);
......@@ -1331,20 +1296,20 @@ class Model {
//set rejectOnEmpty option from model config
options.rejectOnEmpty = options.rejectOnEmpty || this.options.rejectOnEmpty;
return Promise.bind(this).then(function() {
return Promise.try(() => {
this._conformOptions(options, this);
this.$injectScope(options);
if (options.hooks) {
return this.runHooks('beforeFind', options);
}
}).then(function() {
this._expandIncludeAll.call(this, options);
}).then(() => {
this._expandIncludeAll(options);
if (options.hooks) {
return this.runHooks('beforeFindAfterExpandIncludeAll', options);
}
}).then(function() {
}).then(() => {
if (options.include) {
options.hasJoin = true;
......@@ -1371,15 +1336,15 @@ class Model {
if (options.hooks) {
return this.runHooks('beforeFindAfterOptions', options);
}
}).then(function() {
}).then(() => {
originalOptions = Utils.cloneDeep(options);
options.tableNames = Object.keys(tableNames);
return this.QueryInterface.select(this, this.getTableName(options), options);
}).tap(function(results) {
}).tap(results => {
if (options.hooks) {
return this.runHooks('afterFind', results, options);
}
}).then(function (results) {
}).then(results => {
//rejectOnEmpty mode
if (_.isEmpty(results) && options.rejectOnEmpty) {
......@@ -1399,16 +1364,16 @@ class Model {
static $findSeparate(results, options) {
if (!options.include || options.raw || !results) return Promise.resolve(results);
var original = results;
const original = results;
if (options.plain) results = [results];
if (!results.length) return original;
return Promise.map(options.include, function (include) {
return Promise.map(options.include, include => {
if (!include.separate) {
return Model.$findSeparate(
results.reduce(function (memo, result) {
var associations = result.get(include.association.as);
results.reduce((memo, result) => {
let associations = result.get(include.association.as);
// Might be an empty belongsTo relation
if (!associations) return memo;
......@@ -1430,8 +1395,8 @@ class Model {
{},
_.omit(options, 'include', 'attributes', 'order', 'where', 'limit', 'plain'),
_.omit(include, 'parent', 'association', 'as')
)).then(function (map) {
results.forEach(function (result) {
)).then(map => {
for (const result of results) {
result.set(
include.association.as,
map[result.get(include.association.source.primaryKeyAttribute)],
......@@ -1439,7 +1404,7 @@ class Model {
raw: true
}
);
});
}
});
}).return(original);
}
......@@ -1493,7 +1458,7 @@ class Model {
options = Utils.cloneDeep(options);
if (options.limit === undefined) {
var pkVal = options.where && options.where[this.primaryKeyAttribute];
const pkVal = options.where && options.where[this.primaryKeyAttribute];
// Don't add limit if querying directly on the pk
if (!options.where || !(Utils.isPrimitive(pkVal) || Buffer.isBuffer(pkVal))) {
......@@ -1535,9 +1500,9 @@ class Model {
this._validateIncludedElements(options);
}
var attrOptions = this.rawAttributes[attribute]
, field = attrOptions && attrOptions.field || attribute
, aggregateColumn = this.sequelize.col(field);
const attrOptions = this.rawAttributes[attribute];
const field = attrOptions && attrOptions.field || attribute;
let aggregateColumn = this.sequelize.col(field);
if (options.distinct) {
aggregateColumn = this.sequelize.fn('DISTINCT', aggregateColumn);
......@@ -1583,16 +1548,16 @@ class Model {
options = Utils.cloneDeep(options);
_.defaults(options, { hooks: true });
var col = '*';
let col = '*';
return Promise.bind(this).then(function() {
return Promise.try(() => {
this._conformOptions(options, this);
this.$injectScope(options);
if (options.hooks) {
return this.runHooks('beforeCount', options);
}
}).then(function() {
}).then(() => {
if (options.include) {
col = this.name + '.' + this.primaryKeyField;
this._expandIncludeAll(options);
......@@ -1621,7 +1586,7 @@ class Model {
* where: ...,
* limit: 12,
* offset: 12
* }).then(function (result) {
* }).then(result => {
* ...
* })
* ```
......@@ -1651,14 +1616,13 @@ class Model {
throw new Error('The argument passed to findAndCount must be an options object, use findById if you wish to pass a single primary key value');
}
var self = this
// no limit, offset, order, attributes for the options given to count()
, countOptions = _.omit(_.clone(options), ['offset', 'limit', 'order', 'attributes']);
// no limit, offset, order, attributes for the options given to count()
const countOptions = _.omit(_.clone(options), ['offset', 'limit', 'order', 'attributes']);
this._conformOptions(countOptions, this);
if (countOptions.include) {
countOptions.include = _.cloneDeepWith(countOptions.include, function (element) {
countOptions.include = _.cloneDeepWith(countOptions.include, element => {
if (element instanceof Model) return element;
if (element instanceof Association) return element;
return undefined;
......@@ -1668,8 +1632,8 @@ class Model {
this._validateIncludedElements(countOptions);
var keepNeeded = function(includes) {
return includes.filter(function (include) {
const keepNeeded = includes => {
return includes.filter(include => {
if (include.include) include.include = keepNeeded(include.include);
return include.required || include.hasIncludeRequired;
......@@ -1683,19 +1647,17 @@ class Model {
}
}
return self.count(countOptions).then(function(count) {
return this.count(countOptions).then(count => {
if (count === 0) {
return {
count: count || 0,
rows: []
};
}
return self.findAll(options).then(function(results) {
return {
count: count || 0,
rows: (results && Array.isArray(results) ? results : [])
};
});
return this.findAll(options).then(results => ({
count: count || 0,
rows: (results && Array.isArray(results) ? results : [])
}));
});
}
......@@ -1760,9 +1722,7 @@ class Model {
}, options || {});
if (options.attributes) {
options.attributes = options.attributes.map(function(attribute) {
return Array.isArray(attribute) ? attribute[1] : attribute;
});
options.attributes = options.attributes.map(attribute => Array.isArray(attribute) ? attribute[1] : attribute);
}
if (!options.includeValidated) {
......@@ -1790,14 +1750,10 @@ class Model {
}
if (options.attributes) {
options.attributes = options.attributes.map(function(attribute) {
return Array.isArray(attribute) ? attribute[1] : attribute;
});
options.attributes = options.attributes.map(attribute => Array.isArray(attribute) ? attribute[1] : attribute);
}
return valueSets.map(function(values) {
return this.build(values, options);
}.bind(this));
return valueSets.map(values => this.build(values, options));
}
/**
......@@ -1854,17 +1810,16 @@ class Model {
);
}
var self = this
, values;
let values;
return self.find(options).then(function(instance) {
return this.find(options).then(instance => {
if (instance === null) {
values = Utils._.clone(options.defaults) || {};
if (Utils._.isPlainObject(options.where)) {
values = Utils._.defaults(values, options.where);
}
instance = self.build(values);
instance = this.build(values);
return Promise.resolve([instance, true]);
}
......@@ -1899,26 +1854,23 @@ class Model {
options = _.assign({}, options);
if (options.transaction === undefined && this.sequelize.constructor.cls) {
var t = this.sequelize.constructor.cls.get('transaction');
const t = this.sequelize.constructor.cls.get('transaction');
if (t) {
options.transaction = t;
}
}
var self = this
, internalTransaction = !options.transaction
, values
, transaction;
const internalTransaction = !options.transaction;
let values;
let transaction;
// Create a transaction or a savepoint, depending on whether a transaction was passed in
return self.sequelize.transaction(options).bind({}).then(function (t) {
return this.sequelize.transaction(options).then(t => {
transaction = t;
options.transaction = t;
return self.findOne(_.defaults({
transaction: transaction
}, options));
}).then(function(instance) {
return this.findOne(_.defaults({transaction}, options));
}).then(instance => {
if (instance !== null) {
return [instance, false];
}
......@@ -1930,16 +1882,16 @@ class Model {
options.exception = true;
return self.create(values, options).bind(this).then(function(instance) {
if (instance.get(self.primaryKeyAttribute, { raw: true }) === null) {
return this.create(values, options).then(instance => {
if (instance.get(this.primaryKeyAttribute, { raw: true }) === null) {
// If the query returned an empty result for the primary key, we know that this was actually a unique constraint violation
throw new self.sequelize.UniqueConstraintError();
throw new this.sequelize.UniqueConstraintError();
}
return [instance, true];
}).catch(self.sequelize.UniqueConstraintError, function (err) {
let whereFields = Object.keys(options.where).map(name => self.rawAttributes[name].field || name)
, defaultFields = options.defaults && Object.keys(options.defaults).map(name => self.rawAttributes[name].field || name);
}).catch(this.sequelize.UniqueConstraintError, err => {
const whereFields = Object.keys(options.where).map(name => this.rawAttributes[name].field || name);
const defaultFields = options.defaults && Object.keys(options.defaults).map(name => this.rawAttributes[name].field || name);
if (defaultFields) {
if (!_.intersection(Object.keys(err.fields), whereFields).length && _.intersection(Object.keys(err.fields), defaultFields).length) {
......@@ -1949,24 +1901,24 @@ class Model {
if (_.intersection(Object.keys(err.fields), whereFields).length) {
_.each(err.fields, (value, key) => {
let name = self.fieldRawAttributesMap[key].fieldName;
const name = this.fieldRawAttributesMap[key].fieldName;
if (value.toString() !== options.where[name].toString()) {
throw new Error(`${self.name}#findOrCreate: value used for ${name} was not equal for both the find and the create calls, '${options.where[name]}' vs '${value}'`);
throw new Error(`${this.name}#findOrCreate: value used for ${name} was not equal for both the find and the create calls, '${options.where[name]}' vs '${value}'`);
}
});
}
// Someone must have created a matching instance inside the same transaction since we last did a find. Let's find it!
return self.findOne(_.defaults({
return this.findOne(_.defaults({
transaction: internalTransaction ? null : transaction
}, options)).then(function(instance) {
}, options)).then(instance => {
// Sanity check, ideally we caught this at the defaultFeilds/err.fields check
// But if we didn't and instance is null, we will throw
if (instance === null) throw err;
return [instance, false];
});
});
}).finally(function () {
}).finally(() => {
if (internalTransaction && transaction) {
// If we created a transaction internally (and not just a savepoint), we should clean it up
return transaction.commit();
......@@ -1991,22 +1943,18 @@ class Model {
);
}
var values = Utils._.clone(options.defaults) || {};
let values = Utils._.clone(options.defaults) || {};
if (Utils._.isPlainObject(options.where)) {
values = _.defaults(values, options.where);
}
return this.findOne(options).bind(this).then(function (result) {
return this.findOne(options).then(result => {
if (result) return [result, false];
return this.create(values, options).bind(this).then(function (result) {
return [result, true];
}).catch(this.sequelize.UniqueConstraintError, function (err) {
return this.findOne(options).then(function (result) {
return [result, false];
});
});
return this.create(values, options)
.then(result => [result, true])
.catch(this.sequelize.UniqueConstraintError, err => this.findOne(options).then(result => [result, false]));
});
}
......@@ -2040,17 +1988,17 @@ class Model {
options.fields = Object.keys(this.attributes);
}
var createdAtAttr = this._timestampAttributes.createdAt
const createdAtAttr = this._timestampAttributes.createdAt
, updatedAtAttr = this._timestampAttributes.updatedAt
, hadPrimary = this.primaryKeyField in values || this.primaryKeyAttribute in values
, instance = this.build(values);
return instance.validate(options).bind(this).then(function () {
return instance.validate(options).then(() => {
// Map field names
var updatedDataValues = _.pick(instance.dataValues, Object.keys(instance._changed))
, insertValues = Utils.mapValueFieldNames(instance.dataValues, options.fields, this)
, updateValues = Utils.mapValueFieldNames(updatedDataValues, options.fields, this)
, now = Utils.now(this.sequelize.options.dialect);
const updatedDataValues = _.pick(instance.dataValues, Object.keys(instance._changed));
const insertValues = Utils.mapValueFieldNames(instance.dataValues, options.fields, this);
const updateValues = Utils.mapValueFieldNames(updatedDataValues, options.fields, this);
const now = Utils.now(this.sequelize.options.dialect);
// Attach createdAt
if (createdAtAttr && !updateValues[createdAtAttr]) {
......@@ -2110,7 +2058,7 @@ class Model {
options.fields = options.fields || Object.keys(this.tableAttributes);
var dialect = this.sequelize.options.dialect;
const dialect = this.sequelize.options.dialect;
if (options.ignoreDuplicates && ['postgres', 'mssql'].indexOf(dialect) !== -1) {
return Promise.reject(new Error(dialect + ' does not support the \'ignoreDuplicates\' option.'));
}
......@@ -2120,7 +2068,7 @@ class Model {
if (options.updateOnDuplicate) {
// By default, all attributes except 'createdAt' can be updated
var updatableFields = Utils._.pull(Object.keys(this.tableAttributes), 'createdAt');
let updatableFields = Utils._.pull(Object.keys(this.tableAttributes), 'createdAt');
if (Utils._.isArray(options.updateOnDuplicate) && !Utils._.isEmpty(options.updateOnDuplicate)) {
updatableFields = Utils._.intersection(updatableFields, options.updateOnDuplicate);
}
......@@ -2129,41 +2077,38 @@ class Model {
options.model = this;
var self = this
, createdAtAttr = this._timestampAttributes.createdAt
, updatedAtAttr = this._timestampAttributes.updatedAt
, now = Utils.now(self.modelManager.sequelize.options.dialect);
const createdAtAttr = this._timestampAttributes.createdAt;
const updatedAtAttr = this._timestampAttributes.updatedAt;
const now = Utils.now(this.modelManager.sequelize.options.dialect);
var instances = records.map(function(values) {
return self.build(values, {isNewRecord: true});
});
let instances = records.map(values => this.build(values, {isNewRecord: true}));
return Promise.try(function() {
return Promise.try(() => {
// Run before hook
if (options.hooks) {
return self.runHooks('beforeBulkCreate', instances, options);
return this.runHooks('beforeBulkCreate', instances, options);
}
}).then(function() {
}).then(() => {
// Validate
if (options.validate) {
var errors = new Promise.AggregateError()
const errors = new Promise.AggregateError()
, validateOptions = _.clone(options);
validateOptions.hooks = options.individualHooks;
return Promise.map(instances, function(instance) {
return instance.validate(validateOptions).catch(function (err) {
return Promise.map(instances, instance =>
instance.validate(validateOptions).catch(err => {
errors.push({record: instance, errors: err});
});
}).then(function() {
})
).then(() => {
delete options.skip;
if (errors.length) {
throw errors;
}
});
}
}).then(function() {
instances.forEach(function(instance) {
var values = Utils.mapValueFieldNames(instance.dataValues, options.fields, self);
}).then(() => {
for (const instance of instances) {
const values = Utils.mapValueFieldNames(instance.dataValues, options.fields, this);
// set createdAt/updatedAt attributes
if (createdAtAttr && !values[createdAtAttr]) {
......@@ -2174,12 +2119,12 @@ class Model {
}
instance.dataValues = values;
});
}
if (options.individualHooks) {
// Create each instance individually
return Promise.map(instances, function(instance) {
var individualOptions = Utils._.clone(options);
return Promise.map(instances, instance => {
const individualOptions = Utils._.clone(options);
delete individualOptions.fields;
delete individualOptions.individualHooks;
delete individualOptions.ignoreDuplicates;
......@@ -2187,43 +2132,41 @@ class Model {
individualOptions.hooks = true;
return instance.save(individualOptions);
}).then(function(_instances) {
}).then(_instances => {
instances = _instances;
});
} else {
// Create all in one query
// Recreate records from instances to represent any changes made in hooks or validation
records = instances.map(function(instance) {
return Utils._.omit(instance.dataValues, self._virtualAttributes);
records = instances.map(instance => {
return Utils._.omit(instance.dataValues, this._virtualAttributes);
});
// Map attributes for serial identification
var attributes = {};
for (var attr in self.tableAttributes) {
attributes[attr] = self.rawAttributes[attr];
if (self.rawAttributes[attr].field) {
attributes[self.rawAttributes[attr].field] = self.rawAttributes[attr];
const attributes = {};
for (const attr in this.tableAttributes) {
attributes[attr] = this.rawAttributes[attr];
if (this.rawAttributes[attr].field) {
attributes[this.rawAttributes[attr].field] = this.rawAttributes[attr];
}
}
return self.QueryInterface.bulkInsert(self.getTableName(options), records, options, attributes).then(function (results) {
return this.QueryInterface.bulkInsert(this.getTableName(options), records, options, attributes).then(results => {
if (Array.isArray(results)) {
results.forEach(function (result, i) {
instances[i].set(self.primaryKeyAttribute, result[self.rawAttributes[self.primaryKeyAttribute].field], {raw: true});
results.forEach((result, i) => {
instances[i].set(this.primaryKeyAttribute, result[this.rawAttributes[this.primaryKeyAttribute].field], {raw: true});
instances[i].isNewRecord = false;
});
}
return results;
});
}
}).then(function() {
}).then(() => {
// Run after hook
if (options.hooks) {
return self.runHooks('afterBulkCreate', instances, options);
return this.runHooks('afterBulkCreate', instances, options);
}
}).then(function() {
return instances;
});
}).then(() => instances);
}
/**
......@@ -2264,8 +2207,7 @@ class Model {
* @return {Promise<Integer>} The number of destroyed rows
*/
static destroy(options) {
var self = this
, instances;
let instances;
if (!options || !(options.where || options.truncate)) {
throw new Error('Missing where or truncate attribute in the options parameter of model.destroy.');
......@@ -2287,53 +2229,47 @@ class Model {
this.$injectScope(options);
Utils.mapOptionFieldNames(options, this);
options.model = self;
options.model = this;
return Promise.try(function() {
return Promise.try(() => {
// Run before hook
if (options.hooks) {
return self.runHooks('beforeBulkDestroy', options);
return this.runHooks('beforeBulkDestroy', options);
}
}).then(function() {
}).then(() => {
// Get daos and run beforeDestroy hook on each record individually
if (options.individualHooks) {
return self.findAll({where: options.where, transaction: options.transaction, logging: options.logging, benchmark: options.benchmark}).map(function(instance) {
return self.runHooks('beforeDestroy', instance, options).then(function() {
return instance;
return this.findAll({where: options.where, transaction: options.transaction, logging: options.logging, benchmark: options.benchmark})
.map(instance => this.runHooks('beforeDestroy', instance, options).then(() => instance))
.then(_instances => {
instances = _instances;
});
}).then(function(_instances) {
instances = _instances;
});
}
}).then(function() {
}).then(() => {
// Run delete query (or update if paranoid)
if (self._timestampAttributes.deletedAt && !options.force) {
var attrValueHash = {}
, deletedAtAttribute = self.rawAttributes[self._timestampAttributes.deletedAt]
, field = self.rawAttributes[self._timestampAttributes.deletedAt].field
if (this._timestampAttributes.deletedAt && !options.force) {
const attrValueHash = {}
, deletedAtAttribute = this.rawAttributes[this._timestampAttributes.deletedAt]
, field = this.rawAttributes[this._timestampAttributes.deletedAt].field
, where = {};
where[field] = deletedAtAttribute.hasOwnProperty('defaultValue') ? deletedAtAttribute.defaultValue : null;
attrValueHash[field] = Utils.now(self.modelManager.sequelize.options.dialect);
return self.QueryInterface.bulkUpdate(self.getTableName(options), attrValueHash, _.defaults(where, options.where), options, self.rawAttributes);
attrValueHash[field] = Utils.now(this.modelManager.sequelize.options.dialect);
return this.QueryInterface.bulkUpdate(this.getTableName(options), attrValueHash, _.defaults(where, options.where), options, this.rawAttributes);
} else {
return self.QueryInterface.bulkDelete(self.getTableName(options), options.where, options, self);
return this.QueryInterface.bulkDelete(this.getTableName(options), options.where, options, this);
}
}).tap(function() {
}).tap(() => {
// Run afterDestroy hook on each record individually
if (options.individualHooks) {
return Promise.map(instances, function(instance) {
return self.runHooks('afterDestroy', instance, options);
});
return Promise.map(instances, instance => this.runHooks('afterDestroy', instance, options));
}
}).tap(function() {
}).tap(() => {
// Run after hook
if (options.hooks) {
return self.runHooks('afterBulkDestroy', options);
return this.runHooks('afterBulkDestroy', options);
}
}).then(function(affectedRows) {
return affectedRows;
});
}
......@@ -2362,51 +2298,44 @@ class Model {
options.type = QueryTypes.RAW;
options.model = this;
var self = this
, instances;
let instances;
Utils.mapOptionFieldNames(options, this);
return Promise.try(function() {
return Promise.try(() => {
// Run before hook
if (options.hooks) {
return self.runHooks('beforeBulkRestore', options);
return this.runHooks('beforeBulkRestore', options);
}
}).then(function() {
}).then(() => {
// Get daos and run beforeRestore hook on each record individually
if (options.individualHooks) {
return self.findAll({where: options.where, transaction: options.transaction, logging: options.logging, benchmark: options.benchmark, paranoid: false}).map(function(instance) {
return self.runHooks('beforeRestore', instance, options).then(function() {
return instance;
return this.findAll({where: options.where, transaction: options.transaction, logging: options.logging, benchmark: options.benchmark, paranoid: false})
.map(instance => this.runHooks('beforeRestore', instance, options).then(() => instance))
.then((_instances) => {
instances = _instances;
});
}).then(function(_instances) {
instances = _instances;
});
}
}).then(function() {
}).then(() => {
// Run undelete query
var attrValueHash = {}
, deletedAtCol = self._timestampAttributes.deletedAt
, deletedAtAttribute = self.rawAttributes[deletedAtCol]
, deletedAtDefaultValue = deletedAtAttribute.hasOwnProperty('defaultValue') ? deletedAtAttribute.defaultValue : null;
const attrValueHash = {};
const deletedAtCol = this._timestampAttributes.deletedAt;
const deletedAtAttribute = this.rawAttributes[deletedAtCol];
const deletedAtDefaultValue = deletedAtAttribute.hasOwnProperty('defaultValue') ? deletedAtAttribute.defaultValue : null;
attrValueHash[deletedAtCol] = deletedAtDefaultValue;
options.omitNull = false;
return self.QueryInterface.bulkUpdate(self.getTableName(options), attrValueHash, options.where, options, self._timestampAttributes.deletedAt);
}).tap(function() {
return this.QueryInterface.bulkUpdate(this.getTableName(options), attrValueHash, options.where, options, this._timestampAttributes.deletedAt);
}).tap(() => {
// Run afterDestroy hook on each record individually
if (options.individualHooks) {
return Promise.map(instances, function(instance) {
return self.runHooks('afterRestore', instance, options);
});
return Promise.map(instances, instance => this.runHooks('afterRestore', instance, options));
}
}).tap(function() {
}).tap(() => {
// Run after hook
if (options.hooks) {
return self.runHooks('afterBulkRestore', options);
return this.runHooks('afterBulkRestore', options);
}
}).then(function(affectedRows) {
return affectedRows;
});
}
......@@ -2432,7 +2361,6 @@ class Model {
* @return {Promise<Array<affectedCount,affectedRows>>}
*/
static update(values, options) {
var self = this;
if (!options || !options.where) {
throw new Error('Missing where attribute in the options parameter passed to update.');
......@@ -2461,13 +2389,13 @@ class Model {
// Remove values that are not in the options.fields
if (options.fields && options.fields instanceof Array) {
Object.keys(values).forEach(function(key) {
for (const key of Object.keys(values)) {
if (options.fields.indexOf(key) < 0) {
delete values[key];
}
});
}
} else {
var updatedAtAttr = this._timestampAttributes.updatedAt;
const updatedAtAttr = this._timestampAttributes.updatedAt;
options.fields = _.intersection(Object.keys(values), Object.keys(this.tableAttributes));
if (updatedAtAttr && options.fields.indexOf(updatedAtAttr) === -1) {
options.fields.push(updatedAtAttr);
......@@ -2480,14 +2408,14 @@ class Model {
options.model = this;
var instances
, valuesUse;
let instances;
let valuesUse;
return Promise.try(function() {
return Promise.try(() => {
// Validate
if (options.validate) {
var build = self.build(values);
build.set(self._timestampAttributes.updatedAt, values[self._timestampAttributes.updatedAt], { raw: true });
const build = this.build(values);
build.set(this._timestampAttributes.updatedAt, values[this._timestampAttributes.updatedAt], { raw: true });
if (options.sideEffects) {
values = Utils._.assign(values, Utils._.pick(build.get(), build.changed()));
......@@ -2495,8 +2423,8 @@ class Model {
}
// We want to skip validations for all other fields
options.skip = Utils._.difference(Object.keys(self.attributes), Object.keys(values));
return build.validate(options).then(function(attributes) {
options.skip = Utils._.difference(Object.keys(this.attributes), Object.keys(values));
return build.validate(options).then(attributes => {
options.skip = undefined;
if (attributes && attributes.dataValues) {
values = Utils._.pick(attributes.dataValues, Object.keys(values));
......@@ -2504,22 +2432,22 @@ class Model {
});
}
return null;
}).then(function() {
}).then(() => {
// Run before hook
if (options.hooks) {
options.attributes = values;
return self.runHooks('beforeBulkUpdate', options).then(function() {
return this.runHooks('beforeBulkUpdate', options).then(() => {
values = options.attributes;
delete options.attributes;
});
}
return null;
}).then(function() {
}).then(() => {
valuesUse = values;
// Get instances and run beforeUpdate hook on each record individually
if (options.individualHooks) {
return self.findAll({where: options.where, transaction: options.transaction, logging: options.logging, benchmark: options.benchmark}).then(function(_instances) {
return this.findAll({where: options.where, transaction: options.transaction, logging: options.logging, benchmark: options.benchmark}).then(_instances => {
instances = _instances;
if (!instances.length) {
return [];
......@@ -2527,24 +2455,24 @@ class Model {
// Run beforeUpdate hooks on each record and check whether beforeUpdate hook changes values uniformly
// i.e. whether they change values for each record in the same way
var changedValues
, different = false;
let changedValues;
let different = false;
return Promise.map(instances, function(instance) {
return Promise.map(instances, instance => {
// Record updates in instances dataValues
Utils._.extend(instance.dataValues, values);
// Set the changed fields on the instance
Utils._.forIn(valuesUse, function(newValue, attr) {
Utils._.forIn(valuesUse, (newValue, attr) => {
if (newValue !== instance._previousDataValues[attr]) {
instance.setDataValue(attr, newValue);
}
});
// Run beforeUpdate hook
return self.runHooks('beforeUpdate', instance, options).then(function() {
return this.runHooks('beforeUpdate', instance, options).then(() => {
if (!different) {
var thisChangedValues = {};
Utils._.forIn(instance.dataValues, function(newValue, attr) {
const thisChangedValues = {};
Utils._.forIn(instance.dataValues, (newValue, attr) => {
if (newValue !== instance._previousDataValues[attr]) {
thisChangedValues[attr] = newValue;
}
......@@ -2559,11 +2487,11 @@ class Model {
return instance;
});
}).then(function(_instances) {
}).then(_instances => {
instances = _instances;
if (!different) {
var keys = Object.keys(changedValues);
const keys = Object.keys(changedValues);
// Hooks do not change values or change them uniformly
if (keys.length) {
// Hooks change values - record changes in valuesUse so they are executed
......@@ -2574,32 +2502,32 @@ class Model {
} else {
// Hooks change values in a different way for each record
// Do not run original query but save each record individually
return Promise.map(instances, function(instance) {
var individualOptions = Utils._.clone(options);
return Promise.map(instances, instance => {
const individualOptions = Utils._.clone(options);
delete individualOptions.individualHooks;
individualOptions.hooks = false;
individualOptions.validate = false;
return instance.save(individualOptions);
}).tap(function(_instances) {
}).tap(_instances => {
instances = _instances;
});
}
});
});
}
}).then(function(results) {
}).then(results => {
if (results) {
// Update already done row-by-row - exit
return [results.length, results];
}
valuesUse = Utils.mapValueFieldNames(valuesUse, options.fields, self);
options = Utils.mapOptionFieldNames(options, self);
options.hasTrigger = self.options ? self.options.hasTrigger : false;
valuesUse = Utils.mapValueFieldNames(valuesUse, options.fields, this);
options = Utils.mapOptionFieldNames(options, this);
options.hasTrigger = this.options ? this.options.hasTrigger : false;
// Run query to update all rows
return self.QueryInterface.bulkUpdate(self.getTableName(options), valuesUse, options.where, options, self.tableAttributes).then(function(affectedRows) {
return this.QueryInterface.bulkUpdate(this.getTableName(options), valuesUse, options.where, options, this.tableAttributes).then(affectedRows => {
if (options.returning) {
instances = affectedRows;
return [affectedRows.length, affectedRows];
......@@ -2607,25 +2535,22 @@ class Model {
return [affectedRows];
});
}).tap(function(result) {
}).tap(result => {
if (options.individualHooks) {
return Promise.map(instances, function(instance) {
return self.runHooks('afterUpdate', instance, options);
}).then(function() {
return Promise.map(instances, instance => {
return this.runHooks('afterUpdate', instance, options);
}).then(() => {
result[1] = instances;
});
}
}).tap(function() {
}).tap(() => {
// Run after hook
if (options.hooks) {
options.attributes = values;
return self.runHooks('afterBulkUpdate', options).then(function() {
return this.runHooks('afterBulkUpdate', options).then(() => {
delete options.attributes;
});
}
}).then(function(result) {
// Return result in form [affectedRows, instances] (instances missed off if options.individualHooks != true)
return result;
});
}
......@@ -2647,10 +2572,10 @@ class Model {
static $expandAttributes(options) {
if (_.isPlainObject(options.attributes)) {
var attributes = Object.keys(this.rawAttributes);
let attributes = Object.keys(this.rawAttributes);
if (options.attributes.exclude) {
attributes = attributes.filter(function (elem) {
attributes = attributes.filter(elem => {
return options.attributes.exclude.indexOf(elem) === -1;
});
}
......@@ -2664,10 +2589,9 @@ class Model {
// Inject $scope into options. Includes should have been conformed (conformOptions) before calling this
static $injectScope(options) {
var self = this;
var scope = Utils.cloneDeep(this.$scope);
const scope = Utils.cloneDeep(this.$scope);
var filteredScope = _.omit(scope, 'include'); // Includes need special treatment
const filteredScope = _.omit(scope, 'include'); // Includes need special treatment
_.defaults(options, filteredScope);
_.defaults(options.where, filteredScope.where);
......@@ -2677,21 +2601,21 @@ class Model {
// Reverse so we consider the latest include first.
// This is used if several scopes specify the same include - the last scope should take precedence
scope.include.reverse().forEach(function (scopeInclude) {
if (scopeInclude.all || !options.include.some(function matchesModelAndAlias(item) {
var isSameModel = item.model && item.model.name === scopeInclude.model.name;
for (const scopeInclude of scope.include.reverse()) {
if (scopeInclude.all || !options.include.some(item => {
const isSameModel = item.model && item.model.name === scopeInclude.model.name;
if (!isSameModel || !item.as) return isSameModel;
if (scopeInclude.as) {
return item.as === scopeInclude.as;
} else {
var association = scopeInclude.association || self.getAssociation(scopeInclude.model, scopeInclude.as);
const association = scopeInclude.association || this.getAssociation(scopeInclude.model, scopeInclude.as);
return association ? item.as === association.as : false;
}
})) {
options.include.push(scopeInclude);
}
});
}
}
}
......@@ -2725,8 +2649,8 @@ class Model {
}
_initValues(values, options) {
var defaults
, key;
let defaults;
let key;
values = values && _.clone(values) || {};
......@@ -2734,8 +2658,8 @@ class Model {
defaults = {};
if (this.constructor._hasDefaultValues) {
defaults = _.mapValues(this.constructor._defaultValues, function(valueFn) {
var value = valueFn();
defaults = _.mapValues(this.constructor._defaultValues, valueFn => {
const value = valueFn();
return (value && value._isSequelizeMethod) ? value : _.cloneDeep(value);
});
}
......@@ -2792,12 +2716,10 @@ class Model {
* @return {Object}
*/
where() {
var where;
where = this.constructor.primaryKeyAttributes.reduce(function (result, attribute) {
const where = this.constructor.primaryKeyAttributes.reduce((result, attribute) => {
result[attribute] = this.get(attribute, {raw: true});
return result;
}.bind(this), {});
}, {});
if (_.size(where) === 0) {
return this.$modelOptions.whereCollection;
......@@ -2826,7 +2748,7 @@ class Model {
* @param {any} value
*/
setDataValue(key, value) {
var originalValue = this._previousDataValues[key];
const originalValue = this._previousDataValues[key];
if (!Utils.isPrimitive(value) || value !== originalValue) {
this.changed(key, true);
}
......@@ -2859,9 +2781,7 @@ class Model {
}
if (options.plain && this.$options.include && this.$options.includeNames.indexOf(key) !== -1) {
if (Array.isArray(this.dataValues[key])) {
return this.dataValues[key].map(function (instance) {
return instance.get({plain: options.plain});
});
return this.dataValues[key].map(instance => instance.get({plain: options.plain}));
} else if (this.dataValues[key] instanceof Model) {
return this.dataValues[key].get({plain: options.plain});
} else {
......@@ -2872,8 +2792,8 @@ class Model {
}
if (this._hasCustomGetters || (options.plain && this.$options.include) || options.clone) {
var values = {}
, _key;
const values = {};
let _key;
if (this._hasCustomGetters) {
for (_key in this._customGetters) {
......@@ -2918,11 +2838,8 @@ class Model {
* @alias setAttributes
*/
set(key, value, options) { // testhint options:none
var values
, originalValue
, keys
, i
, length;
let values;
let originalValue;
if (typeof key === 'object' && key !== null) {
values = key;
......@@ -2945,7 +2862,7 @@ class Model {
// Loop and call set
if (options.attributes) {
keys = options.attributes;
let keys = options.attributes;
if (this.constructor._hasVirtualAttributes) {
keys = keys.concat(this.constructor._virtualAttributes);
}
......@@ -2954,13 +2871,13 @@ class Model {
keys = keys.concat(this.$options.includeNames);
}
for (i = 0, length = keys.length; i < length; i++) {
for (let i = 0, length = keys.length; i < length; i++) {
if (values[keys[i]] !== undefined) {
this.set(keys[i], values[keys[i]], options);
}
}
} else {
for (key in values) {
for (const key in values) {
this.set(key, values[key], options);
}
}
......@@ -2993,7 +2910,7 @@ class Model {
// If attribute is not in model definition, return
if (!this._isAttribute(key)) {
if (key.indexOf('.') > -1 && this.constructor._isJsonAttribute(key.split('.')[0])) {
var previousDottieValue = Dottie.get(this.dataValues, key);
const previousDottieValue = Dottie.get(this.dataValues, key);
if (!_.isEqual(previousDottieValue, value)) {
Dottie.set(this.dataValues, key, value);
this.changed(key.split('.')[0], true);
......@@ -3078,9 +2995,7 @@ class Model {
return this._changed[key] || false;
}
var changed = Object.keys(this.dataValues).filter(function(key) {
return this.changed(key);
}.bind(this));
const changed = Object.keys(this.dataValues).filter(key => this.changed(key));
return changed.length ? changed : false;
}
......@@ -3098,26 +3013,21 @@ class Model {
return this._previousDataValues[key];
}
return _.pickBy(this._previousDataValues, function(value, key) {
return this.changed(key);
}.bind(this));
return _.pickBy(this._previousDataValues, (value, key) => this.changed(key));
}
_setInclude(key, value, options) {
if (!Array.isArray(value)) value = [value];
if (value[0] instanceof Model) {
value = value.map(function(instance) {
return instance.dataValues;
});
value = value.map(instance => instance.dataValues);
}
var include = this.$options.includeMap[key]
, association = include.association
, self = this
, accessor = key
, childOptions
, primaryKeyAttribute = include.model.primaryKeyAttribute
, isEmpty;
const include = this.$options.includeMap[key];
const association = include.association;
const accessor = key;
const primaryKeyAttribute = include.model.primaryKeyAttribute;
let childOptions;
let isEmpty;
if (!isEmpty) {
childOptions = {
......@@ -3137,10 +3047,10 @@ class Model {
}
isEmpty = (value && value[primaryKeyAttribute] === null) || (value === null);
self[accessor] = self.dataValues[accessor] = isEmpty ? null : include.model.build(value, childOptions);
this[accessor] = this.dataValues[accessor] = isEmpty ? null : include.model.build(value, childOptions);
} else {
isEmpty = value[0] && value[0][primaryKeyAttribute] === null;
self[accessor] = self.dataValues[accessor] = isEmpty ? [] : include.model.bulkBuild(value, childOptions);
this[accessor] = this.dataValues[accessor] = isEmpty ? [] : include.model.bulkBuild(value, childOptions);
}
}
}
......@@ -3191,14 +3101,13 @@ class Model {
}
}
var self = this
, primaryKeyName = this.constructor.primaryKeyAttribute
, primaryKeyAttribute = primaryKeyName && this.constructor.rawAttributes[primaryKeyName]
, updatedAtAttr = this.constructor._timestampAttributes.updatedAt
, createdAtAttr = this.constructor._timestampAttributes.createdAt
, hook = self.isNewRecord ? 'Create' : 'Update'
, wasNewRecord = this.isNewRecord
, now = Utils.now(this.sequelize.options.dialect);
const primaryKeyName = this.constructor.primaryKeyAttribute;
const primaryKeyAttribute = primaryKeyName && this.constructor.rawAttributes[primaryKeyName];
const createdAtAttr = this.constructor._timestampAttributes.createdAt;
const hook = this.isNewRecord ? 'Create' : 'Update';
const wasNewRecord = this.isNewRecord;
const now = Utils.now(this.sequelize.options.dialect);
let updatedAtAttr = this.constructor._timestampAttributes.updatedAt;
if (updatedAtAttr && options.fields.length >= 1 && options.fields.indexOf(updatedAtAttr) === -1) {
options.fields.push(updatedAtAttr);
......@@ -3206,9 +3115,7 @@ class Model {
if (options.silent === true && !(this.isNewRecord && this.get(updatedAtAttr, {raw: true}))) {
// UpdateAtAttr might have been added as a result of Object.keys(Model.attributes). In that case we have to remove it again
Utils._.remove(options.fields, function(val) {
return val === updatedAtAttr;
});
Utils._.remove(options.fields, val => val === updatedAtAttr);
updatedAtAttr = false;
}
......@@ -3236,165 +3143,156 @@ class Model {
this.dataValues[createdAtAttr] = this.constructor.$getDefaultTimestamp(createdAtAttr) || now;
}
return Promise.bind(this).then(function() {
return Promise.try(() => {
// Validate
if (options.validate) {
return this.validate(options);
}
}).then(function() {
return Promise.bind(this).then(function() {
// Run before hook
if (options.hooks) {
var beforeHookValues = _.pick(this.dataValues, options.fields)
, afterHookValues
, hookChanged
, ignoreChanged = _.difference(this.changed(), options.fields); // In case of update where it's only supposed to update the passed values and the hook values
if (updatedAtAttr && options.fields.indexOf(updatedAtAttr) !== -1) {
ignoreChanged = _.without(ignoreChanged, updatedAtAttr);
}
}).then(() => {
// Run before hook
if (options.hooks) {
const beforeHookValues = _.pick(this.dataValues, options.fields);
let ignoreChanged = _.difference(this.changed(), options.fields); // In case of update where it's only supposed to update the passed values and the hook values
let hookChanged;
let afterHookValues;
return this.constructor.runHooks('before' + hook, this, options).bind(this).then(function() {
if (options.defaultFields && !this.isNewRecord) {
afterHookValues = _.pick(this.dataValues, _.difference(this.changed(), ignoreChanged));
if (updatedAtAttr && options.fields.indexOf(updatedAtAttr) !== -1) {
ignoreChanged = _.without(ignoreChanged, updatedAtAttr);
}
hookChanged = [];
Object.keys(afterHookValues).forEach(function (key) {
if (afterHookValues[key] !== beforeHookValues[key]) {
hookChanged.push(key);
}
});
return this.constructor.runHooks('before' + hook, this, options).then(() => {
if (options.defaultFields && !this.isNewRecord) {
afterHookValues = _.pick(this.dataValues, _.difference(this.changed(), ignoreChanged));
options.fields = _.uniq(options.fields.concat(hookChanged));
hookChanged = [];
for (const key of Object.keys(afterHookValues)) {
if (afterHookValues[key] !== beforeHookValues[key]) {
hookChanged.push(key);
}
}
if (hookChanged) {
if (options.validate) {
// Validate again
options.fields = _.uniq(options.fields.concat(hookChanged));
}
options.skip = _.difference(Object.keys(this.constructor.rawAttributes), hookChanged);
return this.validate(options).then(function() {
delete options.skip;
});
}
if (hookChanged) {
if (options.validate) {
// Validate again
options.skip = _.difference(Object.keys(this.constructor.rawAttributes), hookChanged);
return this.validate(options).then(() => {
delete options.skip;
});
}
});
}
}).then(function() {
if (!options.fields.length) return this;
if (!this.isNewRecord) return this;
if (!this.$options.include || !this.$options.include.length) return this;
// Nested creation for BelongsTo relations
return Promise.map(this.$options.include.filter(function (include) {
return include.association instanceof BelongsTo;
}), function (include) {
var instance = self.get(include.as);
if (!instance) return Promise.resolve();
var includeOptions = _(Utils.cloneDeep(include))
.omit(['association'])
.defaults({
transaction: options.transaction,
logging: options.logging,
parentRecord: self
}).value();
return instance.save(includeOptions).then(function () {
return self[include.association.accessors.set](instance, {save: false, logging: options.logging});
});
}
});
})
.then(function() {
if (!options.fields.length) return this;
if (!this.changed() && !this.isNewRecord) return this;
var values = Utils.mapValueFieldNames(this.dataValues, options.fields, this.constructor)
, query = null
, args = [];
if (self.isNewRecord) {
query = 'insert';
args = [self, self.constructor.getTableName(options), values, options];
} else {
var where = this.where();
}
}).then(() => {
if (!options.fields.length) return this;
if (!this.isNewRecord) return this;
if (!this.$options.include || !this.$options.include.length) return this;
// Nested creation for BelongsTo relations
return Promise.map(this.$options.include.filter(include => include.association instanceof BelongsTo), include => {
const instance = this.get(include.as);
if (!instance) return Promise.resolve();
const includeOptions = _(Utils.cloneDeep(include))
.omit(['association'])
.defaults({
transaction: options.transaction,
logging: options.logging,
parentRecord: this
}).value();
return instance.save(includeOptions).then(() => this[include.association.accessors.set](instance, {save: false, logging: options.logging}));
});
}).then(() => {
if (!options.fields.length) return this;
if (!this.changed() && !this.isNewRecord) return this;
where = Utils.mapValueFieldNames(where, Object.keys(where), this.constructor);
let values = Utils.mapValueFieldNames(this.dataValues, options.fields, this.constructor);
let query = null;
let args = [];
query = 'update';
args = [self, self.constructor.getTableName(options), values, where, options];
}
if (this.isNewRecord) {
query = 'insert';
args = [this, this.constructor.getTableName(options), values, options];
} else {
let where = this.where();
return self.sequelize.getQueryInterface()[query].apply(self.sequelize.getQueryInterface(), args)
.then(function(result) {
// Transfer database generated values (defaults, autoincrement, etc)
Object.keys(self.constructor.rawAttributes).forEach(function (attr) {
if (self.constructor.rawAttributes[attr].field &&
values[self.constructor.rawAttributes[attr].field] !== undefined &&
self.constructor.rawAttributes[attr].field !== attr
) {
values[attr] = values[self.constructor.rawAttributes[attr].field];
delete values[self.constructor.rawAttributes[attr].field];
}
});
values = _.extend(values, result.dataValues);
where = Utils.mapValueFieldNames(where, Object.keys(where), this.constructor);
result.dataValues = _.extend(result.dataValues, values);
return result;
})
.tap(function(result) {
if (!wasNewRecord) return self;
if (!self.$options.include || !self.$options.include.length) return self;
// Nested creation for HasOne/HasMany/BelongsToMany relations
return Promise.map(self.$options.include.filter(function (include) {
return !(include.association instanceof BelongsTo);
}), function (include) {
var instances = self.get(include.as);
if (!instances) return Promise.resolve();
if (!Array.isArray(instances)) instances = [instances];
if (!instances.length) return Promise.resolve();
var includeOptions = _(Utils.cloneDeep(include))
.omit(['association'])
.defaults({
transaction: options.transaction,
logging: options.logging,
parentRecord: self
}).value();
// Instances will be updated in place so we can safely treat HasOne like a HasMany
return Promise.map(instances, function (instance) {
if (include.association instanceof BelongsToMany) {
return instance.save(includeOptions).then(function () {
var values = {};
values[include.association.foreignKey] = self.get(self.constructor.primaryKeyAttribute, {raw: true});
values[include.association.otherKey] = instance.get(instance.constructor.primaryKeyAttribute, {raw: true});
return include.association.throughModel.create(values, includeOptions);
});
} else {
instance.set(include.association.foreignKey, self.get(self.constructor.primaryKeyAttribute, {raw: true}));
return instance.save(includeOptions);
}
});
});
})
.tap(function(result) {
// Run after hook
if (options.hooks) {
return self.constructor.runHooks('after' + hook, result, options);
query = 'update';
args = [this, this.constructor.getTableName(options), values, where, options];
}
return this.sequelize.getQueryInterface()[query].apply(this.sequelize.getQueryInterface(), args)
.then(result => {
// Transfer database generated values (defaults, autoincrement, etc)
for (const attr of Object.keys(this.constructor.rawAttributes)) {
if (this.constructor.rawAttributes[attr].field &&
values[this.constructor.rawAttributes[attr].field] !== undefined &&
this.constructor.rawAttributes[attr].field !== attr
) {
values[attr] = values[this.constructor.rawAttributes[attr].field];
delete values[this.constructor.rawAttributes[attr].field];
}
})
.then(function(result) {
options.fields.forEach(function (field) {
result._previousDataValues[field] = result.dataValues[field];
self.changed(field, false);
}
values = _.extend(values, result.dataValues);
result.dataValues = _.extend(result.dataValues, values);
return result;
})
.tap(result => {
if (!wasNewRecord) return this;
if (!this.$options.include || !this.$options.include.length) return this;
// Nested creation for HasOne/HasMany/BelongsToMany relations
return Promise.map(this.$options.include.filter(include => !(include.association instanceof BelongsTo)), include => {
let instances = this.get(include.as);
if (!instances) return Promise.resolve();
if (!Array.isArray(instances)) instances = [instances];
if (!instances.length) return Promise.resolve();
const includeOptions = _(Utils.cloneDeep(include))
.omit(['association'])
.defaults({
transaction: options.transaction,
logging: options.logging,
parentRecord: this
}).value();
// Instances will be updated in place so we can safely treat HasOne like a HasMany
return Promise.map(instances, instance => {
if (include.association instanceof BelongsToMany) {
return instance.save(includeOptions).then(() => {
const values = {};
values[include.association.foreignKey] = this.get(this.constructor.primaryKeyAttribute, {raw: true});
values[include.association.otherKey] = instance.get(instance.constructor.primaryKeyAttribute, {raw: true});
return include.association.throughModel.create(values, includeOptions);
});
} else {
instance.set(include.association.foreignKey, this.get(this.constructor.primaryKeyAttribute, {raw: true}));
return instance.save(includeOptions);
}
});
self.isNewRecord = false;
return result;
});
});
})
.tap(result => {
// Run after hook
if (options.hooks) {
return this.constructor.runHooks('after' + hook, result, options);
}
})
.then(result => {
for (const field of options.fields) {
result._previousDataValues[field] = result.dataValues[field];
this.changed(field, false);
}
this.isNewRecord = false;
return result;
});
});
}
......@@ -3414,15 +3312,15 @@ class Model {
include: this.$options.include || null
});
return this.constructor.findOne(options).bind(this)
.tap(function (reload) {
return this.constructor.findOne(options)
.tap(reload => {
if (!reload) {
throw new sequelizeErrors.InstanceError(
'Instance could not be reloaded because it does not exist anymore (find call returned null)'
);
}
})
.then(function(reload) {
.then(reload => {
// update the internal options of the instance
this.$options = reload.$options;
// re-set instance values
......@@ -3430,7 +3328,8 @@ class Model {
raw: true,
reset: true && !options.attributes
});
}).return(this);
return this;
});
}
/*
......@@ -3462,22 +3361,19 @@ class Model {
* @alias updateAttributes
*/
update(values, options) {
var changedBefore = this.changed() || []
, sideEffects
, fields
, setOptions;
const changedBefore = this.changed() || [];
options = options || {};
if (Array.isArray(options)) options = {fields: options};
options = Utils.cloneDeep(options);
setOptions = Utils.cloneDeep(options);
const setOptions = Utils.cloneDeep(options);
setOptions.attributes = options.fields;
this.set(values, setOptions);
// Now we need to figure out which fields were actually affected by the setter.
sideEffects = _.without.apply(this, [this.changed() || []].concat(changedBefore));
fields = _.union(Object.keys(values), sideEffects);
const sideEffects = _.without.apply(this, [this.changed() || []].concat(changedBefore));
const fields = _.union(Object.keys(values), sideEffects);
if (!options.fields) {
options.fields = _.intersection(fields, this.changed());
......@@ -3505,18 +3401,18 @@ class Model {
force: false
}, options);
return Promise.bind(this).then(function() {
return Promise.try(() => {
// Run before hook
if (options.hooks) {
return this.constructor.runHooks('beforeDestroy', this, options);
}
}).then(function() {
var where = this.where();
}).then(() => {
const where = this.where();
if (this.constructor._timestampAttributes.deletedAt && options.force === false) {
var attribute = this.constructor.rawAttributes[this.constructor._timestampAttributes.deletedAt]
, field = attribute.field || this.constructor._timestampAttributes.deletedAt
, values = {};
const attribute = this.constructor.rawAttributes[this.constructor._timestampAttributes.deletedAt];
const field = attribute.field || this.constructor._timestampAttributes.deletedAt;
const values = {};
values[field] = new Date();
where[field] = attribute.hasOwnProperty('defaultValue') ? attribute.defaultValue : null;
......@@ -3527,13 +3423,11 @@ class Model {
} else {
return this.sequelize.getQueryInterface().delete(this, this.constructor.getTableName(options), where, _.assign({ type: QueryTypes.DELETE, limit: null }, options));
}
}).tap(function() {
}).tap(() => {
// Run after hook
if (options.hooks) {
return this.constructor.runHooks('afterDestroy', this, options);
}
}).then(function(result) {
return result;
});
}
......@@ -3554,19 +3448,19 @@ class Model {
force: false
}, options);
return Promise.bind(this).then(function() {
return Promise.try(() => {
// Run before hook
if (options.hooks) {
return this.constructor.runHooks('beforeRestore', this, options);
}
}).then(function() {
var deletedAtCol = this.constructor._timestampAttributes.deletedAt
, deletedAtAttribute = this.constructor.rawAttributes[deletedAtCol]
, deletedAtDefaultValue = deletedAtAttribute.hasOwnProperty('defaultValue') ? deletedAtAttribute.defaultValue : null;
}).then(() => {
const deletedAtCol = this.constructor._timestampAttributes.deletedAt;
const deletedAtAttribute = this.constructor.rawAttributes[deletedAtCol];
const deletedAtDefaultValue = deletedAtAttribute.hasOwnProperty('defaultValue') ? deletedAtAttribute.defaultValue : null;
this.setDataValue(deletedAtCol, deletedAtDefaultValue);
return this.save(_.extend({}, options, {hooks : false, omitNull : false}));
}).tap(function() {
}).tap(() => {
// Run after hook
if (options.hooks) {
return this.constructor.runHooks('afterRestore', this, options);
......@@ -3599,10 +3493,8 @@ class Model {
* @return {Promise<this>}
*/
increment(fields, options) {
var identifier = this.where()
, updatedAtAttr = this.constructor._timestampAttributes.updatedAt
, values = {}
, where;
const identifier = this.where();
const updatedAtAttr = this.constructor._timestampAttributes.updatedAt;
options = _.defaults({}, options, {
by: 1,
......@@ -3610,12 +3502,13 @@ class Model {
where: {}
});
where = _.extend({}, options.where, identifier);
const where = _.extend({}, options.where, identifier);
let values = {};
if (Utils._.isString(fields)) {
values[fields] = options.by;
} else if (Utils._.isArray(fields)) {
Utils._.each(fields, function(field) {
Utils._.each(fields, field => {
values[field] = options.by;
});
} else { // Assume fields is key-value pairs
......@@ -3626,13 +3519,13 @@ class Model {
options.attributes[updatedAtAttr] = this.constructor.$getDefaultTimestamp(updatedAtAttr) || Utils.now(this.sequelize.options.dialect);
}
Object.keys(values).forEach(function(attr) {
for (const attr of Object.keys(values)) {
// Field name mapping
if (this.constructor.rawAttributes[attr] && this.constructor.rawAttributes[attr].field && this.constructor.rawAttributes[attr].field !== attr) {
values[this.constructor.rawAttributes[attr].field] = values[attr];
delete values[attr];
}
}, this);
}
return this.sequelize.getQueryInterface().increment(this, this.constructor.getTableName(options), values, where, options).return(this);
}
......@@ -3667,7 +3560,7 @@ class Model {
});
if (!Utils._.isString(fields) && !Utils._.isArray(fields)) { // Assume fields is key-value pairs
Utils._.each(fields, function(value, field) {
Utils._.each(fields, (value, field) => {
fields[field] = -value;
});
}
......@@ -3684,19 +3577,16 @@ class Model {
* @return {Boolean}
*/
equals(other) {
var self = this;
if (!other || !other.constructor) {
return false;
}
if (!(other instanceof self.constructor)) {
if (!(other instanceof this.constructor)) {
return false;
}
return Utils._.every(this.constructor.primaryKeyAttributes, function(attribute) {
return self.get(attribute, {raw: true}) === other.get(attribute, {raw: true});
});
return Utils._.every(this.constructor.primaryKeyAttributes, attribute => this.get(attribute, {raw: true}) === other.get(attribute, {raw: true}));
}
/**
......@@ -3706,11 +3596,7 @@ class Model {
* @return {Boolean}
*/
equalsOneOf(others) {
var self = this;
return _.some(others, function(other) {
return self.equals(other);
});
return _.some(others, other => this.equals(other));
}
setValidators(attribute, validators) {
......
'use strict';
var Attribute = function(options) {
class Attribute {
constructor(options) {
if (options.type === undefined) options = {type: options};
this.type = options.type;
};
}
}
module.exports = Attribute;
module.exports.Attribute = Attribute;
module.exports.default = Attribute;
Markdown is supported
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!