sequelize(和 sequelize-cli)queryInterface.createTable 在 PostgreSQL 上,PK id 类型为 UUID 和 defaultValue:Se

Posted

技术标签:

【中文标题】sequelize(和 sequelize-cli)queryInterface.createTable 在 PostgreSQL 上,PK id 类型为 UUID 和 defaultValue:Sequelize.DataTypes.UUIDV4 失败【英文标题】:sequelize (and sequelize-cli) queryInterface.createTable on PostgreSQL with PK id of type UUID with defaultValue: Sequelize.DataTypes.UUIDV4 failure 【发布时间】:2021-07-27 01:25:49 【问题描述】:

是否支持使用在 postgreSQL 方言中自动生成的 UUID 类型 id? 对生成的 CREATE TABLE sql 感到非常惊讶,这对于 postgreSQL 方言似乎不正确。

在 PostgreSQL 上通过 queryGenerator.createTableQuery 对 queryInterface.createTable 进行续集,其 PK id 类型为 UUID 和 defaultValue:Sequelize.DataTypes.UUIDV4 似乎没有创建正确的 CREATE TABLE,当未提供 id 值时,它将为 id 创建默认 UUID 值。

在 queryInterface.createTable 的调用下

await queryInterface.createTable(
  // 'tableName'
  'User2s', 
  // 'attributes'
  
    id: 
      allowNull: false,
//    autoIncrement: true,  // with this "npx sequelize-cli db:migrate" fails with 'ERROR syntax error at or near "SERIAL"' for type UUID. So not using this. This would work if id type was INTEGER (without setting 'defaultValue')
      defaultValue: Sequelize.UUIDV4,  
//    defaultValue: Sequelize.DataTypes.UUIDV4,  // tried this instead of above too
//    defaultValue: require('sequelize').UUIDV4, // tried this instead of above too
      primaryKey: true,
      type: Sequelize.UUID
    ,
    firstName: 
      type: Sequelize.STRING
    ,
    lastName: 
      type: Sequelize.STRING
    ,
    email: 
      type: Sequelize.STRING
    ,
    createdAt: 
      allowNull: false,
      type: Sequelize.DATE
    ,
    updatedAt: 
      allowNull: false,
      type: Sequelize.DATE
    
  ,
  // 'options'
  schema: process.env.DB_POSTGRESQL_SCHEMA_NAME
);

在 SQL 下创建(方言设置为 postgreSQL);

CREATE TABLE exp15c."User2s"
(
    id uuid NOT NULL,
    "firstName" character varying(255) COLLATE pg_catalog."default",
    "lastName" character varying(255) COLLATE pg_catalog."default",
    email character varying(255) COLLATE pg_catalog."default",
    "createdAt" timestamp with time zone NOT NULL,
    "updatedAt" timestamp with time zone NOT NULL,
    CONSTRAINT "User2s_pkey" PRIMARY KEY (id)
)

但上面为 postgreSQL 生成的 CREATE TABLE 可能应该是;

CREATE TABLE exp15c."User2s"
(
    id uuid DEFAULT uuid_generate_v4(),
    "firstName" character varying(255) COLLATE pg_catalog."default",
    "lastName" character varying(255) COLLATE pg_catalog."default",
    email character varying(255) COLLATE pg_catalog."default",
    "createdAt" timestamp with time zone NOT NULL,
    "updatedAt" timestamp with time zone NOT NULL,
    CONSTRAINT "User2s_pkey" PRIMARY KEY (id)
)

当我看文件时; https://github.com/sequelize/sequelize/blob/9f950cbcbdd659d559496b77c40e0f827b108561/docs/manual/core-concepts/model-basics.md UUID 对于 UUID,请使用 DataTypes.UUID。它成为 PostgreSQL 和 SQLite 的 UUID 数据类型,以及 mysql 的 CHAR(36) 数据类型。 Sequelize 可以自动为这些字段生成 UUID,只需使用 Sequelize.UUIDV1 或 Sequelize.UUIDV4 作为默认值:


  type: DataTypes.UUID,
  defaultValue: Sequelize.UUIDV4 // Or Sequelize.UUIDV1

它基本上说,我上面的用法是正确的。

当我查看 sequelize 源代码时,我看到的是; https://github.com/sequelize/sequelize/blob/9f950cbcbdd659d559496b77c40e0f827b108561/lib/utils.js

