I am creating a sequelize repository and I have an object which extends sequelize model.
I was able to do the following to save the model to the db.
repository.create(myModel) // myModel being an instance of MyModel which extends sequelize model
Now I am getting the following error in typescript:
Argument of type 'MyModel' is not assignable to parameter of type 'CreationAttributes<MyModel>'.
Type 'MyModel' is not assignable to type 'Omit<any, string>'.
Index signature for type 'number' is missing in type 'MyModel'.ts(2345)
I was doing some searching and a suggestion was to add to MyModel:
[key: number]: number;
When I do that the error is changed to the following:
Argument of type 'MyModel' is not assignable to parameter of type 'CreationAttributes<MyModel>'.
Type 'MyModel' is not assignable to type 'Omit<any, string>'.
Index signature for type 'symbol' is missing in type 'MyModel'.ts(2345)
I can get around the error by changing the call to create:
repository.create({...myModel})
Can anyone point me to documentation about ts(2345) what it is, perhaps how to ignore it. Or even solve the issue the correct way?
Using the spread operator, to me seems a bit messy, but if that is the correct solution that is fine.
Model definition:
import {
Column, DataType, ForeignKey, Model, Sequelize, Table,
} from 'sequelize-typescript';
import MediaResource from './media_resource';
import User from './users';
#Table({
tableName: 'videos',
timestamps: true,
version: true,
})
export default class Video extends Model {
#Column({
primaryKey: true,
autoIncrement: true,
type: DataType.INTEGER,
defaultValue: Sequelize.literal("nextval('videos_id_seq'::regclass)"),
})
id?: number;
#ForeignKey(() => MediaResource)
#Column({
field: 'media_resource_id',
allowNull: false,
type: DataType.INTEGER,
})
mediaResourceId?: number;
#Column({
allowNull: false,
type: DataType.STRING(191),
})
title?: string;
#Column({
allowNull: false,
type: DataType.STRING(2000),
})
url?: string;
#Column({
allowNull: true,
type: DataType.STRING(512),
})
description?: string;
#Column({
allowNull: false,
type: DataType.BOOLEAN,
})
is_3d?: boolean;
#Column({
allowNull: false,
type: DataType.BOOLEAN,
})
is_360?: boolean;
#ForeignKey(() => User)
#Column({
field: 'user_id',
allowNull: false,
type: DataType.INTEGER,
})
userId?: number;
#Column({
field: 'created_at',
allowNull: false,
type: DataType.DATE,
})
createdAt?: Date;
#Column({
field: 'updated_at',
allowNull: false,
type: DataType.DATE,
})
updatedAt?: Date;
}
Repository create signiture:
const db = DB.getInstance();
const videosRepository = db.getRepository(Video);
const transaction = await db.transaction();
try {
const saved = await videosRepository.create({ ...video }, { transaction });
await transaction.commit();
return saved;
} catch (err) {
await transaction.rollback();
if (err instanceof Error) logger.error(err.message);
throw new InternalServerError();
}
We have a sequelize singleton which is what db is, the video object is created from an express request body.
Also this used to work, I could add a ts-ignore comment and it will work.
sequelize: 6.15.1
sequelize-typescript: 2.1.2
typescript: 4.5.5
So I've come across 2 solutions to this problem, the first is as outlined above:
repository.create({ ...myModel })
And the second is:
repository.create(myModel as any)
This was my solution ...
export default class Video extends Model<InferAttributes<Video>, InferCreationAttributes<Video>> {
https://sequelize.org/master/manual/typescript.html
Related
I want to create a model for a user table. I tried to follow the tutorial here in the docs
Here is my file:
/* eslint-disable #typescript-eslint/naming-convention */
import { RandomUUIDOptions } from "crypto";
import {
DataTypeAbstract,
DataTypes,
Model,
Optional,
Sequelize,
InferAttributes,
} from "sequelize";
// possible attributes of our model
interface User {
id: RandomUUIDOptions;
username: string;
password: string;
trainer_id: RandomUUIDOptions;
researcher_id: RandomUUIDOptions;
}
// type of the object passed to Sequelize’s model.create
type UserCreationAttributes = Optional<User, "trainer_id" | "researcher_id">;
export default function createUser(
sequelize: Sequelize,
DataTypes: DataTypeAbstract,
Model: Model
) {
class User extends Model<InferAttributes<User>, UserCreationAttributes> {
declare id: RandomUUIDOptions;
declare username: string;
declare password: string;
declare trainer_id: RandomUUIDOptions;
declare researcher_id: RandomUUIDOptions;
}
User.init(
{
id: {
type: DataTypes.UUID,
primaryKey: true,
allowNull: false,
defaultValue: DataTypes.UUIDV4,
},
username: {
type: DataTypes.STRING,
unique: true,
allowNull: false,
},
password: {
type: DataTypes.STRING,
allowNull: false,
},
trainer_id: {
type: DataTypes.UUID,
allowNull: true,
references: {
model: "trainer",
key: "id",
},
},
researcher_id: {
type: DataTypes.UUID,
allowNull: true,
references: {
model: "researcher",
key: "id",
},
},
},
{
sequelize,
modelName: "User",
}
);
return User;
}
However, I got some problems:
the Model type throws this error : Type 'Model<any, any>' is not a constructor function type.
the User.init throws this error: Property 'init' does not exist on type 'typeof User'.
the dataTypes properties are throwing this type of error: Property 'some_type_like_string_or_UUID' does not exist on type 'AbstractDataTypeConstructor'.
Why am I getting these errors? How can I solve this?
So I have this entity...
import { Column, Entity, PrimaryColumn } from "typeorm";
#Entity('Users')
export class User {
#PrimaryColumn({ type: 'nvarchar', length: 36, unique: true })
UserId: string;
#Column({ type: 'nvarchar', length: 100, unique: true })
Email: string;
#Column({ type: 'nvarchar', length: 36 })
CrmUserId: string;
#Column({ type: 'nvarchar', length: 36, nullable: true })
AzureB2CUserId: string;
#Column({ type: 'bit', default: false })
IsActive: boolean;
#Column({ type: 'nvarchar', length: 100 })
CreatedBy: string;
#Column({ type: 'datetime' })
CreatedDate: Date;
#Column({ type: 'nvarchar', length: 100, nullable: true })
UpdatedBy: string;
#Column({ type: 'datetime', nullable: true })
UpdatedDate: Date;
}
and using TypeORM I want to get one record by the email, not the UserId. So I had this on the repository.
public async getUserByEmail(email: string): Promise<User | null> {
let _res = await this._userRepository.findOne({ where: { Email: email, IsActive: true }})
return _res;
}
But it always returns a null, even if the record exists, I was thinking of doing it with a CreateQueryBuilder, like this...
public async getUserByEmail(email: string): Promise<User | null> {
let _res = await this._userRepository.createQueryBuilder()
.select()
.from(User, "Users")
.where('email = :emailParameter', { email })
.getOne();
return _res;
}
But the result is the same, I keep getting null, I have no idea what I am doing wrong, because it works if I use the primary key on the findOne and findOneBy. Any help out there with this?
If you are using MongoDB then use import { MongoRepository, Repository } from 'typeorm'; for the _userRepository type.
While using _userRepository findOneBy wrap the userId parameter in import { ObjectId } from 'mongodb' const query = {"_id":ObjectId(req.params.productId)}
OR have a look at this example
know this due to the fact that where in the "entity" you give the default value in the "delete_date" to the user, you must definitely give null.
I wrote in my entity like this
#DeleteDateColumn({
name: 'deleted_date',
type: 'timestamp',
default: null, // default: () => 'CURRENT_TIMESTAMP(6)',
})
public deletedAt: Date;
#CreateDateColumn({
name: 'created_date',
type: 'timestamp',
default: () => 'CURRENT_TIMESTAMP(6)',
})
public createdAt: Date;
#UpdateDateColumn({
name: 'updated_date',
type: 'timestamp',
default: null
onUpdate: 'CURRENT_TIMESTAMP(6)',
})
public updatedAt: Date;
you can't give values to a "delete_date" when you create a user
I want to make the next SQL instruction, but I don't know how. I tried many ways, but I can't make it work in sequelize. This is the SQL instruction:
SELECT interes_revision.nombre FROM interes_revision
LEFT JOIN asociar ON interes_revision.id = asociar.id_interes_revision
WHERE asociar.id_par_evaluador = <req.params>
And tbe models asociated with that instruction:
InteresRevision.ts
#Table({
timestamps:false,
tableName: "interes_revision"
})
export class InteresRevision extends Model {
#Column({
type: DataType.INTEGER,
primaryKey:true,
allowNull:false,
autoIncrement:true
})
id!:number;
#Column({
type: DataType.STRING,
allowNull:true,
unique: true
})
nombre!:string;
#HasMany(() => Asociar)
asociar!: Asociar[];
ParEvaluador.ts
#Table({
timestamps:false,
tableName: "par_evaluador"
})
export class ParEvaluador extends Model {
#Column({
type: DataType.INTEGER,
primaryKey:true,
allowNull:false,
autoIncrement:true
})
id!:number;
#HasMany(() => Asociar)
asociar!: Asociar[];
}
Asociar.ts
#Table({
timestamps:false,
tableName: "asociar"
})
export class Asociar extends Model {
#ForeignKey(() => ParEvaluador)
#Column({
allowNull: false
})
id_par_evaluador!: number;
#ForeignKey(() => InteresRevision)
#Column({
allowNull: false
})
id_interes_revision!: number;
#BelongsTo(() => InteresRevision)
interesRevision!: InteresRevision;
#BelongsTo(() => ParEvaluador)
parEvaluador!: ParEvaluador;
}
The instruction in particular would show only a column from one table, and would return a list of "interes_revision" where the "id_par_evaluador" on "asociar" matches the req.params' id you want to match. Any help is welcome.
I am trying to connect two tables with sequelize, but the primary key and foreign key have different data types,
I am getting errors when I triggered the query. it is not possible to change the schema, it will affect the whole data.
Can you provide some possible solutions to fix this error?
The only way to join them in findAll/findOne calls is to use the on option in the include option:
const items = Items.findAll({
include: [{
model: ChildItem,
on: {
// assuming that childId is an integer and parentId is a string
childId: Sequelize.cast('parentId', 'integer')
}
}]
})
I stumbled on the same issue here is how i resolved it:
The cause of the issue is that Sequlize doing typecasting from integer to string when you create in one schema primaryKey as id and give it type of integer and in another schema you use it as secondaryKey with alias for instance userId so when you reference to connected schema from main schema you receive error operator does not exist: character varying = integer
Code examples (i am using Sequilze-typescript):
main schema where id is id
#Table({ tableName: 'users' })
export class User extends Model<User,> {
#Column({
type: DataType.INTEGER,
primaryKey: true,
unique: true,
autoIncrement: true
})
id: number
#HasMany(() => Post)
posts:Post[]
}
secondary schema that uses User id as secondaryKey
#Table({ tableName: 'posts' })
export class Task extends Model<Task, TaskCreationAttributes> {
#Column({
type: DataType.INTEGER,
primaryKey: true,
unique: true,
autoIncrement: true
})
id: number
#BelongsTo(() => User)
creator: User
#ForeignKey(() => User)
#Column({ type: DataType.INTEGER, allowNull: false })
userId: number
}
so here even-through we explicitly telling that our secondaryKey is number when we query User schema, Sequlize casts id -> userId, integer -> string
so in order to prevent the typecasting from id -> userId we can change
main schema(User) to
#Column({
type: DataType.INTEGER,
primaryKey: true,
unique: true,
autoIncrement: true
})
userId: number
and secondary(Post) schema to:
#Column({
type: DataType.INTEGER,
primaryKey: true,
unique: true,
autoIncrement: true
})
postId: number
#BelongsTo(() => User)
creator: User
#ForeignKey(() => User)
#Column({ type: DataType.INTEGER, allowNull: false })
userId: number
so no collision and typecasting will be done
I have 1:n association Company -> CompanySettings,
what I want is, I find Company by Id then create companySettings by that instance and automatically fill the foreign key attribute
using sequelize-typescript
I have tried $get, $add, $count from association function and its works
but when I try $create function it gives me the errors
Company Class
#Table({
tableName: 'company',
})
export class Company extends Model<Company> {
#Column({ allowNull: false, type: DataType.STRING(50) })
name: string
#CreatedAt
#Column({ field: 'created_at' })
createdAt: Date
#UpdatedAt
#Column({ field: 'updated_at' })
updatedAt: Date
#Column({ field: 'is_deleted', defaultValue: 'f', type: DataType.BOOLEAN })
isDeleted: boolean
#HasMany(() => CompanySettings)
settings: CompanySettings[]
CompanySettings class
#Table({
tableName: 'company_settings',
})
export class CompanySettings extends Model<CompanySettings> {
#ForeignKey(() => Company)
#Column
idCompany: number
#BelongsTo(() => Company)
company: Company
#Column({ type: DataType.ENUM('default', 'alwaysApprove', 'alwaysAsking') })
defaultBookingApproval: defaultBookingOptions
#Column({ type: DataType.SMALLINT })
budgetPeriod: number
#Column({ type: DataType.CHAR(2) })
startDate: string
#CreatedAt
#Column
createdAt: Date
#UpdatedAt
#Column
updatedAt: Date
#Column({ defaultValue: 'f', type: DataType.BOOLEAN })
isDeleted: boolean
controller
const companies = await Company.findOne()
return await companies.$create('settings', { startDate: '22' })
After finding the source instance, I want to create new instance related to the source instance
But the errors I got is shown below
TypeError: this[("create" + string_1.capitalize(...))] is not a function
tell me where am i wrong ?
if you want create one setting, you should write code like below
companies.$create('setting', { startDate: '22 })
instead of
companies.$create('settings', { startDate: '22' })
if you want create bulk, you should like below
companies.$create('settings', [{ startDate: '22', { startDate: '23'}] })
fuzes solutions should work nicely,
If you want to attach this newly created instance to source instance then:
const setting = await companies.$create('setting', { startDate: '22' });
companies.set('settings', [...companies.settings, setting]);