Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Cannot use @DefaultScope() simultaneously in two related Models #1703

Open
1 task
caioalmeida12 opened this issue Dec 17, 2023 · 2 comments
Open
1 task

Cannot use @DefaultScope() simultaneously in two related Models #1703

caioalmeida12 opened this issue Dec 17, 2023 · 2 comments

Comments

@caioalmeida12
Copy link

caioalmeida12 commented Dec 17, 2023

Issue

Cannot use @DefaultScope() simultaneously in two related Models

Versions

  • sequelize: 6.35.1
  • sequelize-typescript: 2.1.6
  • typescript: 5.3.3

Issue type

  • [ x] bug report
  • feature request

Actual behavior

I have two related models: Jogador and Responsavel, in which Jogador is the one referenced by Responsavel. I added a DefaultScope to Jogador, in which it retrieves all attributes and nested objects, including Responsavel; When it comes to Responsavel, i am trying to do the same: add a DefaultScope that retireves all attributes and the Jogador it is related to, but i cant do it because i keep getting the following error as soon as i add the DefaultScope to the ResponsavelModel (note the error only occurs once i add the DefaultScope to this one; if it is only in Jogador the error won't occur and the result will be as expected)

Expected behavior

It should let me query by default Jogador and retrieve it's Responsavel from database automatically; the same thing should happen with Responsavel, but in this case retrieving it's related Jogador.

Steps to reproduce

Start the sequelize-typescript instance and add DefaultScope to two related models

Related code

[tsconfig]
{
  "name": "server",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1",
    "dev": "nodemon --exec ts-node src/index.ts --watch src"
  },
  "author": "",
  "license": "ISC",
  "devDependencies": {
    "@types/bcrypt": "^5.0.0",
    "@types/cors": "^2.8.14",
    "@types/express": "^4.17.17",
    "@types/jest": "^29.5.4",
    "@types/morgan": "^1.9.9",
    "@types/node": "^20.6.5",
    "@types/sequelize": "^4.28.15",
    "@types/supertest": "^2.0.12",
    "dotenv": "^16.3.1",
    "express": "^4.18.2",
    "jest": "^29.7.0",
    "morgan": "^1.10.0",
    "nodemon": "^3.0.1",
    "ts-jest": "^29.1.1",
    "ts-node": "^10.9.1",
    "tsc-alias": "^1.8.7",
    "typescript": "^5.1.6"
  },
  "include": [
    "src/**/*.ts"
  ],
  "dependencies": {
    "bcrypt": "^5.1.1",
    "cors": "^2.8.5",
    "helmet": "^7.1.0",
    "mysql2": "^3.6.1",
    "sequelize": "^6.33.0",
    "sequelize-typescript": "^2.1.6",
    "sqlite3": "^5.1.6",
    "tsconfig-paths": "^4.2.0",
    "utility-types": "^3.10.0",
    "zod": "^3.22.2"
  }
}

[sequelize-typescript instance]

import path from "path";
import { Dialect } from "sequelize";
import { Sequelize } from "sequelize-typescript";

if (!["sqlite", "postgres"].includes(process.env.DB_DIALECT as string)) throw new Error(`Database dialect "${process.env.DB_DIALECT}" not supported`);

let sequelizeConfig = {
    dialect: process.env.DB_DIALECT as Dialect,
    host: process.env.DB_HOST,
    port: Number(process.env.DB_PORT),
    models: [path.resolve(__dirname, '../models/')],
    storage: "" as string | undefined,
    username: "" as string | undefined,
    password: "" as string | undefined,
    database: "" as string | undefined,
    logging: Boolean(process.env.DB_LOGGING == "true") ? console.log : false,
}

switch (process.env.DB_DIALECT) {
    case "sqlite":
        sequelizeConfig = {
            ...sequelizeConfig,
            storage: process.env.DB_STORAGE,
        };
        break;
    case "postgres":
        sequelizeConfig = {
            ...sequelizeConfig,
            username: process.env.DB_USER,
            password: process.env.DB_PASS,
            database: process.env.DB_NAME,
        };
        break;
}

const sequelize = new Sequelize(sequelizeConfig);

sequelize.sync().then(() => {
    console.log(`\x1b[36m\nSuccessfully connected to database "${process.env.DB_NAME}" with "${process.env.DB_DIALECT}" dialect\n\x1b[0m`);
}).catch((error) => {
    console.log(`\x1b[31m\nError connecting to database "${process.env.DB_NAME}" with "${process.env.DB_DIALECT}" dialect\n\x1b[0m`);
    console.log(error);
});

export default sequelize;

[JogadorModel]

import { JogadorType } from '@lib/types/jogadorType';
import { AllowNull, Column, Length, Table, DataType, Model, ForeignKey, BelongsTo, HasMany, Min, Max, PrimaryKey, Unique, Scopes, DefaultScope, Default, HasOne} from "sequelize-typescript";

import ResponsavelModel from './responsavelModel';

@DefaultScope(() => ({
    include: {
        all: true,
        nested: true,
    }
}))
@Table({
    tableName: process.env.MODEL_JOGADOR_TABLE_NAME,
    paranoid: true,
})
export default class JogadorModel extends Model<JogadorType, Omit<JogadorType, "id">> {
    @PrimaryKey
    @Default(DataType.UUIDV4)
    @Column(DataType.UUIDV4)
    declare id: string;

    @Unique
    @AllowNull(false)
    @Length({ min: 11, max: 11 })
    @Column(DataType.STRING(11))
    declare cpf: string;   