const uuidv4 = require('uuid').v4;
function toDefaultValue(value, dialect) 
  if (typeof value === 'function') 
    const tmp = value();
    if (tmp instanceof DataTypes.ABSTRACT) 
      return tmp.toSql();
    
    return tmp;
  
  if (value instanceof DataTypes.UUIDV1) 
    return uuidv1();
  
  if (value instanceof DataTypes.UUIDV4) 
    return uuidv4();
  
  if (value instanceof DataTypes.NOW) 
    return now(dialect);
  
  if (Array.isArray(value)) 
    return value.slice();
  
  if (_.isPlainObject(value)) 
    return  ...value ;
  
  return value;

exports.toDefaultValue = toDefaultValue;

及以上方法用于 https://github.com/sequelize/sequelize/blob/9f950cbcbdd659d559496b77c40e0f827b108561/lib/model.js _initValues(值,选项) 但不确定它是否真的适用于我的用例。

上面生成的 CREATE TABLE sql 执行并创建表,但是当我们尝试在没有 id 值的情况下插入时不起作用,这是我正在尝试做的。

要非常详细; 我正在通过如下 sequelize-cli 命令使用 sequelize; 0) 确保您的 postgreSQL 数据库具有新模式 'exp15c' 初始化 sequelize 目录和文件 npx sequelize-cli 初始化

    创建模型和迁移 npx sequelize-cli model:generate --name User2 --attributes firstName:string,lastName:string,email:string 将模型创建为;

    '使用严格'; 常量 模型 = 要求('续集'); module.exports = (sequelize, DataTypes) => 类 User2 扩展模型 静态关联(模型) // 这里定义关联 ; 用户2.init( 名字:DataTypes.STRING, 姓氏:DataTypes.STRING, 电子邮件:DataTypes.STRING , 续集, 模型名称:'用户2', ); 返回用户2;

    手动更新生成的迁移文件以在“up”中包含上述代码

    '使用严格'; 模块.exports = 向上:异步(queryInterface,Sequelize)=> // 上面提供的带有“await queryInterface._createTable(”的代码片段在这里 , 下来:异步(queryInterface,Sequelize)=> 等待 queryInterface.dropTable('User2s'); ;

    执行上述迁移 npx sequelize-cli 数据库:迁移 观察它使用上面的 CREATE TABLE sql 创建了 User2s 表

    为 User2 创建“种子”文件 npx sequelize-cli 种子:生成 --name init-user2

    手动将上面创建的 User2 种子更新为(注意没有指定 id 值,因为它是通过默认机制自动分配的);

    '使用严格'; 模块.exports = 向上:异步(queryInterface,Sequelize)=> 等待 queryInterface.bulkInsert( tableName:'User2s',架构:'exp15c', [ 名:'ilker',姓:'kiris',电子邮件:'ilker@gmail.com',createdAt:新日期(),更新日期:新日期(), firstName:'selcuk',lastName:'gecer',电子邮件:'selcuk@gmail.com',createdAt:新日期(),updatedAt:新日期(), 名字:'turhan',姓氏:'bozkurt',电子邮件:'turhan@gmail.com',createdAt:新日期(),updatedAt:新日期() ], ); ,

    down: async (queryInterface, Sequelize) => 等待 queryInterface.bulkDelete('User2s', null, ); ;

    为 User2 执行上述种子 npx sequelize-cli db:seed:all 注意以上失败 错误:关系“User2s”的“id”列中的空值违反非空约束 因为生成的 CREATE TABLE sql 并没有真正使用 defaultValue 变量

【问题讨论】:

【参考方案1】:

我不得不说,对于使用 UUID 作为 PK id 这个基本主题缺乏适当的文档、示例和 *** 答案,我感到非常惊讶。我在下面详细记录了答案,这样其他人就不会像我一样受苦了。

经过多次尝试和错误,我找到了解决方案; 事实证明,使用 postgreSQL,

    使用 postgreSQL,默认情况下仅启用“plpgsql”扩展。 因此,要使用“uuid_generate_v1()”需要通过发布启用“uuid-ossp”postgreSQL 扩展; "如果不存在则创建扩展"uuid-ossp";

    并在迁移文件中需要使用;

    defaultValue: Sequelize.literal('uuid_generate_v4()'),

这是迁移文件中的代码sn-p;

'use strict';
module.exports = 
  up: async (queryInterface, Sequelize) => 
