Select 具有一对多关系时使用 Sequelize ORM 的所有记录

Select All Records with Sequelize ORM when having One-To-Many relationship

我有两个表UsersShops,用户可以有多个店铺,店铺可以由一个用户拥有。

我正在使用 Node.js & Sequelize ORM (postgres)

问题

Shop 模型获取数据时,Sequelize 在 select 查询中添加额外的字段 UserId,例如:

SELECT "id", "userId", "createdAt", "updatedAt", "UserId" FROM "Shops" AS "Shop";

当我执行此查询时执行:

const shops = await Shop.findAll();

此行抛出异常说 `错误:“UserId”列不存在'

堆栈跟踪:

Error
    at Query.run (F:\Work\Coding\stack\node_modules\sequelize\lib\dialects\postgres\query.js:50:25)
    at F:\Work\Coding\stack\node_modules\sequelize\lib\sequelize.js:313:28
    at processTicksAndRejections (node:internal/process/task_queues:96:5)
    at async PostgresQueryInterface.select (F:\Work\Coding\stack\node_modules\sequelize\lib\dialects\abstract\query-interface.js:396:12)
    at async Function.findAll (F:\Work\Coding\stack\node_modules\sequelize\lib\model.js:1119:21)
    at async F:\Work\Coding\stack\app.js:47:19 {
  name: 'SequelizeDatabaseError',
  parent: error: column "UserId" does not exist
      at Parser.parseErrorMessage (F:\Work\Coding\stack\node_modules\pg-protocol\dist\parser.js:287:98)
      at Parser.handlePacket (F:\Work\Coding\stack\node_modules\pg-protocol\dist\parser.js:126:29)
      at Parser.parse (F:\Work\Coding\stack\node_modules\pg-protocol\dist\parser.js:39:38)
      at Socket.<anonymous> (F:\Work\Coding\stack\node_modules\pg-protocol\dist\index.js:11:42)
      at Socket.emit (node:events:390:28)
      at addChunk (node:internal/streams/readable:315:12)
      at readableAddChunk (node:internal/streams/readable:289:9)
      at Socket.Readable.push (node:internal/streams/readable:228:10)
      at TCP.onStreamRead (node:internal/stream_base_commons:199:23) {
    length: 164,
    severity: 'ERROR',
    code: '42703',
    detail: undefined,
    hint: 'Perhaps you meant to reference the column "Shop.userId".',
    position: '50',
    internalPosition: undefined,
    internalQuery: undefined,
    where: undefined,
    schema: undefined,
    table: undefined,
    column: undefined,
    dataType: undefined,
    constraint: undefined,
    file: 'parse_relation.c',
    line: '3599',
    routine: 'errorMissingColumn',
    sql: 'SELECT "id", "userId", "createdAt", "updatedAt", "UserId" FROM "Shops" AS "Shop";',
    parameters: undefined
  },
  original: error: column "UserId" does not exist
      at Parser.parseErrorMessage (F:\Work\Coding\stack\node_modules\pg-protocol\dist\parser.js:287:98)
      at Parser.handlePacket (F:\Work\Coding\stack\node_modules\pg-protocol\dist\parser.js:126:29)
      at Parser.parse (F:\Work\Coding\stack\node_modules\pg-protocol\dist\parser.js:39:38)
      at Socket.<anonymous> (F:\Work\Coding\stack\node_modules\pg-protocol\dist\index.js:11:42)
      at Socket.emit (node:events:390:28)
      at addChunk (node:internal/streams/readable:315:12)
      at readableAddChunk (node:internal/streams/readable:289:9)
      at Socket.Readable.push (node:internal/streams/readable:228:10)
    schema: undefined,
    table: undefined,
    column: undefined,
    dataType: undefined,
    constraint: undefined,
    file: 'parse_relation.c',
    line: '3599',
    routine: 'errorMissingColumn',
    sql: 'SELECT "id", "userId", "createdAt", "updatedAt", "UserId" FROM "Shops" AS "Shop";',
    parameters: undefined
  },
  sql: 'SELECT "id", "userId", "createdAt", "updatedAt", "UserId" FROM "Shops" AS "Shop";',
  parameters: {}
}

JavaScript代码

const { Sequelize, Model, DataTypes } = require('sequelize');

const connectToDB = async () => {
    const sequelize = new Sequelize("postgres://postgres:root@localhost:5432/testDB", {
        dialect: 'postgres'
    });
    await sequelize.authenticate();
    console.log('Database connected...');
    return sequelize;
}

connectToDB().then(async (sequelize) => {
    // --- Craete User Model ---
    class User extends Model { }
    await User.init({
        id: {
            primaryKey: true,
            type: DataTypes.UUID,
            defaultValue: Sequelize.UUIDV4,
        }
    }, { sequelize, modelName: 'User' })
    await User.sync();

    // --- Craete Shop Model ---
    class Shop extends Model { }
    await Shop.init({
        id: {
            primaryKey: true,
            type: DataTypes.UUID,
            defaultValue: Sequelize.UUIDV4,
        },
        userId: {
            type: DataTypes.UUID,
            allowNull: false
        }
    }, { sequelize, modelName: 'Shop' })
    await Shop.sync();

    /* RELATION BETWEEN User AND Shop (one-to-many) */
    await User.hasMany(Shop, {
        foreignKey: 'userId',
        onDelete: "CASCADE",
    })
    await Shop.belongsTo(User)

    // --- Find All Shops --
    const shops = await Shop.findAll();
    console.log(shops);

}).catch((err) => console.error(err));

表的创建正常,如控制台中所打印:

Executing (default): SELECT 1+1 AS result
Executing (default): CREATE TABLE IF NOT EXISTS "Users" ("id" UUID , "createdAt"TIMESTAMP WITH TIME ZONE NOT NULL, "updatedAt" TIMESTAMP WITH TIME ZONE NOT NULL, PRIMARY KEY ("id"));
Executing (default): SELECT i.relname AS name, ix.indisprimary AS primary, ix.indisunique AS unique, ix.indkey AS indkey, array_agg(a.attnum) as column_indexes, array_agg(a.attname) AS column_names, pg_get_indexdef(ix.indexrelid) AS definition FROM pg_class t, pg_class i, pg_index ix, pg_attribute a WHERE t.oid = ix.indrelid AND i.oid = ix.indexrelid AND a.attrelid = t.oid AND t.relkind = 'r' and t.relname = 'Users' GROUP BY i.relname, ix.indexrelid, ix.indisprimary, ix.indisunique, ix.indkey ORDER BY i.relname;
Executing (default): CREATE TABLE IF NOT EXISTS "Shops" ("id" UUID , "userId" UUID NOT NULL, "createdAt" TIMESTAMP WITH TIME ZONE NOT NULL, "updatedAt" TIMESTAMP WITH TIME ZONE NOT NULL, PRIMARY KEY ("id"));
Executing (default): SELECT i.relname AS name, ix.indisprimary AS primary, ix.indisunique AS unique, ix.indkey AS indkey, array_agg(a.attnum) as column_indexes, array_agg(a.attname) AS column_names, pg_get_indexdef(ix.indexrelid) AS definition FROM pg_class t, pg_class i, pg_index ix, pg_attribute a WHERE t.oid = ix.indrelid AND i.oid = ix.indexrelid AND a.attrelid = t.oid AND t.relkind = 'r' and t.relname = 'Shops' GROUP BY i.relname, ix.indexrelid, ix.indisprimary, ix.indisunique, ix.indkey ORDER BY i.relname;

您需要在两个关系中指示相同的 foreignKey 选项值:

/* RELATION BETWEEN User AND Shop (one-to-many) */
    User.hasMany(Shop, {
        foreignKey: 'userId',
        onDelete: "CASCADE",
    })
    Shop.belongsTo(User, {
        foreignKey: 'userId',
    })

P.S。不需要注明await因为这些是同步功能