    @AllowNull(false)
    @Length({ min: 1, max: 128 })
    @Column(DataType.STRING(128))
    declare nome_completo: string;

    @AllowNull(false)
    @Length({ min: 11, max: 13 })
    @Column(DataType.STRING(13))
    declare telefone: string;

    @AllowNull(false)
    @Length({ min: 1, max: 128 })
    @Column(DataType.STRING(128))
    declare email: string;

    @HasOne(() => ResponsavelModel, {
        onDelete: "CASCADE",
        onUpdate: "CASCADE",
    })
    declare responsavel: ResponsavelModel;
}

[ResponsavelModel]

import { ResponsavelType } from '@lib/types/responsavelType';
import { AllowNull, Column, Length, Table, DataType, Model, ForeignKey, BelongsTo, HasMany, Min, Max, PrimaryKey, Unique, Scopes, DefaultScope, Default, HasOne } from "sequelize-typescript";

import JogadorModel from './jogadorModel';

@DefaultScope(() => ({
    include: {
        all: true,
        nested: true,
    }
}))
@Table({
    tableName: process.env.MODEL_RESPONSAVEL_TABLE_NAME,
    paranoid: true,
})
export default class ResponsavelModel extends Model<ResponsavelType, Omit<ResponsavelType, "id">> {
    @PrimaryKey
    @Default(DataType.UUIDV4)
    @Column(DataType.UUIDV4)
    declare id: string;

    @Unique
    @AllowNull(false)
    @Length({ min: 11, max: 11 })
    @Column(DataType.STRING(11))
    declare cpf: string;

    @AllowNull(false)
    @Length({ min: 1, max: 128 })
    @Column(DataType.STRING(128))
    declare nome_completo: string;

    @AllowNull(false)
    @Length({ min: 11, max: 13 })
    @Column(DataType.STRING(13))
    declare telefone: string;

    @AllowNull(false)
    @Length({ min: 1, max: 128 })
    @Column(DataType.STRING(128))
    declare email: string;

    @ForeignKey(() => JogadorModel)
    @Column(DataType.UUIDV4)
    declare fk_jogador_id: string;

    @BelongsTo(() => JogadorModel, {
        onDelete: "CASCADE",
        onUpdate: "CASCADE",
    })
    declare jogador: JogadorModel;
}
@caioalmeida12
Copy link
Author

caioalmeida12 commented Dec 17, 2023

[Error]

TypeError: Cannot read properties of undefined (reading 'getTableName')
    at Function._validateIncludedElement (C:\Users\caiod\Desktop\campeonato-municipal\server\node_modules\sequelize\src\model.js:611:30)     
    at C:\Users\caiod\Desktop\campeonato-municipal\server\node_modules\sequelize\src\model.js:542:37
    at Array.map (<anonymous>)
    at Function._validateIncludedElements (C:\Users\caiod\Desktop\campeonato-municipal\server\node_modules\sequelize\src\model.js:537:39)    
    at Function._validateIncludedElement (C:\Users\caiod\Desktop\campeonato-municipal\server\node_modules\sequelize\src\model.js:732:38)     
    at C:\Users\caiod\Desktop\campeonato-municipal\server\node_modules\sequelize\src\model.js:542:37
    at Array.map (<anonymous>)
    at Function._validateIncludedElements (C:\Users\caiod\Desktop\campeonato-municipal\server\node_modules\sequelize\src\model.js:537:39)    
    at Function.findAll (C:\Users\caiod\Desktop\campeonato-municipal\server\node_modules\sequelize\src\model.js:1794:12)
    at processTicksAndRejections (node:internal/process/task_queues:95:5)

@caioalmeida12
Copy link
Author

caioalmeida12 commented Dec 17, 2023

Update: the issue seems to be related to recursive including one class onto another within the DefaultScopes, as i can workaround it by doing the following:

import { ResponsavelType } from '@lib/types/responsavelType';
import { AllowNull, Column, Length, Table, DataType, Model, ForeignKey, BelongsTo, HasMany, Min, Max, PrimaryKey, Unique, Scopes, DefaultScope, Default, HasOne } from "sequelize-typescript";

import JogadorModel from './jogadorModel';

@DefaultScope(() => ({
    include: [JogadorModel.unscoped()]
}))
@Table({
    tableName: process.env.MODEL_RESPONSAVEL_TABLE_NAME,
    paranoid: true,
})
export default class ResponsavelModel extends Model<ResponsavelType, Omit<ResponsavelType, "id">> {
    @PrimaryKey
    @Default(DataType.UUIDV4)
    @Column(DataType.UUIDV4)
    declare id: string;

    @Unique
    @AllowNull(false)
    @Length({ min: 11, max: 11 })
    @Column(DataType.STRING(11))
    declare cpf: string;

    @AllowNull(false)
    @Length({ min: 1, max: 128 })
    @Column(DataType.STRING(128))
    declare nome_completo: string;

    @AllowNull(false)
    @Length({ min: 11, max: 13 })
    @Column(DataType.STRING(13))
    declare telefone: string;

    @AllowNull(false)
    @Length({ min: 1, max: 128 })
    @Column(DataType.STRING(128))
    declare email: string;

    @ForeignKey(() => JogadorModel)
    @Column(DataType.UUIDV4)
    declare fk_jogador_id: string;

    @BelongsTo(() => JogadorModel, {
        onDelete: "CASCADE",
        onUpdate: "CASCADE",
    })
    declare jogador: JogadorModel;
}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant