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

标签 mysql node.js sequelize.js associations

我在我的 Node.js 应用程序中使用 Sequelize.js 并一直遇到一个非常奇怪的问题。

背景:我有两个模型,AccountAccountCategory,如下所示。我的 API 端点调用路由 /accounts,它调用帐户 Controller 来执行 Account.findAll() 查询。

Accounts 模型有一个 defaultScope 来默认包含相关类别,而不必每次都在 findAll({}) 中指定它> 阻止。

问题:当 Accounts 模型试图从数据库访问和返回数据时,defaultScope 试图包含 < em>AccountCategory,Sequelize 抛出错误:

Include unexpected. Element has to be either a Model, an Association or an object.

我怀疑这与以下事实有关:在设置模型时,AccountCategory 位于我的 models 文件夹中的 Account 之后因此未处理(关联)。我基于这样一个事实,即 UserRole(即用户具有角色)等其他关联可以使用相同的方法(即路径深度没有问题)正如 this answer 建议的那样)。

过去 2 天我一直在尝试让 defaultScope 正常工作并停止产生此错误,但没有任何运气。类似的问题没有提供答案,如果能帮助解决这个问题,我将不胜感激。谢谢。

账号:

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;
};

账户类别:

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;
};

模型索引:

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;

最佳答案

你说的是对的:

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).

TLDR:向您的模型类定义添加一个类似于 associate 函数的新函数,并使用 addScope函数定义引用其他模型的任何范围,这些模型可能由于文件树顺序而尚未初始化。最后,以与在 models.index.js 文件中调用 db[modelName].associate 相同的方式调用该新函数。

我有一个类似的问题并通过定义引用任何模型的任何范围来解决它,例如在 include 中,在 models/index.js 文件中运行以下命令后所有模型都已初始化。

这是一个例子:

models/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

'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;

祝你好运!

关于mysql - Sequelize.js:包括意外。元素必须是模型、关联或对象,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/55896380/

相关文章:

node.js - 是否可以在不定义关系的情况下加入 Sequelize 中的表?

mysql - 无法重命名mysql表

mysql - 在没有条件的情况下,如何使查询不返回任何内容?

mysql - 在 SQL 中查找 15 天的桶中的所有事件用户

javascript - 将文本格式添加到我的 NodeJS 应用程序中的评论部分

node.js - instagram 范围不适用于 Passport

javascript - new Date() 更改小时问题_ JavaScript

php - 如何使用 jointable 进行 dql 查询?

c++ - Node 插件 (OSX) 中的 GraphicsMagick 代码卡住

mysql - 如何返回 Sequelize 中树 Node 的子 Node 数?