Node.js Sequelize+PostgreSQL UUID主键配置问题求助
我来帮你理清这些问题,先从错误的根源说起,再给出具体的修复方案:
错误根源分析
Sequelize有个默认行为:如果模型没有显式指定主键,它会自动添加一个名为id的自增整数主键。这就是你第一个错误(column 'id' doesn't exist)的原因——你在迁移里创建了uuid作为主键,但模型里没告诉Sequelize用它当主键,所以Sequelize依然默认去查询id列,而这列根本不存在。
第二个错误是因为你在模型里把id定义成了INTEGER类型,既没标记它为主键,又和迁移里的UUID类型不匹配;另外你在迁移里写了Sequelize.UUIDV4()——这会立即执行生成一个固定的UUID值,而不是每次创建新记录时生成新的UUID,正确的写法应该是传递函数引用Sequelize.UUIDV4。
你的疑问解答&修复方案
1. 是否可以强制Sequelize使用UUID作为表主键而无需定义id列?
当然可以,但需要同时在模型和迁移文件中正确配置,还要明确告诉Sequelize不要自动生成默认的id列。
正确的迁移文件:
'use strict'; const DataTypes = require('sequelize').DataTypes; module.exports = { up: (queryInterface, Sequelize) => { return queryInterface.createTable('Currencies', { uuid: { primaryKey: true, type: Sequelize.UUID, defaultValue: DataTypes.UUIDV4, // 不要加括号,传递函数引用即可 allowNull: false }, name: { type: Sequelize.STRING }, ticker: { type: Sequelize.STRING }, alt_tickers: { type: Sequelize.ARRAY(Sequelize.STRING) }, createdAt: { allowNull: false, type: Sequelize.DATE }, updatedAt: { allowNull: false, type: Sequelize.DATE } }); }, down: (queryInterface, Sequelize) => { return queryInterface.dropTable('Currencies'); } };
正确的模型文件:
'use strict'; module.exports = (sequelize, DataTypes) => { const Currency = sequelize.define('Currency', { uuid: { type: DataTypes.UUID, primaryKey: true, // 必须标记该字段为主键 defaultValue: DataTypes.UUIDV4 }, name: DataTypes.STRING, ticker: DataTypes.STRING, alt_tickers: DataTypes.ARRAY(DataTypes.STRING) }, { id: false, // 关键配置:禁用Sequelize自动生成默认id列的行为 primaryKeyField: 'uuid' // 可选:明确指定主键字段名 }); Currency.associate = function(models) { // 在这里定义模型关联 }; return Currency; };
配置完成后,Sequelize就会使用uuid作为主键,不会再去查询不存在的id列了。
2. 是否可以创建不包含id列的数据表?
完全可以,只要在模型的配置选项中添加id: false,就能禁用Sequelize自动生成id主键的行为。上面的模型配置已经包含了这个选项,对应的迁移文件里也不需要创建id列,最终生成的表就不会有id列。
3. 这些错误的原因是什么,该如何修复?
第一个错误(
column 'id' doesn't exist):
原因:模型没有指定自定义主键,Sequelize默认尝试查询它自动生成的id列,但你的迁移文件里并没有创建这个列。
修复:在模型中给uuid字段添加primaryKey: true,同时设置id: false禁用默认主键的自动生成。第二个错误(
Error: A column called 'id' was added to the attributes of 'Currencies' but not marked with 'primaryKey: true'):
原因:你在模型里把id定义为INTEGER类型,既没标记它为主键,又和迁移文件里的UUID类型不匹配;另外迁移里的defaultValue: Sequelize.UUIDV4()写法错误,会生成固定UUID而不是动态生成。
修复:如果想把主键列名设为id(UUID类型),需要确保模型和迁移的配置匹配:
主键列名为id(UUID类型)的正确配置:
迁移文件:
'use strict'; const DataTypes = require('sequelize').DataTypes; module.exports = { up: (queryInterface, Sequelize) => { return queryInterface.createTable('Currencies', { id: { primaryKey: true, type: Sequelize.UUID, defaultValue: Sequelize.UUIDV4, // 不要加括号! allowNull: false }, name: { type: Sequelize.STRING }, ticker: { type: Sequelize.STRING }, alt_tickers: { type: Sequelize.ARRAY(Sequelize.STRING) }, createdAt: { allowNull: false, type: Sequelize.DATE }, updatedAt: { allowNull: false, type: Sequelize.DATE } }); }, down: (queryInterface, Sequelize) => { return queryInterface.dropTable('Currencies'); } };
模型文件:
'use strict'; module.exports = (sequelize, DataTypes) => { const Currency = sequelize.define('Currency', { id: { type: DataTypes.UUID, primaryKey: true, defaultValue: DataTypes.UUIDV4 }, name: DataTypes.STRING, ticker: DataTypes.STRING, alt_tickers: DataTypes.ARRAY(DataTypes.STRING) }, {}); // 这里不需要id: false,因为我们已经显式把id设为主键了 Currency.associate = function(models) { // 在这里定义模型关联 }; return Currency; };
内容的提问来源于stack exchange,提问作者Number16BusShelter

