在多个文件中 Sequelize 一对多关联

Sequelize one to many assocation in multiple files

我正在处理与 sequelize 的一对多关联。大多数教程和文档都会显示在同一文件中定义两个模型时的示例。 我目前有两个文件,第一个 city.js:

const Promise = require('bluebird');
var Country = require('./country');

var City = sequelize.define("City", {
  id: {
    type: DataTypes.INTEGER,
    field: 'id',
    primaryKey: true,
    autoIncrement: true
  },...
}, {
  freezeTableName: true,
  timestamps: false
});

City.belongsTo(Country, {foreignKey : 'countryId', as: 'Country'});

Promise.promisifyAll(City);
module.exports = City;

还有第二个文件country.js:

const Promise = require('bluebird');
var City = require('./city');

var Country = sequelize.define("Country", {
  id: {
    type: DataTypes.INTEGER,
    field: 'id',
    primaryKey: true,
    autoIncrement: true
  },
  ...
}, {
  freezeTableName: true,
  timestamps: false,
  paranoid: false
});

Country.hasMany(City, {foreignKey : 'countryId', as: 'Cities'});

Promise.promisifyAll(Country);
module.exports = Country;

当我导入两个模块并尝试实例化对象时:

var City = require('../model/external/city');
var CountryRepository = require('../repository/external/countryRepository');

CountryRepository.findById(1).then(function(country) {
    var city = City.build();
    city.name = 'Paris';
    city.setCountry(country);
    console.log('OK');
});

我收到以下错误:

throw new Error(this.name + '.' + Utils.lowercaseFirst(Type.toString()) + ' called with something that\'s not an instance of Sequelize.Model')

问题是模型在从模型导出之前就已经被 promified 了还是我遗漏了什么?

我不确定您的代码到底有什么问题,需要 运行 才能确定。

但是当您正在寻找示例时,请查看 Sequelize Github 中的示例。

它在不同的文件中声明模型并在 index.js 中关联它们。

稍后您可以使用简单的 model 属性引用您的其他模型 model.Country:

City.belongsTo(model.Country, {foreignKey : 'countryId', as: 'Country'});

例如,user.js.

您必须在导出所有模型后声明关联

我遇到了同样的问题!

我创造了一种我认为非常好的技术,甚至允许跨外键使用复合唯一键。

我将举一个基本的例子 user.js 模型 class 文件

models/user.js

const { Model, DataTypes } = require('sequelize');
module.exports = function(sequelize){
    class User extends Model {}
    return User.init({
      id: {
        type: DataTypes.INTEGER,
        primaryKey: true,
        autoIncrement: true
      },
      name: {
          type: DataTypes.STRING,
          allowNull: false
      },
      trading_system_key: {
          type: DataTypes.STRING,
          allowNull: false
      },
    }, {
      sequelize,
      modelName: 'User',
      indexes: [{ unique: true, fields: ['trading_system_key'] }]
    });
};

这显示了用户 class 的一个简单示例,尚未关联,但下一个文件将使用它。

models/algorithm.js

const { Model, DataTypes } = require('sequelize');
const User = require('../models/user');

module.exports = function(sequelize){
    class Algorithm extends Model {}
    UserModel = User(sequelize);//@JA - Gets a defined version of user class

    var AlgorithmFrame = Algorithm.init({
        id: {
            type: DataTypes.INTEGER,
            primaryKey: true,
            autoIncrement: true
        },
        name: {
            type: DataTypes.STRING,
            allowNull: false,
        },
        user_Id: {
            type: DataTypes.INTEGER,
            references: { 
                model: UserModel,
                key: 'id',
            },
        }
    }, {
      sequelize,
      modelName: 'Algorithm',
      indexes: [{ unique: true, fields: ['name','user_id'] }]
    });

    return AlgorithmFrame
};

此示例展示了如何创建外键引用而不必说 belongsTo 或 HasOne 等...当您需要创建复合键时,这会派上用场。

例如,请注意我输入了 indexes: [{ unique: true, fields: ['name','user_id'] }]

这使得名称和 user_id 不能相同(复合键)。因此,例如,如果名字是 Joe 而 user_id 是 1 并且我试图创建另一个具有相同详细信息的记录它会拒绝它,但是如果我创建了名称为 Rachel 并且 user_id 1 的东西会允许它,因为它只有在两者相同时才是唯一的。

据我所知,除非您以这种方式设置,否则无法使用复合键。

此技术的另一个优点是模型位于单独的文件中并定义为 classes!

那么我该如何使用这些呢?

(在你的文件中你定义了sequelize连接...) api.js

const { Sequelize, DataTypes, Model } = require('sequelize');
const User = require('../models/user');
const Algorithm = require('../models/algorithm');

const sequelize = new Sequelize('database', 'username', 'passwordhere', {
      host: 'db',
      dialect: 'mysql' /* one of 'mysql' | 'mariadb' | 'postgres' | 'mssql' */
    });

const UserModel = User(sequelize);
const AlogorithmModel = Algorithm(sequelize);

(async () => {
      try {
        await sequelize.authenticate();
        await UserModel.sync({ alter: true });
        await AlogorithmModel.sync({ alter: true });

        //Do something with the models now....
        //Etc...

      } catch (error) {
        console.error('Unable to connect to the database:', error);
      }

});

由于 class 文件的定义方式,此技术有效。在典型的软件开发中,您包含 class 并引用它并实例化它,但在这种情况下 我们已经传递了它的实例化版本

例如,您会在 class 文件中注意到我定义了 class user extends model {} 并且它里面只是空的。然后我们 return class 的实例化版本。当您以这种方式使用 module.exports 时,它实际上需要一个 sequelize 连接实例的参数。

这就是为什么在 api.js 文件中我说 const User = require('../models/user');,这引用了文件 returning 中的 'function'。

该函数不会执行任何操作,除非您将其传递给 sequelize,因此您会看到下一行...

const UserModel = User(sequelize);

通过这样做,您可以将模型放在单独的文件中并使它们成为 classes,并且它们仍然可以具有关联甚至复合索引!

希望这对您有所帮助。

更新::

如果你想要 onUpdate 或 onDelete 级联选项,只需像这样用模型定义它。

 algorithm_id: {
            type: DataTypes.INTEGER,
            allowNull: false,
            onDelete: 'CASCADE',
            onUpdate: 'CASCADE',
            references: { 
                model: AlgorithmModel,
                key: 'id',
            }
        },

您也可以设置是否希望它为 null,但是请记住,allowNull 将无法与 'alter' table 命令一起使用,除非首先删除了外键!不过,onUpdate 和 onDelete 似乎与 alter 一起工作得很好。