带Sequelize的外键无法按预期工作

时间:2018-11-04 22:16:44

标签: foreign-keys sequelize.js

我试图在两个表之间创建关联,我想添加一个外键。

两个模型是用户和公司

User.associate = (models) => { User.belongsTo(models.Companies, { foreignKey: 'Company' }); };

我期望上面的代码是在用户表中添加一个Company ID字段,该字段引用了Companies表的Company ID。

在运行上面的代码时,我看不到创建任何其他列。我尝试检查是否在数据库中创建了外键关联,并且该关联也丢失了。

但是,如果我尝试在保留关联代码的同时添加具有相同名称的列,则会发生名称冲突。这似乎表明正在创建该关联,但我看不到它。

有人可以帮助我了解我在做什么错吗?感谢您的帮助!

models / company.js

module.exports = (sequelize, DataTypes) => {
    var Company = sequelize.define('company', {
        company: { type: DataTypes.STRING, primaryKey: true },
    });

    Company.associate = (models) => {
        Company.hasMany(models.user, { as: 'users' });
    };

    Company.sync();

    return Company;
};

models / user.js

const uuid = require('uuid/v4');

'use strict';
module.exports = (sequelize, DataTypes) => {
    var User = sequelize.define('user', {
        id: { type: DataTypes.UUID, primaryKey: true },
        name: { type: DataTypes.STRING, allowNull: false }
    });

    User.associate = (models) => {
        User.belongsTo(models.company);
    };

    User.beforeCreate((user, _ ) => {
        user.id = uuid();
        return user;
    });

    User.sync();

    return User;
};

models / index.js

'use strict';

var fs        = require('fs');
var path      = require('path');
var Sequelize = require('sequelize');
var basename  = path.basename(__filename);
var env       = process.env.NODE_ENV || 'development';
// var config    = require(__dirname + '/../config/config.js')[env];
var db        = {};

// if (config.use_env_variable) {
//   var sequelize = new Sequelize(process.env[config.use_env_variable], config);
// } else {
//   var sequelize = new Sequelize(config.database, config.username, config.password, config);
// }

const sequelize = new Sequelize('postgres://postgres:user@localhost:5432/mydb');

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

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

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

module.exports = db;

4 个答案:

答案 0 :(得分:3)

我能够解决这个问题。

问题在于调用同步的顺序。在我的原始代码中,我在每个模型中调用了sync。即使我添加了选项forcealter,我仍然认为未添加外键。因此,我从模型内部删除了sync代码,并将其添加到index.js内部的单独循环中。

这给了我一个新问题。由于表应预先存在,因此创建表的顺序与外键工作的创建顺序不符。我通过手动提供sync的顺序来解决了这个问题,现在我看到了正在创建的列。

总结一下:模型定义->模型关联->按顺序进行模型同步

SO的成员,谢谢您的建议。

答案 1 :(得分:1)

您的模型很好!您必须从模型文件中删除sync,然后检查迁移文件中是否有带有foregin key的外键的模型,

用于迁移用户:

module.exports = {
    up: (queryInterface, Sequelize) => {
        return queryInterface.createTable('Users', {
            id: {
                allowNull: false,
                autoIncrement: true,
                primaryKey: true,
                type: Sequelize.UUID
            },
            name: {
                type: Sequelize.STRING
            },
            companyId: {
                type: Sequelize.UUID,
                references: {
                    model: 'Company',// company migration define
                    key: 'id'
                }
            },
            createdAt: {
                allowNull: false,
                type: Sequelize.DATE
            },
            updatedAt: {
                allowNull: false,
                type: Sequelize.DATE
            }
        });
    },
    down: (queryInterface, Sequelize) => {
        return queryInterface.dropTable('Users');
    }
};

要从index.js和模型创建自动表,必须安装sequelize-cli

通过类型npm install --save sequelize-cli

然后必须运行此命令以在db中创建模型表

sequelize db:migrate

答案 2 :(得分:0)

通过使用foreignKey: 'Company'告诉它与名为Company的列关联。您通常还希望使用单表名,因此将company与关联companies一起使用。默认情况下,Sequelize将使用关联的主键,因此,如果要更改它或设置其他参数,只需指定foreignKey

const User = sequelize.define(
  'user',
  { /* columns */ },
  { /* options */ }
);
User.associate = (models) => {
    User.belongsTo(models.Company);
};

const Company = sequelize.define(
  'company',
  { /* columns */ },
  { /* options */ }
);
Company.associate = (models) => {
    Company.hasMany(models.User, { as: 'users' });
};

这将创建以下表Company (id)User (id, company_id)

查询与单个User关联的所有Company记录:

const user = await User.findAll({ include: { model: Company } });
/*
user = {
  id: 1,
  company_id: 1,
  company: {
    id: 1,
  },
};
*/

通过Company查询具有多个关联User记录的所有users记录:

const company = await User.findAll({ include: { model: User, as: 'users' } });
/*
company = {
  id: 1,
  users: [{
    id: 1
    company_id: 1,
  }],
};
*/

答案 3 :(得分:0)

我的猜测是associate方法没有被调用,因此,您的关联没有被创建。请记住,associate不是内置的Sequelize方法,而只是社区使用的一种模式。 (有关this thread的更多信息)

有多种方法可以处理调用associate,这是一个示例。您有一个models.js文件来处理关联,并在主app.js文件中对其进行了初始化。

// app.js (aka your main application)
const models = require('./models')(sequelize, DataTypes);

// models.js
module.exports = (sequelize, DataTypes) => {
    const models = {
        user: require('./userModel')(sequelize, DataTypes),
        company: require('./companyModel')(sequelize, DataTypes)
    };

    Object.keys(models).forEach(key => {
        if (models[key] && models[key].associate) {
            models[key].associate(models);
        }
    });
};

// companyModel.js
module.exports = (sequelize, DataTypes) => {
    var Company = sequelize.define('company', {...});

    Company.associate = (models) => {
        Company.hasMany(models.user, { as: 'users' });
    };

    Company.sync();

    return Company;
};

// userModel.js
module.exports = (sequelize, DataTypes) => {
    var User = sequelize.define('user', {...});

    User.sync();

    return User;
};

也,仅供参考,您可能知道这一点,但是sync仅应用于实验或测试,而不能用于生产应用。