Handling multiple tables reference Id in a single column in PostgreSQL - node.js

In the PostgreSQL database, I want to manage a History table that relates to multiple modules in the application. It contains a 'common_id' field which stores the reference ids of each module, ie; different tables.
eg: If I am creating the history related to 3 tables. Profile, Event, Settings the history table will contain info as follows.
Common_id | Type
user_id | Profile
event_id | Event
settings_id | Settings
And I need to query the data based on each Id. Currently, PostgreSQL creates a default foreign key reference to one of the related tables and it causes errors while inserting into other tables. Below is the code snippet.
Repository Function
createHistory: async ({ models }, args) => {
return await models.History.create(args);
},
Model
'use strict';
const { Model } = require('sequelize');
module.exports = (sequelize, DataTypes) => {
class History extends Model {
static associate(models) {
History.belongsTo(models.Training, {
foreignKey: 'common_id',
as: 'trainingId',
});
History.belongsTo(models.Club, {
foreignKey: 'club_id',
as: 'club',
});
History.belongsTo(models.User, {
foreignKey: 'created_by',
as: 'uid',
});
}
}
History.init(
{
message: DataTypes.STRING,
users_associated: DataTypes.ARRAY(DataTypes.INTEGER),
history_type: DataTypes.STRING,
},
{
timestamps: true,
sequelize,
paranoid: true,
modelName: 'History',
}
);
return History;
};

Related

How to add record to "through" table in Sequelize

I have this User Model
'use strict';
const { Model } = require('sequelize');
module.exports = (sequelize, DataTypes) => {
class User extends Model {
/**
* Helper method for defining associations.
* This method is not a part of Sequelize lifecycle.
* The `models/index` file will call this method automatically.
*/
static associate(models) {
// define association here
User.belongsToMany(models.Programs, { through: 'UserPrograms' })
}
};
User.init({
id: {
type: DataTypes.UUID,
defaultValue: DataTypes.UUIDV4,
primaryKey: true
},
username: DataTypes.STRING,
}, {
sequelize,
modelName: 'User',
paranoid: true
});
return User;
};
And I have a program model
'use strict';
const { Model } = require('sequelize');
module.exports = (sequelize, DataTypes) => {
class Programs extends Model {
/**
* Helper method for defining associations.
* This method is not a part of Sequelize lifecycle.
* The `models/index` file will call this method automatically.
*/
static associate(models) {
Programs.belongsToMany(models.User, { through: 'UserPrograms' })
}
};
Programs.init({
id: {
type: DataTypes.UUID,
defaultValue: DataTypes.UUIDV4,
primaryKey: true
},
name: DataTypes.STRING,
}, {
sequelize,
modelName: 'Programs',
paranoid: true
});
return Programs;
};
When I create a user, I send an array with some program ids (they exist in the program table) and I want to be able to "assign" those programs to the user that I'm creating. But I don't know the correct syntax. The documentation talks about creating a new record for the second table (program in my case) but it doesn't say anything about creating it with an id that already exists (at least I didn't find anything, I looked here https://sequelize.org/master/manual/advanced-many-to-many.html)
I figured it out. First I added the user as I normally would, then with the user object that is created, I did the following
//Create user programs
programs.map(async(v) => {
const program = await db.Programs.findByPk(v.ProgramId);
newUser.addProgram(program);
})
It worked perfectly.

Problem with Sequelize and specifying foreign key in an association

I have placements that belong to an App. The foreign_key on the placements table is appId (not AppId). I have specified as follows but get the error:
Unhandled rejection SequelizeDatabaseError: column Placements.AppId does not exist
My Placement Model:
'use strict';
const App = require('./').App
module.exports = (sequelize, DataTypes) => {
const Placement = sequelize.define('Placement', {
name: DataTypes.STRING,
isActive: DataTypes.BOOLEAN,
}, {
tableName: 'placements'
});
Placement.associate = function(models) {
Placement.belongsTo(models.App, { foreignKey: 'appId'})
// associations can be defined here
};
return Placement;
};
How do I tell Sequelize that the foreignKey is appId and not AppId?
Hmm... I don't see nothing wrong with your code. Which version of Sequelize are you using?
Just a couple of non-related comments:
You don't need to import App, it is already in models object.
I think you don't need tableName parameter either.
Try to use the field property
Placement.belongsTo(models.App, {
foreignKey: {
name: 'AppId',
field: 'appId'
}
}
)
You need to have the AppId columns present in App model. Explicitly passing { foreignKey: 'appId'}) to belongsTo makes Sequelize use the key as it is.
So you need to create an App model which has -
const App = sequelize.define('App', {
AppId: DataTypes.INTEGER,
..
}, {
tableName: 'App'
});
Another options would be to change this
Placement.belongsTo(models.App, { foreignKey: 'appId'})
to
Placement.belongsTo(models.App)
This way Sequelize automatically handles all relations between the models.

How to return the result set of sequelize query in camel case fashion?

