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

Commit e0e21726 by Alexander Mochalin Committed by Felix Becker

Include: expand alias into association (#6280)

1 parent 6eefaf5c
# Future # Future
- [ADDED] include now supports string as an argument (on top of model/association), string will expand into an association matched literally from Model.associations
- [FIXED] Accept dates as string while using `typeValidation` [#6453](https://github.com/sequelize/sequelize/issues/6453) - [FIXED] Accept dates as string while using `typeValidation` [#6453](https://github.com/sequelize/sequelize/issues/6453)
# 4.0.0-1 # 4.0.0-1
......
...@@ -260,7 +260,7 @@ The success listener is called with an array of instances if the query succeeds. ...@@ -260,7 +260,7 @@ The success listener is called with an array of instances if the query succeeds.
| [options.attributes.include] | Array.<String> | Select all the attributes of the model, plus some additional ones. Useful for aggregations, e.g. `{ attributes: { include: [[sequelize.fn('COUNT', sequelize.col('id')), 'total']] }` | | [options.attributes.include] | Array.<String> | Select all the attributes of the model, plus some additional ones. Useful for aggregations, e.g. `{ attributes: { include: [[sequelize.fn('COUNT', sequelize.col('id')), 'total']] }` |
| [options.attributes.exclude] | Array.<String> | Select all the attributes of the model, except some few. Useful for security purposes e.g. `{ attributes: { exclude: ['password'] } }` | | [options.attributes.exclude] | Array.<String> | Select all the attributes of the model, except some few. Useful for security purposes e.g. `{ attributes: { exclude: ['password'] } }` |
| [options.paranoid=true] | Boolean | If true, only non-deleted records will be returned. If false, both deleted and non-deleted records will be returned. Only applies if `options.paranoid` is true for the model. | | [options.paranoid=true] | Boolean | If true, only non-deleted records will be returned. If false, both deleted and non-deleted records will be returned. Only applies if `options.paranoid` is true for the model. |
| [options.include] | Array.<Object | Model> | A list of associations to eagerly load using a left join. Supported is either `{ include: [ Model1, Model2, ...]}` or `{ include: [{ model: Model1, as: 'Alias' }]}`. If your association are set up with an `as` (eg. `X.hasMany(Y, { as: 'Z }`, you need to specify Z in the as attribute when eager loading Y). | | [options.include] | Array.<Object | Model | String> | A list of associations to eagerly load using a left join. Supported is either `{ include: [ Model1, Model2, ...]}` or `{ include: [{ model: Model1, as: 'Alias' }, ...]}` or `{ include: ['Alias', ...]}`. If your association are set up with an `as` (eg. `X.hasMany(Y, { as: 'Z }`, you need to specify Z in the as attribute when eager loading Y). |
| [options.include[].model] | Model | The model you want to eagerly load | | [options.include[].model] | Model | The model you want to eagerly load |
| [options.include[].as] | String | The alias of the relation, in case the model you want to eagerly load is aliased. For `hasOne` / `belongsTo`, this should be the singular name, and for `hasMany`, it should be the plural | | [options.include[].as] | String | The alias of the relation, in case the model you want to eagerly load is aliased. For `hasOne` / `belongsTo`, this should be the singular name, and for `hasMany`, it should be the plural |
| [options.include[].association] | Association | The association you want to eagerly load. (This can be used instead of providing a model/as pair) | | [options.include[].association] | Association | The association you want to eagerly load. (This can be used instead of providing a model/as pair) |
...@@ -273,7 +273,7 @@ The success listener is called with an array of instances if the query succeeds. ...@@ -273,7 +273,7 @@ The success listener is called with an array of instances if the query succeeds.
| [options.include[].limit] | Number | Limit the joined rows, only supported with include.separate=true | | [options.include[].limit] | Number | Limit the joined rows, only supported with include.separate=true |
| [options.include[].through.where] | Object | Filter on the join model for belongsToMany relations | | [options.include[].through.where] | Object | Filter on the join model for belongsToMany relations |
| [options.include[].through.attributes] | Array | A list of attributes to select from the join model for belongsToMany relations | | [options.include[].through.attributes] | Array | A list of attributes to select from the join model for belongsToMany relations |
| [options.include[].include] | Array.<Object | Model> | Load further nested related models | | [options.include[].include] | Array.<Object | Model | String> | Load further nested related models |
| [options.order] | String | Array | Sequelize.fn | Specifies an ordering. If a string is provided, it will be escaped. Using an array, you can provide several columns / functions to order by. Each element can be further wrapped in a two-element array. The first element is the column / function to order by, the second is the direction. For example: `order: [['name', 'DESC']]`. In this way the column will be escaped, but the direction will not. | | [options.order] | String | Array | Sequelize.fn | Specifies an ordering. If a string is provided, it will be escaped. Using an array, you can provide several columns / functions to order by. Each element can be further wrapped in a two-element array. The first element is the column / function to order by, the second is the direction. For example: `order: [['name', 'DESC']]`. In this way the column will be escaped, but the direction will not. |
| [options.limit] | Number | | | [options.limit] | Number | |
| [options.offset] | Number | | | [options.offset] | Number | |
......
...@@ -501,6 +501,30 @@ User.findAll({ include: [{ model: Tool, as: 'Instruments' }] }).then(function(us ...@@ -501,6 +501,30 @@ User.findAll({ include: [{ model: Tool, as: 'Instruments' }] }).then(function(us
}) })
``` ```
You can also include by alias name by specifying a string that matches the association alias:
```js
User.findAll({ include: ['Instruments'] }).then(function(users) {
console.log(JSON.stringify(users))
/*
[{
"name": "John Doe",
"id": 1,
"createdAt": "2013-03-20T20:31:45.000Z",
"updatedAt": "2013-03-20T20:31:45.000Z",
"Instruments": [{
"name": "Toothpick",
"id": 1,
"createdAt": null,
"updatedAt": null,
"userId": 1
}]
}]
*/
})
```
When eager loading we can also filter the associated model using `where`. This will return all `User`s in which the `where` clause of `Tool` model matches rows. When eager loading we can also filter the associated model using `where`. This will return all `User`s in which the `where` clause of `Tool` model matches rows.
```js ```js
......
...@@ -187,6 +187,13 @@ class Model { ...@@ -187,6 +187,13 @@ class Model {
if (include._pseudo) return include; if (include._pseudo) return include;
if (self && typeof include === 'string') {
if (!self.associations.hasOwnProperty(include)) {
throw new Error('Association with alias "' + include + '" does not exists');
}
include = self.associations[include];
}
if (include instanceof Association) { if (include instanceof Association) {
if (self && include.target.name === self.name) { if (self && include.target.name === self.name) {
model = include.source; model = include.source;
...@@ -1369,7 +1376,7 @@ class Model { ...@@ -1369,7 +1376,7 @@ class Model {
* @param {Array<String>} [options.attributes.include] Select all the attributes of the model, plus some additional ones. Useful for aggregations, e.g. `{ attributes: { include: [[sequelize.fn('COUNT', sequelize.col('id')), 'total']] }` * @param {Array<String>} [options.attributes.include] Select all the attributes of the model, plus some additional ones. Useful for aggregations, e.g. `{ attributes: { include: [[sequelize.fn('COUNT', sequelize.col('id')), 'total']] }`
* @param {Array<String>} [options.attributes.exclude] Select all the attributes of the model, except some few. Useful for security purposes e.g. `{ attributes: { exclude: ['password'] } }` * @param {Array<String>} [options.attributes.exclude] Select all the attributes of the model, except some few. Useful for security purposes e.g. `{ attributes: { exclude: ['password'] } }`
* @param {Boolean} [options.paranoid=true] If true, only non-deleted records will be returned. If false, both deleted and non-deleted records will be returned. Only applies if `options.paranoid` is true for the model. * @param {Boolean} [options.paranoid=true] If true, only non-deleted records will be returned. If false, both deleted and non-deleted records will be returned. Only applies if `options.paranoid` is true for the model.
* @param {Array<Object|Model>} [options.include] A list of associations to eagerly load using a left join. Supported is either `{ include: [ Model1, Model2, ...]}` or `{ include: [{ model: Model1, as: 'Alias' }]}`. If your association are set up with an `as` (eg. `X.hasMany(Y, { as: 'Z }`, you need to specify Z in the as attribute when eager loading Y). * @param {Array<Object|Model|String>}[options.include] A list of associations to eagerly load using a left join. Supported is either `{ include: [ Model1, Model2, ...]}` or `{ include: [{ model: Model1, as: 'Alias' }]}` or `{ include: ['Alias']}`. If your association are set up with an `as` (eg. `X.hasMany(Y, { as: 'Z }`, you need to specify Z in the as attribute when eager loading Y).
* @param {Model} [options.include[].model] The model you want to eagerly load * @param {Model} [options.include[].model] The model you want to eagerly load
* @param {String} [options.include[].as] The alias of the relation, in case the model you want to eagerly load is aliased. For `hasOne` / `belongsTo`, this should be the singular name, and for `hasMany`, it should be the plural * @param {String} [options.include[].as] The alias of the relation, in case the model you want to eagerly load is aliased. For `hasOne` / `belongsTo`, this should be the singular name, and for `hasMany`, it should be the plural
* @param {Association} [options.include[].association] The association you want to eagerly load. (This can be used instead of providing a model/as pair) * @param {Association} [options.include[].association] The association you want to eagerly load. (This can be used instead of providing a model/as pair)
...@@ -1382,7 +1389,7 @@ class Model { ...@@ -1382,7 +1389,7 @@ class Model {
* @param {Number} [options.include[].limit] Limit the joined rows, only supported with include.separate=true * @param {Number} [options.include[].limit] Limit the joined rows, only supported with include.separate=true
* @param {Object} [options.include[].through.where] Filter on the join model for belongsToMany relations * @param {Object} [options.include[].through.where] Filter on the join model for belongsToMany relations
* @param {Array} [options.include[].through.attributes] A list of attributes to select from the join model for belongsToMany relations * @param {Array} [options.include[].through.attributes] A list of attributes to select from the join model for belongsToMany relations
* @param {Array<Object|Model>} [options.include[].include] Load further nested related models * @param {Array<Object|Model|String>}[options.include[].include] Load further nested related models
* @param {String|Array|Sequelize.fn} [options.order] Specifies an ordering. If a string is provided, it will be escaped. Using an array, you can provide several columns / functions to order by. Each element can be further wrapped in a two-element array. The first element is the column / function to order by, the second is the direction. For example: `order: [['name', 'DESC']]`. In this way the column will be escaped, but the direction will not. * @param {String|Array|Sequelize.fn} [options.order] Specifies an ordering. If a string is provided, it will be escaped. Using an array, you can provide several columns / functions to order by. Each element can be further wrapped in a two-element array. The first element is the column / function to order by, the second is the direction. For example: `order: [['name', 'DESC']]`. In this way the column will be escaped, but the direction will not.
* @param {Number} [options.limit] * @param {Number} [options.limit]
* @param {Number} [options.offset] * @param {Number} [options.offset]
......
...@@ -253,6 +253,21 @@ describe(Support.getTestDialectTeaser('Model'), function() { ...@@ -253,6 +253,21 @@ describe(Support.getTestDialectTeaser('Model'), function() {
}); });
}); });
describe('_conformInclude: string alias', function () {
it('should expand association from string alias', function () {
const options = {
include: ['Owner']
};
Sequelize.Model._conformOptions(options, this.Company);
expect(options.include[0]).to.deep.equal({
model: this.User,
association: this.Company.Owner,
as: 'Owner'
});
});
});
describe('subQuery', function () { describe('subQuery', function () {
it('should be true if theres a duplicating association', function () { it('should be true if theres a duplicating association', function () {
var options = Sequelize.Model._validateIncludedElements({ var options = Sequelize.Model._validateIncludedElements({
......
Markdown is supported
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!