【问题标题】:sequelize V6 typescript: get data as plain object with findallsequelize V6 typescript:使用 findall 将数据作为普通对象获取
【发布时间】:2021-01-22 17:10:09
【问题描述】:

我正在测试 sequelize(使用 typescript)来替换 typeorm。 但是,我正在努力以正确的格式从数据库中获取数据。 “正确格式”是指逐行记录作为一个普通对象,它基本上表示该类的一个实例。(检查图片与预期结果) 下面是一个自显式的简单代码。球队有几个球员,一个球员属于一个球队。在 postgres 表中,有 2 支球队,第一支球队有 3 名球员,第二支球队有 2 名球员。 与 postgres 的连接选项很简单。

    "pg": "^8.3.3",
    "pg-hstore": "^2.3.3",
    "sequelize": "^6.3.5",
    "typescript": "^4.0.2"


import { DataTypes, Model, Sequelize } from "sequelize";
import { createDbConnection } from "./DbConnections";

interface ITeamAttributes {
    id: number;
    teamName: string;
}

type ITeamCreationAttributes = Partial<ITeamAttributes> & { id: number };

class Team
    extends Model<ITeamAttributes, ITeamCreationAttributes>
    implements ITeamAttributes {
    public id: number;
    public teamName: string;
    public teamPlayers: Array<Player>;

    static defineModel(
        dbConnection: Sequelize
    ): Model<ITeamAttributes, ITeamCreationAttributes> {
        const table = this.init<Team>(
            {
                id: {
                    type: DataTypes.INTEGER,
                    allowNull: false,
                    autoIncrementIdentity: true,
                    primaryKey: true,
                },
                teamName: {
                    type: DataTypes.STRING(255),
                    allowNull: false,
                    defaultValue: "",
                },
            },
            {
                sequelize: dbConnection,
                tableName: "team",
            }
        );

        return table;
    }

    static defineRelations(): void {
        Team.hasMany(Player, {
            sourceKey: "id",
            foreignKey: "teamId",
            as: "teamPlayers",
        });
    }
}

interface IPlayerAttributes {
    id: number;
    playerName: string;
}

type IPlayerCreationAttributes = Partial<IPlayerAttributes> & { id: number };

class Player
    extends Model<IPlayerAttributes, IPlayerCreationAttributes>
    implements IPlayerAttributes {
    public id: number;
    public playerName: string;
    public playerTeam: Team;

    public static defineModel(
        dbConnection: Sequelize
    ): Model<IPlayerAttributes, IPlayerCreationAttributes> {
        const table = this.init<Player>(
            {
                id: {
                    type: DataTypes.INTEGER,
                    allowNull: false,
                    autoIncrementIdentity: true,
                    primaryKey: true,
                },
                playerName: {
                    type: DataTypes.STRING(255),
                    allowNull: false,
                    defaultValue: "",
                },
            },
            {
                sequelize: dbConnection,
                tableName: "player",
            }
        );

        return table;
    }
    public static defineRelations(): void {
        Player.belongsTo<Player, Team>(Team, {
            foreignKey: "teamId",
            targetKey: "id",
            as: "playerTeam",
        });
    }
}

const main = async (): Promise<void> => {
    const dbConnection: Sequelize = createDbConnection();

    if (dbConnection.authenticate()) {
        Player.defineModel(dbConnection);
        Team.defineModel(dbConnection);
        // relations to be defined after all Model definition
        Player.defineRelations();
        Team.defineRelations();

        await dbConnection.sync();

        const teams: Array<Team> = await Team.findAll<Team>({
            include: [{ association: "teamPlayers" }],
        });
        const players: Array<Player> = await Player.findAll<Player>({
            include: [{ association: "playerTeam" }],
        });

        console.log(teams);
        console.log(players);

        console.log(teams.map((team) => team.get()));
        console.log(players.map((player) => player.get()));

        const tt = JSON.parse(JSON.stringify(teams, null, 2)) as Team[];
        const pp = JSON.parse(JSON.stringify(players, null, 2)) as Player[];

        console.log(tt, pp);

        tt.map((t) => t.teamPlayers.map((p) => console.log(p.playerName)));
    } else {
        console.log("not connected to database");
    }
};

main();

问题是,如果表正在这样做,那么唯一获取数据作为真实实例的方法:

    const tt = JSON.parse(JSON.stringify(teams, null, 2)) as Team[];
    const pp = JSON.parse(JSON.stringify(players, null, 2)) as Player[];

其他 console.log 没有将记录作为普通对象返回。 我试图将选项{raw:true} 添加到findAll 调用中,但这是最糟糕的。获得团队时,不是每个团队都收到一组球员的记录(所以 2 条记录),结果是平淡无奇。每对 Team/Player 有一个记录,因此 2*3 = 6 个记录。

解决办法是什么?因为应用JSON.STRINGIFYJSON.PARSE 是一种糟糕的方式。

问候

[编辑] 这里是findAll&lt;Team&gt;()的结果,这不是Team的数组

这是findAll&lt;Team&gt;({raw:true}) 的结果。每个团队/球员有一个记录。预期结果是每个团队有一个记录,其中包含一组玩家

这是findAll&lt;Team&gt;()findAll&lt;Player&gt;() 的预期结果。 Player 数组中每个 Team 有一个记录,每个 Player 都有他的团队信息。这个结果是通过应用JSON.parse(JSON.stringify(teams, null, 2)) as Team[]JSON.parse(JSON.stringify(players, null, 2)) as Player[]得到的

【问题讨论】:

    标签: typescript sequelize.js


    【解决方案1】:

    尝试像这样使用plain 选项:

    console.log(teams.map((team) => team.get({ plain: true })));
            console.log(players.map((player) => player.get({ plain: true })));
    

    【讨论】:

    • 结果与使用 JSON 字符串化和解析类似。但由于我必须运行地图,所以速度较慢。我很困惑为什么`Model.findAll`的结果不是T的数组。Typescript将类型推断为T[],但实际上不是。
    • 添加了 2 张图片,显示 findAll&lt;Team&gt;()findAll&lt;Team&gt;({raw=true}) 没有发回类的实例。
    • 默认情况下 findAll 返回模型实例而不是普通对象。这样您就可以调用各种实例方法来销毁、重新加载或更新某个模型实例。如果你想要普通的对象,你应该使用get({ plain: true})
    • 谢谢,已接受您的评论和回答。
    猜你喜欢
    • 2020-01-31
    • 2016-09-14
    • 2019-08-15
    • 2020-10-07
    • 2019-03-01
    • 2021-11-27
    • 1970-01-01
    • 2016-08-30
    • 1970-01-01
    相关资源
    最近更新 更多