Sequelize Association throws an error in Create - javascript

I'm trying to do a simple association with sequelize in my NodeJS API, the idea is simple, I want to create a person and his type in the same moment. I try to follow the docs, but, when the create function is called Sequelize throws the error
"TypeError: Cannot read property 'getTableName' of undefined"
Bellow is the code used:
Models
Person.js
import Sequelize from 'sequelize';
export default (sequelize) => {
const Person = sequelize.define('person', {
email: { type: Sequelize.STRING },
nickname: { type: Sequelize.STRING(60) },
fullname: { type: Sequelize.STRING(60) },
observation: { type: Sequelize.TEXT },
}, { underscored: true, freezeTableName: true });
Person.associate = (models) => {
Person.hasOne(models.PersonType, {
foreignKey: 'person_id',
});
};
return Person;
};
PersonType.js
import Sequelize from 'sequelize';
export default (sequelize, DataTypes) => {
const PersonType = sequelize.define('person_type', {
type: { type: Sequelize.STRING(14), unique: true },
}, { underscored: true, freezeTableName: true });
PersonType.associate = (models) => {
PersonType.belongsTo(models.Person, {
foreignKey: 'person_id',
});
};
return PersonType;
};
index.js
import Sequelize from 'sequelize';
import console from 'console';
const database = process.env.DB_URL;
const sequelize = new Sequelize(database, {
dialect: 'postgres',
underscored: true,
});
const models = {
Person: sequelize.import('./person'),
PersonType: sequelize.import('./personType'),
};
Object.keys(models).forEach((modelName) => {
if ('associate' in models[modelName]) {
models[modelName].associate(models);
}
});
models.sequelize = sequelize;
models.Sequelize = Sequelize;
export default models;
The file where I try to execute the operation:
import models from '../models';
export default class PersonOperations {
constructor(db) {
this.db = db; // db here is the sequelize model of Person
}
create(person) {
return this.db.create({
email: person.email,
nickname: person.nickname,
observation: person.observation,
personType: {
type: person.personType.type,
},
}, {
include: [{
model: models.PersonType,
include: [person.personType],
}],
});
}
The error:
TypeError: Cannot read property 'getTableName' of undefined
at Function._validateIncludedElement (/home/~/node_modules/sequelize/lib/model.js:465:30)
at options.include.options.include.map.include (/home/~/node_modules/sequelize/lib/model.js:395:37)
at Array.map (<anonymous>)
at Function._validateIncludedElements (/home/~/node_modules/sequelize/lib/model.js:390:39)
I have already tried a lot of different things that I have found, but none of them helped me.
I am using freezeTableName, I have already tried to remove that, drop the database and create again, but the problem still persists.

Just Comment out the below code block from Person.js and run the code
Person.associate = (models) => {
Person.hasOne(models.PersonType, {
foreignKey: 'person_id',
});
};
Reason of error :
Here you are creating the circular dependecies , using
models.PersonType inside models.Person and then after using
models.Person inside models.PersonType
hasOne and belongsTo both are the same thing only diff is (READ) :
Player.belongsTo(Team) // `teamId` will be added on Player / Source model
Coach.hasOne(Team) // `coachId` will be added on Team / Target model

Related

How to include related models in Sequelize?

I am trying to retrieve all transactions and include all related stripePayments but get the error include.model.getTableName is not a function.
I have a transactions table that has many stripePayments. This can seen with the following sequelize models:
src/models/transaction.js
module.exports = (sequelize, DataTypes) => {
class Transaction extends Model {
static associate(models) {
this.hasOne(models.StripePayment, {
as: 'StripePayment',
foreignKey: 'stripePaymentId'
});
}
}
Transaction.init(
{
id:{
allowNull: false,
primaryKey: true,
type: DataTypes.UUID
},
stripePaymentId: {
type: DataTypes.UUID,
allowNull: false,
foreignKey: true,
references: {
model: stripePayment,
key: 'id',
},
}
},
{
sequelize,
modelName: 'Transaction',
}
);
return Transaction;
};
src/models/stripePayment.js
module.exports = (sequelize, DataTypes) => {
class StripePayment extends Model {
static associate(models) {
this.belongsTo(models.Transaction, {
as: 'Transaction',
foreignKey: 'stripePaymentId'
});
}
}
StripePayment.init(
{
id:{
allowNull: false,
primaryKey: true,
type: DataTypes.UUID,
defaultValue: DataTypes.UUIDV4
}
},
{
sequelize,
modelName: 'StripePayment',
}
);
return StripePayment;
};
I then have an index file that imports all the models and calls the association method like so:
src/models/index.js
const db = fs
.readdirSync(__dirname)
.filter(isModelFile)
.reduce((acc, file) => {
const model = require(path.join(__dirname, file))(sequelize, Sequelize.DataTypes);
acc[model.name] = model;
return acc;
}, {});
Object.keys(db).forEach((modelName) => {
if (db[modelName].associate) {
db[modelName].associate(db);
}
console.log(db[modelName], db[modelName].association())
});
The console log outputs the following:
StripePayment, { Transaction: Transaction }
Transaction, { StripePayment: StripePayment}
which I assume means the association has worked?
However, when running a query like:
await models.Transaction.findAll({
where: { id: "my_id" },
include: [
{
model: 'StripePayment',
as: 'stripePayments'
}
]
});
is when I get the error include.model.getTableName is not a function.
What is causing this to fail?
Have I defined the relationships correctly in the models?
Turns out the issue was with my query. I needed to specify the model instance, not the name of the model as a string:
I.e. I needed to do this:
await models.Transaction.findAll({
where: { id: "my_id" },
include: [
{
model: models.StripePayment,
as: "StripePayment", // match the alias specified in the model
attributes: ['id', 'status'], // need to select attributes
}
]
});

How to solve EagerLoadingError [SequelizeEagerLoadingError]: Table_B is not associated to Table_A?

I am trying to fetch data from Table_A and Table_B using node and sequelize
Table Structure
Table_A:
id PK
name Text
Table_B:
id PK
a_id FK_tableA_id
name Text
Model
TableA.js
'use strict';
const DataTypes = require('sequelize').DataTypes;
module.exports = (sequelize) => {
const Table_A = sequelize.define('Table_A', {
id: {
type: DataTypes.UUID,
allowNull: false,
primaryKey: true
},
name: {
type: DataTypes.TEXT,
allowNull: true
}
});
Table_A.associate = models => {
Table_A.belongsTo(models.Table_B, { as: 'tb' });
}
return Table_A;
};
TableB.js
'use strict';
const DataTypes = require('sequelize').DataTypes;
module.exports = (sequelize) => {
const Table_B = sequelize.define('Table_B', {
id: {
type: DataTypes.UUID,
allowNull: false,
primaryKey: true
},
a_id: {
type: DataTypes.UUID,
allowNull: false,
defaultValue: null
},
name: {
type: DataTypes.TEXT,
allowNull: true
}
});
return Table_B;
};
I am getting below error while I am trying to run the query using sequelize, Can you please guide me where I am making the mistake?
Error
EagerLoadingError [SequelizeEagerLoadingError]: Table_B is not associated to Table_A!
at Function._getIncludedAssociation (C:\Project\test\FilterTest\node_modules\sequelize\dist\lib\model.js:545:13)
at Function._validateIncludedElement (C:\Project\test\FilterTest\node_modules\sequelize\dist\lib\model.js:482:53)
at C:\Project\test\FilterTest\node_modules\sequelize\dist\lib\model.js:401:37
at Array.map (<anonymous>)
at Function._validateIncludedElements (C:\Project\test\FilterTest\node_modules\sequelize\dist\lib\model.js:397:39)
at Function.aggregate (C:\Project\test\FilterTest\node_modules\sequelize\dist\lib\model.js:1204:12)
at Function.count (C:\Project\test\FilterTest\node_modules\sequelize\dist\lib\model.js:1252:31)
at async Promise.all (index 0)
at async Function.findAndCountAll (C:\Project\test\FilterTest\node_modules\sequelize\dist\lib\model.js:1268:27)
index.js
'use strict';
const { Op } = require('sequelize');
const {sequelize, connect } = require('./db');
const uninitModels = require('./models');
let initModels = uninitModels(sequelize);
initModels = { connection: sequelize, ...initModels }
const {
Table_A, Table_B
} = initModels;
function dbCall(final) {
Table_A.findAndCountAll(final).then((result)=>{
console.log(result)
}).catch((err)=>{
console.log(err)
})
}
function data() {
let final = {
include: [
{
model: Table_B,
attributes: ['id', 'name', 'a_id'],
as: 'tb'
}
]
}
dbCall(final);
}
data();
I suppose you didn't register associations that should be registered by calling associate methods of models.
Also you confused how models are linked. If a model1 has a foreign key field pointing to a model2 then an association should be model1.belongsTo(model2).
In your case it should be:
Table_A.associate = models => {
Table_A.hasMany(models.Table_B, { as: 'tb', foreginKey: 'a_id' });
}
and in the model Table_B:
Table_B.associate = models => {
Table_B.belongsTo(models.Table_A, { as: 'ta', foreginKey: 'a_id' });
}
Pay attention to foreignKey option, you need to indicate it explicitly because your foreign key field is named other than Table_A+id.
I got stuck on this same error (for what seemed like an eternity) and finally realized that there was a big flaw in the way I was declaring my associations.
Incorrect:
Account.associate = function (models) {
Account.hasMany(models.History, {
onDelete: "cascade"
});
};
Account.associate = function (models) {
Account.hasMany(models.User, {
onDelete: "cascade"
});
};
In hindsight, this was a really silly oversight doing two declarations here. tl;dr the 2nd declaration was canceling out the 1st one.
Correct:
Account.associate = function (models) {
Account.hasMany(models.History, {
onDelete: "cascade"
});
Account.hasMany(models.User, {
onDelete: "cascade"
});
};
One declaration with multiple function calls for the win.

How to call mongoose method in the select method

I have a mongoose model that represents a player and want to be able to fetch the player and when selecting the player, want to call isReady like a getter.
The model looks like so:
const PlayerSchema = new Schema({
user: { type: Schema.Types.ObjectId, ref: "User" },
famousPerson: { type: String }
})
PlayerSchema.methods.isReady = function (cb) {
return Boolean(this.famousPerson)
}
And I want to be able to call it like so:
const player = await PlayerModel
.findOne({_id: playerId})
.select(["_id", "username", "isReady"])
Am I able to set the method on the class as a getter?
You can use mongoose virtuals for this, but to work as expected you need to configure your schema so that it can return virtuals, because by default virtuals will not be included.
const PlayerSchema = new Schema(
{
famousPerson: { type: String },
},
{
toJSON: { virtuals: true },
toObject: { virtuals: true },
}
);
PlayerSchema.virtual("isReady").get(function () {
return Boolean(this.famousPerson);
});
You Can Follow This Code
const player = await PlayerModel
.findOne({_id: playerId})
.select(" +_id +username +isReady)

RepositoryNotFoundError : TypeORM

I am trying to get the following example working:
https://github.com/typeorm/javascript-example/tree/master/src/app3-es6
I am running into the following error:
Error
at new RepositoryNotFoundError (...\node_modules\typeorm\connection\error\RepositoryNotFoundError.js:24:23)
at Connection.findRepositoryAggregator (...\node_modules\typeorm\connection\Connection.js:513:19)
at Connection.getRepository (...\node_modules\typeorm\connection\Connection.js:405:21)
at ...\index.js:27:37
name: 'RepositoryNotFoundError',
message: 'No repository for "Post" was found. Looks like this entity is not registered in current "default" connection?'
here is index.js
const typeorm = require("typeorm"); // import * as typeorm from "typeorm";
const Post = require("./model/Post"); // import {Post} from "./model/Post";
// import Post from './model/Post.js';
const Category = require("./model/Category"); // import {Category} from "./model/Category";
typeorm.createConnection({
driver: {
type: "oracle",
host: "localhost",
port: 1521,
username: "uname",
password: "pwd",
sid: "dev"
},
entities: [
__dirname + "/entity/*.js"
],
autoSchemaSync: true
}).then(function (connection) {
console.log(connection);
let post = new Post.Post();
post.title = "Control flow based type analysis";
post.text = "TypeScript 2.0 implements a control flow-based type analysis for local variables and parameters.";
post.categories = [new Category.Category(0, "TypeScript"), new Category.Category(0, "Programming")];
let postRepository = connection.getRepository(Post.Post);
postRepository.persist(post)
.then(function(savedPost) {
console.log("Post has been saved: ", savedPost);
console.log("Now lets load all posts: ");
return postRepository.find();
})
.then(function(allPosts) {
console.log("All posts: ", allPosts);
});
}).catch(function(error) {
console.log("Error: ", error);
});
Post.js in /model/
/*export */ class Post {
constructor(id, title, text, categories) {
this.id = id;
this.title = title;
this.text = text;
this.categories = categories;
}
}
module.exports = {
Post: Post
};
Category.js
/*export */ class Category {
constructor(id, name) {
this.id = id;
this.name = name;
}
}
module.exports = {
Category: Category
};
PostSchema.js in /entity/
const Post = require("../model/Post"); // import {Post} from "../model/Post";
const Category = require("../model/Category"); // import {Category} from "../model/Category";
const PostSchema = {
target: Post,
columns: {
id: {
primary: true,
type: "int",
generated: true
},
title: {
type: "string"
},
text: {
type: "text"
}
},
relations: {
categories: {
target: Category,
type: "many-to-many",
joinTable: true,
cascadeInsert: true
}
}
};
module.exports = {
PostSchema: PostSchema
};
CategorySchema.js
const Category = require("../model/Category"); // import {Category} from "../model/Category";
const CategorySchema = {
target: Category,
columns: {
id: {
primary: true,
type: "int",
generated: true
},
name: {
type: "string"
}
}
};
module.exports = {
CategorySchema: CategorySchema
};
i dont know what i am doing wrong
It looks like your entity import is not working. If you import via the wildcard:
entities: [
__dirname + "/entity/*.js"
],`
Make sure your model is compiled to js. You also could just import
createConnection({
...,
entities: [
Post,
...
],}).then(...)
For those who are using typescript and experience this problem: Be reminded that you need to include both ts and js file suffixes when specifying the entities-path:
ts used when locally running with ts-node
js used when having
built for production via tsc.
Code:
import * as path from 'path';
// ...
entities: [
// assuming _dirname is your project root
path.resolve(__dirname, '**/*.entity{.ts,.js}'),
],
I had the same problem for months and finally figured out what I was doing wrong.
When you import Entities, make sure the file names are EXACTLY matching. It's not going to throw any errors, but during the run time, it's going to throw the above error.
Ex. In the entity or model classes, if we import like this,
import { FooClass } from "./foo-Class.model";
it's different from
import { FooClass } from "./foo-class.model";
It won't show any errors, but when you try to call the table, it will show the exact same error.
I had the same problem. None of the solutions worked for me. After much debugging I figured out that you'll receive this error if your connection is closed.
So if you are facing this error, make sure your connection is not closed.
try {
connection = getConnection(config.name)
//after adding this if block, I no longer received this error
if (!connection.isConnected) {
await connection.connect();
}
} catch(err) {
connection = await createConnection(config);
}
If it is closed, connect it again.

How to extend Sequelize model

Is there a way to extend (maybe inherit) model to add hooks and fields after model was defined?
So something like this:
User = sequelize.define("user", {
name: sequelize.String
});
makeStateful(User); // adds state,updated,added fields and some hooks
this is not possible at the moment. But you could easily make it work the other way around: Define your mixin before and use that when you define the model:
var Sequelize = require('sequelize')
, sequelize = new Sequelize('sequelize_test', 'root')
var mixin = {
attributes: {
state: Sequelize.STRING,
added_at: Sequelize.DATE
},
options: {
hooks: {
beforeValidate: function(instance, cb) {
console.log('Validating!!!')
cb()
}
}
}
}
var User = sequelize.define(
'Model'
, Sequelize.Utils._.extend({
username: Sequelize.STRING
}, mixin.attributes)
, Sequelize.Utils._.extend({
instanceMethods: {
foo: function() {
return this.username
}
}
}, mixin.options)
)
User.sync({ force: true }).success(function() {
User.create({ username: 'foo' }).success(function(u) {
console.log(u.foo()) // 'foo'
})
})

Categories