简体   繁体   English

Sequelize.js:包括意外。 元素必须是模型、关联或对象

[英]Sequelize.js: Include unexpected. Element has to be either a Model, an Association or an object

I'm using Sequelize.js in my Node.js application and keep running into a very strange problem.我在我的 Node.js 应用程序中使用 Sequelize.js 并不断遇到一个非常奇怪的问题。

Background: I have two models, Account and AccountCategory as follows.背景:我有两个模型, AccountAccountCategory ,如下所示。 My API endpoint calls the route /accounts which calls the accounts controller to do an Account.findAll() query.我的 API 端点调用路由/accounts调用帐户控制器来执行Account.findAll()查询。

Accounts model has a defaultScope to include the related category by default, without having to specify it each time inside the findAll({}) block. Accounts模型有一个defaultScope以默认包含相关类别,而不必每次都在findAll({})块中指定它。

Problem: When the Accounts model is attempting to access and return the data from the database, the defaultScope is trying to include the AccountCategory , Sequelize throws the error:问题:Accounts模型试图访问并返回数据库中的数据时, defaultScope试图包含AccountCategory ,Sequelize 抛出错误:

Include unexpected.包括意外。 Element has to be either a Model, an Association or an object.元素必须是模型、关联或对象。

I suspect it has to do with the fact that AccountCategory is placed after Account in my models folder when the models are being set up and thus not processed (associated).我怀疑这与在设置模型时AccountCategory放在我的模型文件夹中的Account之后的事实有关,因此未处理(关联)。 I base this on the fact that other associations like User and Role (ie. a user has a role) are fine using the same method (ie. no problem with path depth as this answer suggests).我基于这样一个事实,即用户角色(即用户具有角色)等其他关联可以使用相同的方法(即,路径深度没有问题,正如这个答案所暗示的那样)。

I've spent the last 2 days trying to get the defaultScope working and stop producing this error without any luck.在过去的 2 天里,我一直试图让defaultScope正常工作并停止产生这个错误,但没有任何运气。 Similar questions do not provide an answer and I would greatly appreciate any help resolving this problem.类似的问题没有提供答案,我将非常感谢解决此问题的任何帮助。 Thanks.谢谢。

Account:帐户:

module.exports = (sequelize, DataTypes) => {
    const Account = sequelize.define(
        "Account",
        {
            id: {
                type: DataTypes.INTEGER(11),
                allowNull: false,
                primaryKey: true,
                autoIncrement: true
            },
            name: {
                type: DataTypes.STRING(100)
            },
            category_id: {
                type: DataTypes.INTEGER(11),
                allowNull: false
            }
        },
        {
            timestamps: false,
            tableName: "Account",
            defaultScope: {
                include: [{
                    model: sequelize.models.AccountCategory,
                    as: "category"
                }]
            }
        }
    );

    Account.associate = models => {
        // Association: Account -> AccountCategory
        Account.belongsTo(models.AccountCategory, {
            onDelete: "CASCADE",
            foreignKey: {
                fieldName: "category_id",
                allowNull: false,
                require: true
            },
            targetKey: "id",
            as: "category"
        });
    };

    return Account;
};

Account Category:账户类别:

module.exports = (sequelize, DataTypes) => {
    var AccountCategory = sequelize.define(
        "AccountCategory",
        {
            id: {
                type: DataTypes.INTEGER(11),
                allowNull: false,
                primaryKey: true,
                autoIncrement: true
            },
            name: {
                type: DataTypes.STRING(30),
                allowNull: false,
                unique: true
            }
        },
        {
            timestamps: false,
            tableName: "Account_Category"
        }
    );

    return AccountCategory;
};

Models Index:型号索引:

const fs = require("fs");
const path = require("path");
const Sequelize = require("sequelize");
const basename = path.basename(__filename);
const env = process.env.NODE_ENV || "development";
const db = {};

const sequelize = new Sequelize(
    process.env.DB_NAME,
    process.env.DB_USER,
    process.env.DB_PASS,
    {
        host: process.env.DB_HOST,
        dialect: "mysql",
        operatorAliases: false,

        pool: {
            max: 5,
            min: 0,
            acquire: 30000,
            idle: 10000
        }
    }
);

fs.readdirSync(__dirname)
    .filter(function(file) {
        return (
            file.indexOf(".") !== 0 && file !== basename && file.slice(-3) === ".js"
        );
    })
    .forEach(function(file) {
        var model = sequelize["import"](path.join(__dirname, file));
        db[model.name] = model;
    });

Object.keys(db).forEach(function(modelName) {
    if (db[modelName].associate) {
        db[modelName].associate(db);
    }
    db[modelName].associate(db);
});

