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

Commit 6c7ec12f by Mick Hansen

feat(associations): add 1:M getters for associations with scopes

1 parent bce7e323
......@@ -12,11 +12,19 @@ module.exports = (function() {
};
HasManySingleLinked.prototype.injectGetter = function(options, queryOptions) {
var scopeWhere = this.association.scope ? {} : null;
if (this.association.scope) {
Object.keys(this.association.scope).forEach(function (attribute) {
scopeWhere[attribute] = this.association.scope[attribute];
}.bind(this));
}
options.where = new Utils.and([
new Utils.where(
this.target.rawAttributes[this.association.identifier],
this.instance[this.source.primaryKeyAttribute])
,
this.instance[this.source.primaryKeyAttribute]
),
scopeWhere,
options.where
]);
......@@ -92,7 +100,7 @@ module.exports = (function() {
update[attribute] = this.association.scope[attribute];
}.bind(this));
}
updateWhere[primaryKey] = unassociatedIds;
promises.push(this.association.target.update(
......
......@@ -160,6 +160,7 @@ Mixin.hasOne = singleLinked(HasOne);
* @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|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 {object} [options.scope] A key/value set that will be used for association create and find defaults.
* @param {string} [options.onDelete='SET NULL']
* @param {string} [options.onUpdate='CASCADE']
* @param {boolean} [options.constraints=true] Should on update and on delete constraints be enabled on the foreign key.
......@@ -230,6 +231,7 @@ Mixin.belongsTo = singleLinked(BelongsTo);
* @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.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 {object} [options.scope] A key/value set that will be used for association create and find defaults.
* @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 {boolean} [options.constraints=true] Should on update and on delete constraints be enabled on the foreign key.
......
"use strict";
/* jshint camelcase: false, expr: true */
var chai = require('chai')
, expect = chai.expect
, Support = require(__dirname + '/../support')
, DataTypes = require(__dirname + "/../../lib/data-types")
, Sequelize = require('../../index')
, Promise = Sequelize.Promise
, assert = require('assert');
chai.config.includeStack = true;
describe(Support.getTestDialectTeaser("associations"), function() {
describe('scope', function () {
beforeEach(function () {
this.Post = this.sequelize.define('post', {});
this.Image = this.sequelize.define('image', {});
this.Question = this.sequelize.define('question', {});
this.Comment = this.sequelize.define('comment', {
title: Sequelize.STRING,
commentable: Sequelize.STRING,
commentable_id: Sequelize.INTEGER
}, {
instanceMethods: {
getItem: function() {
return this['get'+this.get('commentable').substr(0, 1).toUpperCase()+this.get('commentable').substr(1)]();
}
}
});
this.Post.hasMany(this.Comment, {
foreignKey: 'commentable_id',
scope: {
commentable: 'post'
}
});
this.Comment.belongsTo(this.Post, {
foreignKey: 'commentable_id',
as: 'post',
scope: {
commentable: 'post'
}
});
this.Image.hasMany(this.Comment, {
foreignKey: 'commentable_id',
scope: {
commentable: 'image'
}
});
this.Comment.belongsTo(this.Image, {
foreignKey: 'commentable_id',
as: 'image',
scope: {
commentable: 'image'
}
});
this.Question.hasMany(this.Comment, {
foreignKey: 'commentable_id',
scope: {
commentable: 'question'
}
});
this.Comment.belongsTo(this.Question, {
foreignKey: 'commentable_id',
as: 'question',
scope: {
commentable: 'question'
}
});
});
it('should create associations and find association with scope values', function () {
var self = this;
return this.sequelize.sync({force: true}).then(function () {
return Promise.join(
self.Post.create(),
self.Image.create(),
self.Question.create(),
self.Comment.create({
title: 'I am a image comment'
}),
self.Comment.create({
title: 'I am a question comment'
})
);
}).bind(this).spread(function (post, image, question, commentA, commentB) {
this.post = post;
this.image = image;
this.question = question;
return Promise.join(
post.createComment({
title: 'I am a post comment'
}),
image.addComment(commentA),
question.setComments([commentB])
);
}).then(function () {
return self.Comment.findAll();
}).then(function (comments) {
comments.forEach(function (comment) {
expect(comment.get('commentable')).to.be.ok;
});
expect(comments.map(function (comment) {
return comment.get('commentable');
}).sort()).to.deep.equal(['image', 'post', 'question']);
}).then(function () {
return Promise.join(
this.post.getComments(),
this.image.getComments(),
this.question.getComments()
);
}).spread(function (postComments, imageComments, questionComments) {
expect(postComments.length).to.equal(1);
expect(postComments[0].get('title')).to.equal('I am a post comment');
expect(imageComments.length).to.equal(1);
expect(imageComments[0].get('title')).to.equal('I am a image comment');
expect(questionComments.length).to.equal(1);
expect(questionComments[0].get('title')).to.equal('I am a question comment');
return [postComments[0], imageComments[0], questionComments[0]];
}).spread(function (postComment, imageComment, questionComment) {
return Promise.join(
postComment.getItem(),
imageComment.getItem(),
questionComment.getItem()
);
}).spread(function (post, image, question) {
expect(post.Model).to.equal(self.Post);
expect(image.Model).to.equal(self.Image);
expect(question.Model).to.equal(self.Question);
});
});
});
});
\ 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!