// create the table 'users' for 'User' model
await queryInterface.createTable(
  // 'tableName' input to queryInterface.createTable
  'Users', 
  // 'attributes' input to queryInterface.createTable
  
//      CREATE TABLE exp15c."Users"
//      (
//          id integer NOT NULL DEFAULT nextval('exp15c."Users_id_seq"'::regclass),
//          "firstName" character varying(255) COLLATE pg_catalog."default",
//          "lastName" character varying(255) COLLATE pg_catalog."default",
//          email character varying(255) COLLATE pg_catalog."default",
//          "createdAt" timestamp with time zone NOT NULL,
//          "updatedAt" timestamp with time zone NOT NULL,
//          CONSTRAINT "Users_pkey" PRIMARY KEY (id)
//      )
    // NOTE below block creates above table
//      id: 
//        allowNull: false,
//        autoIncrement: true,
//        primaryKey: true,
//        type: Sequelize.INTEGER
//      ,
    // NOTE above using INTEGER for id, below is using UUID
    id: 
      allowNull: false,
//    autoIncrement: true,  // WRONG, with this "npx sequelize-cli db:migrate" fails with 'ERROR syntax error at or near "SERIAL"'
      // NOTE with postgreSQL, by default only "plpgsql" extension is enabled. To use "uuid_generate_v1()" need to enable "uuid-ossp" postgreSQL extension via issuing;  "CREATE EXTENSION IF NOT EXISTS "uuid-ossp";
      //  "https://www.postgresqltutorial.com/postgresql-uuid/"
      defaultValue: Sequelize.literal('uuid_generate_v4()'), // NOTE results in "id UUID NOT NULL DEFAULT uuid_generate_v4()" in generated CREATE TABLE which needs need to enable "uuid-ossp" postgreSQL extension via issuing;  "CREATE EXTENSION IF NOT EXISTS "uuid-ossp";
//    defaultValue: uuid_v4(),                  // WRONG ends up adding "DEFAULT 'a1d10f9f-aa6f-493d-8442-5a6ebc304079'::uuid" in "id uuid NOT NULL DEFAULT 'a1d10f9f-aa6f-493d-8442-5a6ebc304079'::uuid", which  fails upon "npx sequelize-cli db:seed:all" with "ERROR: Validation error" 
//    defaultValue: Sequelize.UUIDV4,           // WRONG results in "id UUID NOT NULL"
//    defaultValue: Sequelize.DataTypes.UUIDV4, // WRONG equivalent to above line, results in "id uuid NOT NULL"
//    defaultValue: require('sequelize').UUIDV4,// WRONG equivalent to above line, results in "id uuid NOT NULL"
      primaryKey: true,
      type: Sequelize.UUID         
    ,
    // NOTE above block creates below table
//  CREATE TABLE exp15c."Users"
//  (
//      id uuid NOT NULL DEFAULT uuid_generate_v4(),
//   -- id uuid NOT NULL DEFAULT 'a1d10f9f-aa6f-493d-8442-5a6ebc304079'::uuid,
//   -- id uuid NOT NULL,
//      "firstName" character varying(255) COLLATE pg_catalog."default",
//      "lastName" character varying(255) COLLATE pg_catalog."default",
//      email character varying(255) COLLATE pg_catalog."default",
//      "createdAt" timestamp with time zone NOT NULL,
//      "updatedAt" timestamp with time zone NOT NULL,
//      CONSTRAINT "Users_pkey" PRIMARY KEY (id)
//  )      
        firstName: 
          type: Sequelize.STRING
        ,
        lastName: 
          type: Sequelize.STRING
        ,
        email: 
          type: Sequelize.STRING
        ,
        createdAt: 
          allowNull: false,
          type: Sequelize.DATE
        ,
        updatedAt: 
          allowNull: false,
          type: Sequelize.DATE
        
      ,
      // 'options' input to queryInterface.createTable
      schema: process.env.DB_POSTGRESQL_SCHEMA_NAME
    );
  ,
  down: async (queryInterface, Sequelize) => 
    await queryInterface.dropTable('Users');
  
;

【讨论】:

你能把代码清理成可读和殖民吗?谢谢

以上是关于sequelize(和 sequelize-cli)queryInterface.createTable 在 PostgreSQL 上,PK id 类型为 UUID 和 defaultValue:Se的主要内容,如果未能解决你的问题,请参考以下文章

在 Node 14 中使用 sequelize-cli,类型为:module

nodejs: express sequelize-cli

nodejs: express sequelize-cli

Node、Sequelize、Mysql - 如何为模型定义排序规则和字符集?

Sequelize 6 从文件中导入模型

GraphQL 和 graphql-sequelize-schema-generator