db.sequelize = sequelize;
db.Sequelize = Sequelize;

module.exports = db;

You should try to define include by alias 您应该尝试通过别名定义包含

defaultScope: {
  include: 'category'
}

Works for me in sequelize 5+ 连续为我工作5+

You are correct when you say:当你说:

I suspect it has to do with the fact that AccountCategory is placed after Account in my models folder when the models are being set up and thus not processed (associated).我怀疑这与在设置模型时 AccountCategory 放在我的模型文件夹中的 Account 之后的事实有关,因此未处理(关联)。

TLDR: Add a new function to your model class definition similar to the associate function, and use the addScope function to define any scopes that reference other models that may have not been initialized due to file tree order. TLDR:在模型类定义中添加一个类似于associate函数的新函数,并使用addScope函数定义引用其他模型的任何作用域,这些模型可能由于文件树顺序而未初始化。 Finally, call that new function the same way you call db[modelName].associate in your models.index.js file.最后,以与在models.index.js文件中调用db[modelName].associate相同的方式调用该新函数。

I had a similar problem and solved it by defining any scopes that reference any models, eg in an include , after all the models are initialized after running the following in your models/index.js file.我有一个类似的问题,并通过定义引用任何模型的任何范围来解决它,例如在include中,在所有模型在您的models/index.js文件中运行以下内容后初始化之后。

Here is an example:这是一个例子:

models/agent.js模型/agent.js

'use strict';
const { Model } = require('sequelize');
const camelCase = require('lodash/camelCase');
const { permissionNames } = require('../../api/constants/permissions');

module.exports = (sequelize, DataTypes) => {
  /**
   * @summary Agent model
   */
  class Agent extends Model {}

  Agent.init(
    {
      id: {
        type: DataTypes.INTEGER,
        allowNull: false,
        autoIncrement: true,
        primaryKey: true,
      },
      firstName: {
        type: DataTypes.STRING,
        allowNull: false,
      },
      lastName: {
        type: DataTypes.STRING,
        allowNull: false,
      },
    },
    {
      sequelize,
      scopes: {
        // Works because the agent-role.js file / model comes before agent.js in the file tree
        [camelCase(permissionNames.readAgentRoles)]: {
          include: [
            {
              model: sequelize.models.AgentRole,
            },
          ],
        },
        // Doesn't work due to import order in models/index.js, i.e., agent.js is before role.js in the file tree
        // [camelCase(permissionNames.readRoles)]: {
        //   include: [
        //     {
        //       model: sequelize.models.Role,
        //     },
        //   ],
        // },
      },
    }
  );

  Agent.associate = function (models) {
    Agent.belongsToMany(models.Role, {
      through: 'AgentRole',
      onDelete: 'CASCADE', // default for belongsToMany
      onUpdate: 'CASCADE', // default for belongsToMany
      foreignKey: {
        name: 'agentId',
        type: DataTypes.INTEGER,
        allowNull: false,
      },
    });
    Agent.hasMany(models.AgentRole, {
      onDelete: 'CASCADE',
      onUpdate: 'CASCADE',
      foreignKey: {
        name: 'agentId',
        type: DataTypes.INTEGER,
        allowNull: false,
      },
    });
  };

  // Add a custom `addScopes` function to call after initializing all models in `index.js`
  Agent.addScopes = function (models) {
    Agent.addScope(camelCase(permissionNames.readRoles), {
      include: [
        {
          model: models.Role,
        },
      ],
    });
  };

  return Agent;
};

models/index.js模型/index.js

'use strict';

const fs = require('fs');
const path = require('path');
const Sequelize = require('sequelize');
const basename = path.basename(__filename);
const config = require('../database-config.js');
const db = {};

const sequelize = new Sequelize(config.database, config.username, config.password, config);

/**
 * Import and attach all of the model definitions within this 'models' directory to the sequelize instance.
 */
fs.readdirSync(__dirname)
  .filter((file) => {
    return file.indexOf('.') !== 0 && file !== basename && file.slice(-3) === '.js';
  })
  .forEach((file) => {
    // Here is where file tree order matters... the sequelize const may not have the required model added to it yet
    const model = require(path.join(__dirname, file))(sequelize, Sequelize.DataTypes);
    db[model.name] = model;
  });

Object.keys(db).forEach((modelName) => {
  if (db[modelName].associate) {
    db[modelName].associate(db);
  }
  // We need to add scopes that reference other tables once they have all been initialized
  if (db[modelName].addScopes) {
    db[modelName].addScopes(db);
  }
});

db.sequelize = sequelize;
db.Sequelize = Sequelize;

module.exports = db;

Goodluck!祝你好运!

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM