How to add record to "through" table in Sequelize - node.js

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.

Related

Why when I generate a model with sequelize has a different structure than others that includes a class instead of a const?

I'm new with sequelize and I've seen many tutorials on how to create models with sequelize, but when I did, this is the structure that created it.
'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
}
};
Users.init({
nickname: DataTypes.STRING,
password: DataTypes.STRING
}, {
sequelize,
modelName: 'Users',
});
return User;
};
But the other structure that I see is "most popular" is this.
'use strict';
module.exports = (sequelize, DataTypes) => {
const User = sequelize.define('User', {
nickname: DataTypes.STRING,
password: DataTypes.STRING
},{
underscored: true
});
return User;
};
How can I generate a model with the second structure using "sequelize model:generate" in console?
Thanks for your time.
According to the official documentation both methods to define a model are equal to each other. define is more popular because it's an older approach then classes and that's all. I recommend to use a newer one because I suppose it will have a longer life cycle and support.

Sequelize defining tables pluralized when using sync

I've written migrations to define my tables in a seperate service. Im then using a node.js service to run sequelize where i'm defining my models like this:
import Sequelize from "sequelize";
import sequelize from "../config/sequelize";
export const MasterPlaylist = sequelize.define("Master_playlist", {
id: { type: Sequelize.STRING, primaryKey: true },
updated_at: {
type: Sequelize.DATE,
defaultValue: new Date(),
allowNull: false,
},
});
Im then importing this and calling it like so:
import Sequelize from "sequelize";
import sequelize from "./config/sequelize";
import { Label } from "./models/Label";
async function run(){
await sequelize.sync();
const label = await Label.findOne();
console.log("done", label);
}
run()
Have simplified things slightly here, but this is whats being run.
When i run this it creates new tables but pluralised so i end up with the correctly named tables as inserted by the migrations and then pluralised copies.
For example for this table i end up with 'Master_playlists'
Anyone got an idea whats happening here?
here is my config file:
import Sequelize from "sequelize";
const sequelize = new Sequelize(
process.env.DB,
process.env.USERNAME,
process.env.PASSWORD,
{
host: process.env.HOST,
dialect: "postgres",
}
);
async function run() {
await sequelize.authenticate();
}
run();
export default sequelize;
Not exactly sure what the issue is, but sequelize uses inflection to pluralize table names.
By default, when the table name is not given, Sequelize automatically pluralizes the model name and uses that as the table name. This pluralization is done under the hood by a library called inflection, so that irregular plurals (such as person -> people) are computed correctly.
You can control the table names directly using freezeTableName: true which will keep the table name the same as model name:
sequelize.define('User', {
// ... (attributes)
}, {
freezeTableName: true
});
or specify the table name explicitly:
sequelize.define('User', {
// ... (attributes)
}, {
tableName: 'users'
});

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() doesn't return an instance of sequelize

I recently started using Sequelize to get a model of my postgresql database.
To map the database I am using sequelize-auto.
I was able to create an auto generated mapping of my db using sequelize-auto when I sent my arguments this way to its constructor:
init.js
let sequelizeAutoInstance = new SequelizeAuto(dbName,username,password,options)
But it doesn't work when I try to send an instance of Sequelize this way:
new-init.js
let sequelizeInstance = new Sequelize(sequelizeOptions);
sequelizeAutoInstance = new SequelizeAuto(sequelizeInstance)
Looking into sequelize-auto ctor I saw it runs those lines:
if (database instanceof Sequelize) {
this.sequelize = database;
}
but the instance returning form new Sequelize doesn't return an instance of Sequelize.
What did I miss?
thanks
If I got you correct. You need to pass an instance of sequelize to your models. If you are using extension models you can simply pass in the instance into the init options.
const { Sequelize, DataTypes, Model } = require('sequelize');
const sequelize = require("../your/db/file");
class User extends Model {}
User.init({
// Model attributes are defined here
firstName: {
type: DataTypes.STRING,
allowNull: false
},
lastName: {
type: DataTypes.STRING
// allowNull defaults to true
}
}, {
// Other model options go here
sequelize, // We need to pass the connection instance
modelName: 'User' // We need to choose the model name
});
if its functional then
const { Sequelize, DataTypes } = require('sequelize');
const sequelize = require("../your/db/file");
const User = sequelize.define('User', {
// Model attributes are defined here
firstName: {
type: DataTypes.STRING,
allowNull: false
},
lastName: {
type: DataTypes.STRING
// allowNull defaults to true
}
}, {
// Other model options go here
});
All this can be found in the Sequelize models documentation

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