I have defined a table schema in the database in an underscored fashion but I want to return the result set API response in camel case fashion. I know I can process the underscored object returned by sequelize and convert it into camelcase fashion. Is there any functionality to return the response of a query in camelcase fashion in sequelize itself?
To archieve this you need to use field when defining your model.
module.exports = (sequelize, DataTypes) => {
const yourTable = sequelize.define('yourTable', { // table name use it for Sequelize
camelCase: { //camelCase name that you'll use with sequelize.
field: 'under_score', //underscore name on yor database.
type: DataTypes.STRING
},
keyId: { //need to the same with association
field: 'key_id',
type: DataTypes.INTEGER
},
}, {
tableName: 'your_table', // then name of the table on the db
underscored: true,
});
yourTable.associate = (models) => {
yourTable.belongsTo(models.otherTable, {
as: 'Something',
foreignKey: 'key_id', //put attention here and keyId above.
onDelete: 'cascade'
});
}
}

Sequelize does not create hasMany associations but foreign keys defined in DB

Altough it seems my associations defined in the database when I call create on my manufacturer model associations does not create.
These are my associated models.
ManufacturerText.associate = function (models) {
models.manufacturer_text.belongsTo(models.language, {
as: 'language'
});
models.manufacturer_text.belongsTo(models.manufacturer, {
as: 'manufacturer'
});
};
ManufacturerVideo.associate = function (models) {
models.manufacturer_video.belongsTo(models.language, {
as: 'language'
});
models.manufacturer_video.belongsTo(models.video_type, {
as: 'video_type'
});
models.manufacturer_video.belongsTo(models.manufacturer, {
as: 'manufacturer'
});
}
And this is the main model:
```
Manufacturer.associate = function(models) {
// models
models.manufacturer.hasMany(models.manufacturer_text, {foreignKey:'manufacturer_id', as: 'manufacturer_translations' });
models.manufacturer.hasMany(models.manufacturer_video, {foreignKey:'manufacturer_id', as: 'manufacturer_videos' });
models.manufacturer.hasMany(models.inspiration_image, {foreignKey:'manufacturer_id', as: 'inspirations' });
models.manufacturer.belongsTo(models.file, {as: 'image'});
models.manufacturer.belongsTo(models.file, {as: 'header_image'});
};
none of the two associations above work.
When I inspect DB with MySQL Workbench it seems associations defined properly.
It seems the problem caused by the input.
When you send id attribute even doesn't matter if its null, sequelize do not understand that it is an INSERT
I was sending a record like below;
{
delivery_days: 10,
image_id: 4,
{ id: null, url: 'http://url', ... }
}

Try to create HABTM connection Sequelize.js

I'm trying to create a HABTM relationship with Sequelize but I can't get it done.... I still receive an error message:
return (tableName1.toLowerCase() < tableName2.toLowerCase()) ? (tableName1
^
TypeError: Cannot call method 'toLowerCase' of undefined
I have a User model, a Book model and an UserBooks model. And ofcourse my database contains a "users" table, "user_books" table and "books" table.
UserBooks model:
module.exports = function(schema, DataTypes) {
var UserBooks = schema.define('UserBooks', {
}, {
tableName: 'user_books', // this will define the table's name
timestamps: false // this will deactivate the timestamp columns
});
UserBooks.sync();
return UserBooks;
};
User model:
module.exports = function(schema, DataTypes) {
var User = schema.define('User', {
keywords: DataTypes.STRING
}, {
tableName: 'users', // this will define the table's name
timestamps: false ,// this will deactivate the timestamp columns
syncOnAssociation:false
});
User.hasMany(Book, { foreignKey: 'user_id', through: UserBooks });
User.sync();
return User;
};
Book model:
module.exports = function(schema, DataTypes) {
var Book = schema.define('Book', {
keywords: DataTypes.STRING
}, {
tableName: 'books', // this will define the table's name
timestamps: false ,// this will deactivate the timestamp columns
syncOnAssociation:false
});
Book.hasMany(User, { foreignKey: 'book_id', through: UserBooks });
Book.sync();
return Book;
};
In your User model you are trying to create an association with a model that is not defined in that scope. In User.js, you only have access to User, not Book or UserBooks which are undefined. Thats whats causing your error.
You can either create associations in the place where you import all your models into your app, or in the models file by importing the models you want to associate with (bevare of circular imports). Your user model could be changed to:
module.exports = function(schema, DataTypes) {
var Book = schema.import(__dirname + '/book');
var UserBooks = schema.import(__dirname + '/userbooks');
var User = schema.define('User', {
keywords: DataTypes.STRING
}, {
tableName: 'users', // this will define the table's name
timestamps: false ,// this will deactivate the timestamp columns
syncOnAssociation:false
});
User.hasMany(Book, { foreignKey: 'user_id', through: UserBooks });
Book.hasMany(User, { foreignKey: 'book_id', through: UserBooks });
return User;
};
For another example of how to do it, see http://sequelizejs.com/articles/express#minimal-express-app
Also, I've removed the call to User.sync from your code. Sync is an async call, while import is sync. This means that your are defining your model, starting to sync it to the DB, and then returning it, before you know that it has finished syncing. This means you could potentially be trying to work create instances with it before the table has been created. Instead, you should use sequelize.sync to sync all your models at once, and attach a callback to wait for the sync to finish (see the link I posted for a code example)

Resources