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

Commit f2723c7f by Mick Hansen

Merge pull request #2222 from janmeier/master

Fix object syntax for hasmany and belongsto foreign keys
2 parents 8a706120 30ce052b
# Next
- [BUG] Fixed an issue with foreign key object syntax for hasOne and belongsTo
- [FEATURE] Added `field` and `name` to the object form of foreign key definitions
#### Backwards compatability changes
- The `fieldName` property, used in associations with a foreign key object `(A.hasMany(B, { foreignKey: { ... }})`, has been renamed to `name` to avoid confusion with `field`.
# v2.0.0-dev13 # v2.0.0-dev13
We are working our way to the first 2.0.0 release candidate. We are working our way to the first 2.0.0 release candidate.
......
...@@ -16,7 +16,7 @@ module.exports = (function() { ...@@ -16,7 +16,7 @@ module.exports = (function() {
if (Utils._.isObject(this.options.foreignKey)) { if (Utils._.isObject(this.options.foreignKey)) {
this.foreignKeyAttribute = this.options.foreignKey; this.foreignKeyAttribute = this.options.foreignKey;
this.foreignKey = this.foreignKeyAttribute.fieldName; this.foreignKey = this.foreignKeyAttribute.name || this.foreignKeyAttribute.fieldName;
} else { } else {
this.foreignKeyAttribute = {}; this.foreignKeyAttribute = {};
this.foreignKey = this.options.foreignKey; this.foreignKey = this.options.foreignKey;
...@@ -42,7 +42,14 @@ module.exports = (function() { ...@@ -42,7 +42,14 @@ module.exports = (function() {
); );
} }
this.identifier = this.options.foreignKey; this.identifier = this.foreignKey || Utils._.camelizeIf(
[
Utils._.underscoredIf(this.options.name.singular, this.target.options.underscored),
this.target.primaryKeyAttribute
].join('_'),
!this.target.options.underscored
);
this.targetIdentifier = this.target.primaryKeyAttribute; this.targetIdentifier = this.target.primaryKeyAttribute;
this.associationAccessor = this.as; this.associationAccessor = this.as;
this.options.useHooks = options.useHooks; this.options.useHooks = options.useHooks;
...@@ -67,7 +74,7 @@ module.exports = (function() { ...@@ -67,7 +74,7 @@ module.exports = (function() {
this.options.onUpdate = this.options.onUpdate || 'CASCADE'; this.options.onUpdate = this.options.onUpdate || 'CASCADE';
} }
Helpers.addForeignKeyConstraints(newAttributes[this.identifier], this.target, this.source, this.options); Helpers.addForeignKeyConstraints(newAttributes[this.identifier], this.target, this.source, this.options);
Utils._.defaults(this.source.rawAttributes, newAttributes); Utils.mergeDefaults(this.source.rawAttributes, newAttributes);
// Sync attributes and setters/getters to DAO prototype // Sync attributes and setters/getters to DAO prototype
this.source.refreshAttributes(); this.source.refreshAttributes();
......
...@@ -30,7 +30,7 @@ module.exports = (function() { ...@@ -30,7 +30,7 @@ module.exports = (function() {
if (Utils._.isObject(this.options.foreignKey)) { if (Utils._.isObject(this.options.foreignKey)) {
this.foreignKeyAttribute = this.options.foreignKey; this.foreignKeyAttribute = this.options.foreignKey;
this.foreignKey = this.foreignKeyAttribute.fieldName; this.foreignKey = this.foreignKeyAttribute.name || this.foreignKeyAttribute.fieldName;
} else { } else {
this.foreignKeyAttribute = {}; this.foreignKeyAttribute = {};
this.foreignKey = this.options.foreignKey; this.foreignKey = this.options.foreignKey;
...@@ -273,7 +273,7 @@ module.exports = (function() { ...@@ -273,7 +273,7 @@ module.exports = (function() {
constraintOptions.onUpdate = constraintOptions.onUpdate || 'CASCADE'; constraintOptions.onUpdate = constraintOptions.onUpdate || 'CASCADE';
} }
Helpers.addForeignKeyConstraints(newAttributes[this.identifier], this.source, this.target, constraintOptions); Helpers.addForeignKeyConstraints(newAttributes[this.identifier], this.source, this.target, constraintOptions);
Utils._.defaults(this.target.rawAttributes, newAttributes); Utils.mergeDefaults(this.target.rawAttributes, newAttributes);
} }
// Sync attributes and setters/getters to DAO prototype // Sync attributes and setters/getters to DAO prototype
......
...@@ -16,7 +16,7 @@ module.exports = (function() { ...@@ -16,7 +16,7 @@ module.exports = (function() {
if (Utils._.isObject(this.options.foreignKey)) { if (Utils._.isObject(this.options.foreignKey)) {
this.foreignKeyAttribute = this.options.foreignKey; this.foreignKeyAttribute = this.options.foreignKey;
this.foreignKey = this.foreignKeyAttribute.fieldName; this.foreignKey = this.foreignKeyAttribute.name || this.foreignKeyAttribute.fieldName;
} else { } else {
this.foreignKeyAttribute = {}; this.foreignKeyAttribute = {};
this.foreignKey = this.options.foreignKey; this.foreignKey = this.options.foreignKey;
...@@ -42,7 +42,14 @@ module.exports = (function() { ...@@ -42,7 +42,14 @@ module.exports = (function() {
); );
} }
this.identifier = this.options.foreignKey; this.identifier = this.foreignKey || Utils._.camelizeIf(
[
Utils._.underscoredIf(this.source.options.name.singular, this.source.options.underscored),
this.source.primaryKeyAttribute
].join('_'),
!this.source.options.underscored
);
this.sourceIdentifier = this.source.primaryKeyAttribute; this.sourceIdentifier = this.source.primaryKeyAttribute;
this.associationAccessor = this.as; this.associationAccessor = this.as;
this.options.useHooks = options.useHooks; this.options.useHooks = options.useHooks;
...@@ -63,7 +70,7 @@ module.exports = (function() { ...@@ -63,7 +70,7 @@ module.exports = (function() {
, keyType = this.source.rawAttributes[this.sourceIdentifier].type; , keyType = this.source.rawAttributes[this.sourceIdentifier].type;
newAttributes[this.identifier] = Utils._.defaults(this.foreignKeyAttribute, { type: this.options.keyType || keyType }); newAttributes[this.identifier] = Utils._.defaults(this.foreignKeyAttribute, { type: this.options.keyType || keyType });
Utils._.defaults(this.target.rawAttributes, newAttributes); Utils.mergeDefaults(this.target.rawAttributes, newAttributes);
if (this.options.constraints !== false) { if (this.options.constraints !== false) {
this.options.onDelete = this.options.onDelete || 'SET NULL'; this.options.onDelete = this.options.onDelete || 'SET NULL';
......
...@@ -48,7 +48,7 @@ var Utils = require('./../utils') ...@@ -48,7 +48,7 @@ var Utils = require('./../utils')
* ```js * ```js
* User.hasMany(Picture, { * User.hasMany(Picture, {
* foreignKey: { * foreignKey: {
* fieldName: 'uid' * name: 'uid'
* allowNull: false * allowNull: false
* } * }
* }) * })
...@@ -135,7 +135,7 @@ var singleLinked = function (Type) { ...@@ -135,7 +135,7 @@ var singleLinked = function (Type) {
* @param {object} [options] * @param {object} [options]
* @param {boolean} [options.hooks=false] Set to true to run before-/afterDestroy hooks when an associated model is deleted because of a cascade. For example if `User.hasOne(Profile, {onDelete: 'cascade', hooks:true})`, the before-/afterDestroy hooks for profile will be called when a user is deleted. Otherwise the profile will be deleted without invoking any hooks * @param {boolean} [options.hooks=false] Set to true to run before-/afterDestroy hooks when an associated model is deleted because of a cascade. For example if `User.hasOne(Profile, {onDelete: 'cascade', hooks:true})`, the before-/afterDestroy hooks for profile will be called when a user is deleted. Otherwise the profile will be deleted without invoking any hooks
* @param {string} [options.as] The alias of this model, in singular form. See also the `name` option passed to `sequelize.define`. If you create multiple associations between the same tables, you should provide an alias to be able to distinguish between them. If you provide an alias when creating the assocition, you should provide the same alias when eager loading and when getting assocated models. Defaults to the singularized name of target * @param {string} [options.as] The alias of this model, in singular form. See also the `name` option passed to `sequelize.define`. If you create multiple associations between the same tables, you should provide an alias to be able to distinguish between them. If you provide an alias when creating the assocition, you should provide the same alias when eager loading and when getting assocated models. Defaults to the singularized name of target
* @param {string|object} [options.foreignKey] The name of the foreign key in the target table or an object representing the type definition for the foreign column (see `Sequelize.define` for syntax). When using an object, you can add a `fieldName` property to set the name of the colum. Defaults to the name of source + primary key of source * @param {string|object} [options.foreignKey] The name of the foreign key in the target table or an object representing the type definition for the foreign column (see `Sequelize.define` for syntax). When using an object, you can add a `name` property to set the name of the colum. Defaults to the name of source + primary key of source
* @param {string} [options.onDelete='SET NULL'] * @param {string} [options.onDelete='SET NULL']
* @param {string} [options.onUpdate='CASCADE'] * @param {string} [options.onUpdate='CASCADE']
* @param {boolean} [options.constraints=true] Should on update and on delete constraints be enabled on the foreign key. * @param {boolean} [options.constraints=true] Should on update and on delete constraints be enabled on the foreign key.
...@@ -159,7 +159,7 @@ Mixin.hasOne = singleLinked(HasOne); ...@@ -159,7 +159,7 @@ Mixin.hasOne = singleLinked(HasOne);
* @param {object} [options] * @param {object} [options]
* @param {boolean} [options.hooks=false] Set to true to run before-/afterDestroy hooks when an associated model is deleted because of a cascade. For example if `User.hasOne(Profile, {onDelete: 'cascade', hooks:true})`, the before-/afterDestroy hooks for profile will be called when a user is deleted. Otherwise the profile will be deleted without invoking any hooks * @param {boolean} [options.hooks=false] Set to true to run before-/afterDestroy hooks when an associated model is deleted because of a cascade. For example if `User.hasOne(Profile, {onDelete: 'cascade', hooks:true})`, the before-/afterDestroy hooks for profile will be called when a user is deleted. Otherwise the profile will be deleted without invoking any hooks
* @param {string} [options.as] The alias of this model, in singular form. See also the `name` option passed to `sequelize.define`. If you create multiple associations between the same tables, you should provide an alias to be able to distinguish between them. If you provide an alias when creating the assocition, you should provide the same alias when eager loading and when getting assocated models. Defaults to the singularized name of target * @param {string} [options.as] The alias of this model, in singular form. See also the `name` option passed to `sequelize.define`. If you create multiple associations between the same tables, you should provide an alias to be able to distinguish between them. If you provide an alias when creating the assocition, you should provide the same alias when eager loading and when getting assocated models. Defaults to the singularized name of target
* @param {string|object} [options.foreignKey] The name of the foreign key in the source table or an object representing the type definition for the foreign column (see `Sequelize.define` for syntax). When using an object, you can add a `fieldName` property to set the name of the colum. Defaults to the name of target + primary key of target * @param {string|object} [options.foreignKey] The name of the foreign key in the source table or an object representing the type definition for the foreign column (see `Sequelize.define` for syntax). When using an object, you can add a `name` property to set the name of the colum. Defaults to the name of target + primary key of target
* @param {string} [options.onDelete='SET NULL'] * @param {string} [options.onDelete='SET NULL']
* @param {string} [options.onUpdate='CASCADE'] * @param {string} [options.onUpdate='CASCADE']
* @param {boolean} [options.constraints=true] Should on update and on delete constraints be enabled on the foreign key. * @param {boolean} [options.constraints=true] Should on update and on delete constraints be enabled on the foreign key.
...@@ -229,7 +229,7 @@ Mixin.belongsTo = singleLinked(BelongsTo); ...@@ -229,7 +229,7 @@ Mixin.belongsTo = singleLinked(BelongsTo);
* @param {boolean} [options.hooks=false] Set to true to run before-/afterDestroy hooks when an associated model is deleted because of a cascade. For example if `User.hasOne(Profile, {onDelete: 'cascade', hooks:true})`, the before-/afterDestroy hooks for profile will be called when a user is deleted. Otherwise the profile will be deleted without invoking any hooks * @param {boolean} [options.hooks=false] Set to true to run before-/afterDestroy hooks when an associated model is deleted because of a cascade. For example if `User.hasOne(Profile, {onDelete: 'cascade', hooks:true})`, the before-/afterDestroy hooks for profile will be called when a user is deleted. Otherwise the profile will be deleted without invoking any hooks
* @param {Model|string} [options.through] The name of the table that is used to join source and target in n:m associations. Can also be a sequelize model if you want to define the junction table yourself and add extra attributes to it. * @param {Model|string} [options.through] The name of the table that is used to join source and target in n:m associations. Can also be a sequelize model if you want to define the junction table yourself and add extra attributes to it.
* @param {string|object} [options.as] The alias of this model. If you provide a string, it should be plural, and will be singularized using node.inflection. If you want to control the singular version yourself, provide an object with `plural` and `singular` keys. See also the `name` option passed to `sequelize.define`. If you create multiple associations between the same tables, you should provide an alias to be able to distinguish between them. If you provide an alias when creating the assocition, you should provide the same alias when eager loading and when getting assocated models. Defaults to the pluralized name of target * @param {string|object} [options.as] The alias of this model. If you provide a string, it should be plural, and will be singularized using node.inflection. If you want to control the singular version yourself, provide an object with `plural` and `singular` keys. See also the `name` option passed to `sequelize.define`. If you create multiple associations between the same tables, you should provide an alias to be able to distinguish between them. If you provide an alias when creating the assocition, you should provide the same alias when eager loading and when getting assocated models. Defaults to the pluralized name of target
* @param {string|object} [options.foreignKey] The name of the foreign key in the target table / join table or an object representing the type definition for the foreign column (see `Sequelize.define` for syntax). When using an object, you can add a `fieldName` property to set the name of the colum. Defaults to the name of source + primary key of source * @param {string|object} [options.foreignKey] The name of the foreign key in the target table / join table or an object representing the type definition for the foreign column (see `Sequelize.define` for syntax). When using an object, you can add a `name` property to set the name of the colum. Defaults to the name of source + primary key of source
* @param {string} [options.onDelete='SET NULL|CASCADE'] Cascade if this is a n:m, and set null if it is a 1:m * @param {string} [options.onDelete='SET NULL|CASCADE'] Cascade if this is a n:m, and set null if it is a 1:m
* @param {string} [options.onUpdate='CASCADE'] * @param {string} [options.onUpdate='CASCADE']
* @param {boolean} [options.constraints=true] Should on update and on delete constraints be enabled on the foreign key. * @param {boolean} [options.constraints=true] Should on update and on delete constraints be enabled on the foreign key.
......
...@@ -57,6 +57,15 @@ var Utils = module.exports = { ...@@ -57,6 +57,15 @@ var Utils = module.exports = {
return _; return _;
})(), })(),
// Same concept as _.merge, but don't overwrite properties that have already been assigned
mergeDefaults: function (a, b) {
return this._.merge(a, b, function (objectValue, sourceValue) {
// If it's an object, let _ handle it this time, we will be called again for each property
if (!this._.isPlainObject(objectValue) && objectValue !== undefined) {
return objectValue;
}
}, this);
},
lowercaseFirst: function (s) { lowercaseFirst: function (s) {
return s[0].toLowerCase() + s.slice(1); return s[0].toLowerCase() + s.slice(1);
}, },
......
...@@ -510,8 +510,27 @@ describe(Support.getTestDialectTeaser("BelongsTo"), function() { ...@@ -510,8 +510,27 @@ describe(Support.getTestDialectTeaser("BelongsTo"), function() {
}) })
}) })
it('allows the user to provide an attribute definition as foreignKey', function () { describe('allows the user to provide an attribute definition object as foreignKey', function () {
var User = this.sequelize.define('user', { it('works with a column that hasnt been defined before', function () {
var Task = this.sequelize.define('task', {})
, User = this.sequelize.define('user', {
});
Task.belongsTo(User, {
foreignKey: {
allowNull: false,
name: 'uid'
}
});
expect(Task.rawAttributes.uid).to.be.defined
expect(Task.rawAttributes.uid.allowNull).to.be.false;
expect(Task.rawAttributes.uid.references).to.equal(User.getTableName())
expect(Task.rawAttributes.uid.referencesKey).to.equal('id')
});
it('works when taking a column directly from the object', function () {
var User = this.sequelize.define('user', {
uid: { uid: {
type: Sequelize.INTEGER, type: Sequelize.INTEGER,
primaryKey: true primaryKey: true
...@@ -524,13 +543,30 @@ describe(Support.getTestDialectTeaser("BelongsTo"), function() { ...@@ -524,13 +543,30 @@ describe(Support.getTestDialectTeaser("BelongsTo"), function() {
} }
}) })
Profile.belongsTo(User, { foreignKey: Profile.rawAttributes.user_id}) Profile.belongsTo(User, { foreignKey: Profile.rawAttributes.user_id})
expect(Profile.rawAttributes.user_id).to.be.defined expect(Profile.rawAttributes.user_id).to.be.defined
expect(Profile.rawAttributes.user_id.references).to.equal(User.getTableName()) expect(Profile.rawAttributes.user_id.references).to.equal(User.getTableName())
expect(Profile.rawAttributes.user_id.referencesKey).to.equal('uid') expect(Profile.rawAttributes.user_id.referencesKey).to.equal('uid')
expect(Profile.rawAttributes.user_id.allowNull).to.be.false expect(Profile.rawAttributes.user_id.allowNull).to.be.false
}) });
it('works when merging with an existing definition', function () {
var Task = this.sequelize.define('task', {
projectId: {
defaultValue: 42,
type: Sequelize.INTEGER
}
})
, Project = this.sequelize.define('project', {});
Task.belongsTo(Project, { foreignKey: { allowNull: true }});
expect(Task.rawAttributes.projectId).to.be.defined
expect(Task.rawAttributes.projectId.defaultValue).to.equal(42);
expect(Task.rawAttributes.projectId.allowNull).to.be.ok;
})
});
it('should throw an error if foreignKey and as result in a name clash', function () { it('should throw an error if foreignKey and as result in a name clash', function () {
var Person = this.sequelize.define('person', {}) var Person = this.sequelize.define('person', {})
......
...@@ -504,11 +504,7 @@ describe(Support.getTestDialectTeaser("HasMany"), function() { ...@@ -504,11 +504,7 @@ describe(Support.getTestDialectTeaser("HasMany"), function() {
'title': DataTypes.STRING 'title': DataTypes.STRING
}), Label = this.sequelize.define('Label', { }), Label = this.sequelize.define('Label', {
'text': DataTypes.STRING, 'text': DataTypes.STRING
'ArticleId': {
type: DataTypes.INTEGER,
allowNull: false
}
}); });
Article.hasMany(Label); Article.hasMany(Label);
...@@ -1337,7 +1333,7 @@ describe(Support.getTestDialectTeaser("HasMany"), function() { ...@@ -1337,7 +1333,7 @@ describe(Support.getTestDialectTeaser("HasMany"), function() {
}).then(function (user) { }).then(function (user) {
return user.setTasks(null).on('sql', spy).on('sql', _.after(2, function (sql) { return user.setTasks(null).on('sql', spy).on('sql', _.after(2, function (sql) {
expect(sql).to.have.string("DELETE FROM"); expect(sql).to.have.string("DELETE FROM");
expect(sql).to.have.string("IN (1,2)"); expect(sql).to.match(/IN \(1,2\)|IN \(2,1\)/);
})); }));
}).then(function () { }).then(function () {
expect(spy.calledTwice).to.be.ok; // Once for SELECT, once for DELETE expect(spy.calledTwice).to.be.ok; // Once for SELECT, once for DELETE
...@@ -2210,8 +2206,49 @@ describe(Support.getTestDialectTeaser("HasMany"), function() { ...@@ -2210,8 +2206,49 @@ describe(Support.getTestDialectTeaser("HasMany"), function() {
}); });
}); });
it('allows the user to provide an attribute definition as foreignKey', function () { describe('allows the user to provide an attribute definition object as foreignKey', function () {
var Task = this.sequelize.define('task', {}) it('works with a column that hasnt been defined before', function () {
var Task = this.sequelize.define('task', {})
, User = this.sequelize.define('user', {});
User.hasMany(Task, {
foreignKey: {
name: 'uid',
allowNull: false
}
});
expect(Task.rawAttributes.uid).to.be.defined;
expect(Task.rawAttributes.uid.allowNull).to.be.false;
expect(Task.rawAttributes.uid.references).to.equal(User.getTableName());
expect(Task.rawAttributes.uid.referencesKey).to.equal('id');
Task.hasMany(User, {
foreignKey: {
allowNull: false
}
});
expect(Task.rawAttributes.uid).not.to.be.defined;
expect(Task.associations.tasksusers.through.rawAttributes.taskId).to.be.defined;
expect(Task.associations.tasksusers.through.rawAttributes.taskId.allowNull).to.be.false;
expect(Task.associations.tasksusers.through.rawAttributes.taskId.references).to.equal(Task.getTableName());
expect(Task.associations.tasksusers.through.rawAttributes.taskId.referencesKey).to.equal('id');
expect(Task.associations.tasksusers.through.rawAttributes.uid).to.be.defined;
expect(Task.associations.tasksusers.through.rawAttributes.uid.allowNull).to.be.false;
expect(Task.associations.tasksusers.through.rawAttributes.uid.references).to.equal(User.getTableName());
expect(Task.associations.tasksusers.through.rawAttributes.uid.referencesKey).to.equal('id');
});
it('works when taking a column directly from the object', function () {
var Project = this.sequelize.define('project', {
user_id: {
type: Sequelize.INTEGER,
defaultValue: 42
}
})
, User = this.sequelize.define('user', { , User = this.sequelize.define('user', {
uid: { uid: {
type: Sequelize.INTEGER, type: Sequelize.INTEGER,
...@@ -2219,33 +2256,29 @@ describe(Support.getTestDialectTeaser("HasMany"), function() { ...@@ -2219,33 +2256,29 @@ describe(Support.getTestDialectTeaser("HasMany"), function() {
} }
}); });
User.hasMany(Task, { User.hasMany(Project, { foreignKey: Project.rawAttributes.user_id});
foreignKey: {
fieldName: 'user_id',
allowNull: false
}
});
expect(Task.rawAttributes.user_id.allowNull).to.be.false;
Task.hasMany(User, { expect(Project.rawAttributes.user_id).to.be.defined;
foreignKey: { expect(Project.rawAttributes.user_id.references).to.equal(User.getTableName());
allowNull: false expect(Project.rawAttributes.user_id.referencesKey).to.equal('uid');
} expect(Project.rawAttributes.user_id.defaultValue).to.equal(42);
}); });
expect(Task.associations.tasksusers.through.rawAttributes.taskId.allowNull).to.be.false; it('works when merging with an existing definition', function () {
var Task = this.sequelize.define('task', {
var Project = this.sequelize.define('project', { userId: {
user_id: { defaultValue: 42,
type: Sequelize.INTEGER type: Sequelize.INTEGER
} }
}); })
, User = this.sequelize.define('user', {});
User.hasMany(Project, { foreignKey: Project.rawAttributes.user_id}); User.hasMany(Task, { foreignKey: { allowNull: true }});
expect(Project.rawAttributes.user_id.references).to.equal(User.getTableName()); expect(Task.rawAttributes.userId).to.be.defined;
expect(Project.rawAttributes.user_id.referencesKey).to.equal('uid'); expect(Task.rawAttributes.userId.defaultValue).to.equal(42);
expect(Task.rawAttributes.userId.allowNull).to.be.ok;
});
}); });
it('should throw an error if foreignKey and as result in a name clash', function () { it('should throw an error if foreignKey and as result in a name clash', function () {
......
...@@ -471,27 +471,84 @@ describe(Support.getTestDialectTeaser("HasOne"), function() { ...@@ -471,27 +471,84 @@ describe(Support.getTestDialectTeaser("HasOne"), function() {
}) })
}) })
it('allows the user to provide an attribute definition as foreignKey', function () { describe('allows the user to provide an attribute definition object as foreignKey', function () {
var User = this.sequelize.define('user', { it('works with a column that hasnt been defined before', function () {
var User = this.sequelize.define('user', {})
, Profile = this.sequelize.define('project', {})
User.hasOne(Profile, {
foreignKey: {
allowNull: false,
name: 'uid'
}
});
expect(Profile.rawAttributes.uid).to.be.defined
expect(Profile.rawAttributes.uid.references).to.equal(User.getTableName())
expect(Profile.rawAttributes.uid.referencesKey).to.equal('id')
expect(Profile.rawAttributes.uid.allowNull).to.be.false;
// Let's clear it
Profile = this.sequelize.define('project', {})
User.hasOne(Profile, {
foreignKey: {
allowNull: false,
name: 'uid',
}
});
expect(Profile.rawAttributes.uid).to.be.defined
expect(Profile.rawAttributes.uid.references).to.equal(User.getTableName())
expect(Profile.rawAttributes.uid.referencesKey).to.equal('id')
expect(Profile.rawAttributes.uid.allowNull).to.be.false;
});
it('works when taking a column directly from the object', function () {
var User = this.sequelize.define('user', {
uid: {
type: Sequelize.INTEGER,
primaryKey: true
}
})
, Profile = this.sequelize.define('project', {
user_id: {
type: Sequelize.INTEGER,
allowNull: false
}
})
User.hasOne(Profile, { foreignKey: Profile.rawAttributes.user_id})
console.log(Profile.rawAttributes.user_id);
expect(Profile.rawAttributes.user_id).to.be.defined
expect(Profile.rawAttributes.user_id.references).to.equal(User.getTableName())
expect(Profile.rawAttributes.user_id.referencesKey).to.equal('uid')
expect(Profile.rawAttributes.user_id.allowNull).to.be.false;
});
it('works when merging with an existing definition', function () {
var User = this.sequelize.define('user', {
uid: { uid: {
type: Sequelize.INTEGER, type: Sequelize.INTEGER,
primaryKey: true primaryKey: true
} }
}) })
, Profile = this.sequelize.define('project', { , Project = this.sequelize.define('project', {
user_id: { userUid: {
type: Sequelize.INTEGER, type: Sequelize.INTEGER,
allowNull: false defaultValue: 42
} }
}) });
User.hasOne(Profile, { foreignKey: Profile.rawAttributes.user_id}) User.hasOne(Project, { foreignKey: { allowNull: false }});
expect(Profile.rawAttributes.user_id).to.be.defined expect(Project.rawAttributes.userUid).to.be.defined;
expect(Profile.rawAttributes.user_id.references).to.equal(User.getTableName()) expect(Project.rawAttributes.userUid.allowNull).to.be.false;
expect(Profile.rawAttributes.user_id.referencesKey).to.equal('uid') expect(Project.rawAttributes.userUid.references).to.equal(User.getTableName())
expect(Profile.rawAttributes.user_id.allowNull).to.be.false expect(Project.rawAttributes.userUid.referencesKey).to.equal('uid')
}) expect(Project.rawAttributes.userUid.defaultValue).to.equal(42);
});
});
it('should throw an error if an association clashes with the name of an already define attribute', function () { it('should throw an error if an association clashes with the name of an already define attribute', function () {
var User = this.sequelize.define('user', { var User = this.sequelize.define('user', {
......
...@@ -284,32 +284,30 @@ describe(Support.getTestDialectTeaser("DAOFactory"), function () { ...@@ -284,32 +284,30 @@ describe(Support.getTestDialectTeaser("DAOFactory"), function () {
User.belongsTo(Binary, { foreignKey: 'binary' }) User.belongsTo(Binary, { foreignKey: 'binary' })
User.sync({ force: true }).success(function() { this.sequelize.sync({ force: true }).success(function() {
Binary.sync({ force: true }).success(function() { User.bulkCreate([
User.bulkCreate([ {username: 'boo5', aBool: false},
{username: 'boo5', aBool: false}, {username: 'boo6', aBool: true}
{username: 'boo6', aBool: true} ]).success(function() {
Binary.bulkCreate([
{id: buf1},
{id: buf2}
]).success(function() { ]).success(function() {
Binary.bulkCreate([ User.find(1).success(function(user) {
{id: buf1}, Binary.find(buf1).success(function(binary) {
{id: buf2} user.setBinary(binary).success(function() {
]).success(function() { User.find(2).success(function(_user) {
User.find(1).success(function(user) { Binary.find(buf2).success(function(_binary) {
Binary.find(buf1).success(function(binary) { _user.setBinary(_binary).success(function() {
user.setBinary(binary).success(function() { _user.getBinary().success(function(_binaryRetrieved) {
User.find(2).success(function(_user) { user.getBinary().success(function(binaryRetrieved) {
Binary.find(buf2).success(function(_binary) { expect(binaryRetrieved.id).to.be.an.instanceof.string
_user.setBinary(_binary).success(function() { expect(_binaryRetrieved.id).to.be.an.instanceof.string
_user.getBinary().success(function(_binaryRetrieved) { expect(binaryRetrieved.id).to.have.length(16)
user.getBinary().success(function(binaryRetrieved) { expect(_binaryRetrieved.id).to.have.length(16)
expect(binaryRetrieved.id).to.be.an.instanceof.string expect(binaryRetrieved.id.toString()).to.be.equal(buf1.toString())
expect(_binaryRetrieved.id).to.be.an.instanceof.string expect(_binaryRetrieved.id.toString()).to.be.equal(buf2.toString())
expect(binaryRetrieved.id).to.have.length(16) done()
expect(_binaryRetrieved.id).to.have.length(16)
expect(binaryRetrieved.id.toString()).to.be.equal(buf1.toString())
expect(_binaryRetrieved.id.toString()).to.be.equal(buf2.toString())
done()
})
}) })
}) })
}) })
......
...@@ -933,8 +933,6 @@ describe(Support.getTestDialectTeaser("Include"), function () { ...@@ -933,8 +933,6 @@ describe(Support.getTestDialectTeaser("Include"), function () {
}) })
}) })
it("should be possible to define a belongsTo include as required with child hasMany not required", function(done) { it("should be possible to define a belongsTo include as required with child hasMany not required", function(done) {
var S = this.sequelize var S = this.sequelize
, Address = S.define('Address', { 'active': DataTypes.BOOLEAN }) , Address = S.define('Address', { 'active': DataTypes.BOOLEAN })
......
...@@ -29,13 +29,13 @@ describe(Support.getTestDialectTeaser("Include"), function () { ...@@ -29,13 +29,13 @@ describe(Support.getTestDialectTeaser("Include"), function () {
User.hasMany( SomeConnection, { foreignKey: 'u' }) User.hasMany( SomeConnection, { foreignKey: 'u' })
SomeConnection.belongsTo( User, { foreignKey: 'u' }) SomeConnection.belongsTo( User, { foreignKey: 'u' })
SomeConnection.belongsTo( A, { foreignKey: 'fk' }) SomeConnection.belongsTo( A, { foreignKey: 'fk', constraints: false })
SomeConnection.belongsTo( B, { foreignKey: 'fk' }) SomeConnection.belongsTo( B, { foreignKey: 'fk', constraints: false })
SomeConnection.belongsTo( C, { foreignKey: 'fk' }) SomeConnection.belongsTo( C, { foreignKey: 'fk', constraints: false })
A.hasMany( SomeConnection, { foreignKey: 'fk' }) A.hasMany( SomeConnection, { foreignKey: 'fk', constraints: false })
B.hasMany( SomeConnection, { foreignKey: 'fk' }) B.hasMany( SomeConnection, { foreignKey: 'fk', constraints: false })
C.hasMany( SomeConnection, { foreignKey: 'fk' }) C.hasMany( SomeConnection, { foreignKey: 'fk', constraints: false })
// Sync them // Sync them
S.sync({ force: true }).done( function ( err ) { expect( err ).not.to.be.ok; S.sync({ force: true }).done( function ( err ) { expect( err ).not.to.be.ok;
...@@ -142,4 +142,4 @@ describe(Support.getTestDialectTeaser("Include"), function () { ...@@ -142,4 +142,4 @@ describe(Support.getTestDialectTeaser("Include"), function () {
}) })
}) })
}) })
\ No newline at end of file
Markdown is supported
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!