Sequelize mixes camel case and snake case keys in result object - javascript

Am trying to get the resulting plain object from Sequelize create (or any Sequelize query) to be snake case keys throughout. The result objects are mixed however. Here's an example query:
const object = await models.Account.create({
userId,
name,
accountNumber
})
console.log(object.get({ plain: true }))
The result object is mixed keys of camel case and snake case:
{
"id": 2,
"userId": 1,
"name": "baller",
"accountNumber": "1234-123-1234",
"updated_at": "2019-01-07T02:23:41.305Z",
"created_at": "2019-01-07T02:23:41.305Z",
"balance": "0.00",
"deleted_at": null
}
Any idea how to get the result plain object or nested objects to be completely snake case keys only? Upgrade sequelize from ^4.42.0 to ^5.0.0-beta in package.json and happens for both. Not sure what else to try?
Accounts table is all snake case column names:
return queryInterface.createTable('accounts', {
id: {
allowNull: false,
autoIncrement: true,
primaryKey: true,
type: Sequelize.INTEGER
},
user_id: {
allowNull: false,
type: Sequelize.INTEGER,
references: {
model: 'users',
key: 'id'
}
},
name: {
allowNull: false,
type: Sequelize.STRING(50)
},
account_number: {
allowNull: false,
type: Sequelize.STRING(50)
},
Account model has has option underscored: true and camelCase attrs with field in snake case
const Account = sequelize.define('Account', {
userId: {
type: DataTypes.INTEGER,
field: 'user_id',
allowNull: false,
validate: {
notNull(value) {
if (value == null) {
throw new Error('Missing user id')
}
}
}
},
name: {
type: DataTypes.STRING,
field: 'name',
validate: {
notEmpty: true
}
},
accountNumber: {
type: DataTypes.STRING,
field: 'account_number',
validate: {
notEmpty: true
}
},
}, {
tableName: 'accounts',
paranoid: true,
underscored: true
})

For now I'm resolving it with:
import snakeCaseKeys from 'snakecase-keys'
let jsonObject = object.get({ plain: true })
jsonObject = snakeCaseKeys(jsonObject)

Related

Sequlize How to make bulkCreate with associated table and not create new values if theay already exist in table

I Have 2 models. Actors And Movies, they have BelongsToMany Asscociation
const Movie = sequelize.define(
MOVIES,
{
id: {
type: DataTypes.INTEGER,
autoIncrement: true,
primaryKey: true,
},
title: {
type: DataTypes.STRING,
allowNull: false,
},
year: {
type: DataTypes.NUMBER,
allowNull: false,
},
format: {
type: DataTypes.ENUM,
values: [VHS, DVD, BLU_RAY],
allowNull: false,
},
},
{
indexes: [
{
unique: true,
fields: ['title'],
},
],
}
);
const Actor = sequelize.define(
ACTORS,
{
id: {
type: DataTypes.INTEGER,
autoIncrement: true,
primaryKey: true,
},
name: {
type: DataTypes.STRING,
allowNull: false,
},
},
{
indexes: [
{
unique: true,
fields: ['name'],
},
],
}
);
and this logic:
const moviesData = req.files.movies.data.toString();
const newMovies = movieHelper.formatArrayOfMovieObjects(moviesData);
const movies = await Movie.bulkCreate(newMovies, {
include: {
model: Actor,
},
updateOnDuplicate: ['title'],
});
res.json(movies).status(200);
How to make to not create new records if movie.title exist in table
I tried updateOnDuplicate param but it give me this error: [Error: SQLITE_CONSTRAINT: FOREIGN KEY constraint failed
If your SQLite version supports unique constraints/indexes then you can create one indicating the title field and this way the option updateOnDuplicate should work well.

How to Update a DataTypes.ENUM datatype in Sequelize

I have this sequelize model:
var TableName = sequelize.define(
"TableName",
{
id: { type: DataTypes.INTEGER, primaryKey: true, autoIncrement: true },
name: { type: DataTypes.STRING, allowNull: false },
approvedTenure: {
type: DataTypes.INTEGER,
allowNull: false,
defaultValue: 0,
},
status: {
type: DataTypes.ENUM,
values: ["PENDING", "YES", "NO", "DON'T KNOW"],
allowNull: false,
defaultValue: "PENDING",
},
narrative: { type: DataTypes.STRING, allowNull: false, defaultValue: "" },
},
{
timestamps: true,
tableName: "table_name",
freezeTableName: true,
}
);
And when I run the following code trying to update the table:
const data = {id: 23, status: "YES", narrative: "I am good", approvedTenure: 2}
async updateTable(data) {
return await TableName.update(
{
status: data.status,
narrative: data.narrative,
approvedTenure: data.approvedTenure,
},
{
where: { id: data.id },
returning: true,
}
);
}
The table gets updated except the status column with DataTypes.ENUM as the data type. How do I go about updating the status column with the DataTypes.ENUM data type?

How can I parse complex query operations using Sequelize.js?

I have an Option table which has a question_id as a foreign key to table Questions.
Then in Questions table I've 2 foreign keys namely question_category_id and section_id. For the 1st Option part I am able to apply LEFT OUTER JOIN query but also I need to fetch the values of Question_Category and Section table as well.
Let me first clear out how I want my output to be:
Output JSON
"questions": [
{
"id": 9,
"isActive": true,
"question_text": "What is abc ?",
"createdBy": "avis",
"questionCategory": {
"id": 1,
"name": "aptitude"
},
"section": {
"id": 1,
"marks": 5
},
"options": [
{
"id": 1,
"answer": true,
"option_text": "A",
"question_id": 9
},
{
"id": 2,
"answer": false,
"option_text": "B",
"question_id": 9
}
]
}
]
Now I am specifying my database models:
question_category.js
module.exports = (sequelize, Sequelize) => {
const QuestionCategory = sequelize.define('question_category', {
id:{ type: Sequelize.BIGINT, autoIncrement: true, allowNull: false, primaryKey: true },
isActive: { type: Sequelize.BOOLEAN, allowNull: false },
question_category_name: { type: Sequelize.STRING, allowNull: false },
createdBy: { type: Sequelize.STRING, allowNull: false },
createdAt: { type: Sequelize.DATE, allowNull: false, defaultValue: Sequelize.literal('CURRENT_TIMESTAMP') }
}, { timestamps: false });
return QuestionCategory;
};
section.js
module.exports = (sequelize, Sequelize) => {
const Section = sequelize.define('section', {
id: { type: Sequelize.BIGINT, autoIncrement: true, allowNull: false, primaryKey: true },
isActive: { type: Sequelize.BOOLEAN, allowNull: false },
marks_per_question: { type: Sequelize.INTEGER, allowNull: false },
createdBy: { type: Sequelize.STRING, allowNull: false },
createdAt: { type: Sequelize.DATE, allowNull: false, defaultValue: Sequelize.literal('CURRENT_TIMESTAMP') }
}, { timestamps: false });
return Section;
};
question.js
module.exports = (sequelize, Sequelize) => {
const Questions = sequelize.define('questions', {
id:{ type: Sequelize.BIGINT, autoIncrement: true, allowNull: false, primaryKey: true },
isActive: { type: Sequelize.BOOLEAN, allowNull: false },
question_text: { type: Sequelize.STRING, allowNull: false },
createdBy: { type: Sequelize.STRING, allowNull: false },
createdAt: { type: Sequelize.DATE, allowNull: false, defaultValue: Sequelize.literal('CURRENT_TIMESTAMP') }
}, { timestamps: false });
return Questions;
};
option.js
module.exports = (sequelize, Sequelize) => {
const Options = sequelize.define('options', {
id:{ type: Sequelize.BIGINT, autoIncrement: true, allowNull: false, primaryKey: true },
answer: { type: Sequelize.BOOLEAN, allowNull: true },
option_text: { type: Sequelize.STRING, allowNull: false },
createdAt: { type: Sequelize.DATE, allowNull: false, defaultValue: Sequelize.literal('CURRENT_TIMESTAMP') }
}, { timestamps: false });
return Options;
};
In the database.js i.e. the main js file for exporting the models I have associated the models like this:
const dbConfig = require('../config/db.config');
const Sequelize = require('sequelize');
const sequelize = new Sequelize(
dbConfig.DB, dbConfig.USER, dbConfig.PASSWORD,
{
host: dbConfig.HOST,
port: dbConfig.PORT,
dialect: 'mysql',
operatorsAliases: 0
}
);
sequelize.authenticate().then(() => {
console.log('Connection has been established successfully.');
}).catch(err => {
console.error('Unable to connect to the database:', err);
});
const db = {};
db.Sequelize = Sequelize;
db.sequelize = sequelize;
db.QuestionCategory = require('./question_model/question_category')(sequelize, Sequelize);
db.Section = require('./question_model/section')(sequelize, Sequelize);
db.Question = require('./question_model/question')(sequelize, Sequelize);
db.Option = require('./question_model/option')(sequelize, Sequelize);
// Relating Question Category with Questions
db.QuestionCategory.hasMany(db.Question, {
foreignKey: 'question_category_id',
sourceKey: 'id'
});
db.Question.belongsTo(db.QuestionCategory, {
foreignKey: 'question_category_id',
targetKey: 'id'
});
// Relating Sections with Questions
db.Section.hasMany(db.Question, {
foreignKey: 'section_id',
sourceKey: 'id'
});
db.Question.belongsTo(db.Section, {
foreignKey: 'section_id',
targetKey: 'id'
});
// Relating Questions with Options
db.Question.hasMany(db.Option, {
foreignKey: 'question_id',
sourceKey: 'id'
});
db.Option.belongsTo(db.Question, {
foreignKey: 'question_id',
targetKey: 'id'
});
So this is my structure.
Now to achieve the above output format I've written the below logic but it's not outputting the correct JSON:
const db = require('../models/database');
const errors = require('../config/errors').errors;
exports.viewQuestion = (req, res, next) => {
try {
db.Question.findAll({
attributes: { exclude: ['createdAt','section_id','question_category_id'] },
include: [{
model: db.Option,
attributes: { exclude: ['createdAt'] }
}]
}).then(data => {
if(data.length == 0) {
return res.status(200).send({
status: 200,
questions: 'No Data'
});
}
db.QuestionCategory.findAll({
attributes: { exclude: ['createdBy','createdAt','isActive'] },
include: db.Question,
attributes: { exclude: ['id','isActive','question_text','createdBy','createdAt','section_id'] }
}).then(question_category => {
Object.assign(data[0], { 'questionCategories': question_category });
res.status(200).send({
status: 200,
questions: data
});
});
}).catch(err => {
return res.status(204).send(errors.MANDATORY_FIELDS);
});
} catch(err) {
return res.status(204).send(errors.MANDATORY_FIELDS);
}
};
I didn't wrote the logic for Section part yet as I was going Step by Step. The output that I am getting by writing this logic is:
{
"status": 200,
"questions": [
{
"id": 9,
"isActive": true,
"question_text": "What is abc ?",
"createdBy": "avis",
"options": [
{
"id": 1,
"answer": true,
"option_text": "A",
"question_id": 9
},
{
"id": 2,
"answer": false,
"option_text": "B",
"question_id": 9
}
]
}
]
}
The questionCategories didn't got reflected in the output.
Please help me out as I've more scenarios like this and all I can solve depending on this.
If you are using Sequelize to get objects from a DB via models then you should turn them into plain objects before adding some properties. For instance, if you get a collection of objects you should call get({ plain: true }) for each of them.
const plainObj = data[0].get({ plain: true })
Object.assign(plainObj, { 'questionCategories': question_category });
res.status(200).send({
status: 200,
questions: plainObj
});

Sequelize association methods attribures

I have a struggle with associations method. I have users and lists with many to many association, so the sequelize generate for me method user.getLists(); But when I call that mehod result is
[
{
"id": 1,
"title": "Boop",
"created_at": "2018-08-15T14:53:51.276Z",
"updated_at": "2018-08-15T14:53:51.276Z",
"deleted_at": null,
"user_list": {
"owner": true,
"created_at": "2018-08-15T14:53:51.290Z",
"updated_at": "2018-08-15T14:53:51.290Z",
"deleted_at": null,
"user_id": 1,
"list_id": 1
}
}
]
And I can't find a way to remove user_list attribute from it or rename it.
Is there a way to remove this attribute on ORM level or rename it?
For example this is not working:
user.getLists({ attributes: ['id'] });
user.getLists({ attributes: [['user_list', 'random_name']] });
My models look like this
User model:
const user = db.define(
'user',
{
id: {
type: sequelize.INTEGER,
primaryKey: true,
autoIncrement: true,
},
email: {
type: sequelize.STRING,
unique: 'email_unique',
allowNull: false,
validate: {
isEmail: true,
},
},
});
List model:
const list = db.define(
'list',
{
id: {
type: sequelize.INTEGER,
primaryKey: true,
autoIncrement: true,
},
title: {
type: sequelize.STRING,
allowNull: false,
},
},
);
UserList:
const userList = db.define(
'user_list',
{
owner: {
type: sequelize.BOOLEAN,
allowNull: false,
},
},
);
Associations:
User.belongsToMany(
List,
{
through: UserList,
as: {
singular: 'List',
plural: 'Lists',
},
})
List.belongsToMany(User, { through: UserList });

id: null when creating a new item in sequelize

When I try to create a new Conversation item Sequelize will return an object with id: null eventhough there is an valid id in the database. How can I get Sequelize to return the last inserted id to the newly created item?
Conversation.create({
type: 'private',
createdBy: 1,
}).then(conversation => {
reply(conversation);
});
Will return
{
"type": "conversations",
"id": null,
"createdBy": 1,
"created_at": "2016-03-18T01:47:48.000Z"
}
My code:
const Conversation = model.define('Conversation', {
id: {
type: Sequelize.INTEGER,
primaryKey: true,
},
type: {
type: Sequelize.ENUM,
values: ['private', 'group'],
validate: {
isIn: ['private', 'group'],
},
},
createdBy: {
type: Sequelize.INTEGER,
field: 'created_by',
},
}, {
tableName: 'conversations',
timestamps: true,
createdAt: 'created_at',
updatedAt: false,
getterMethods: {
type: () => 'conversations',
},
});
const User = model.define('User', {
id: {
type: Sequelize.INTEGER,
primaryKey: true,
},
firstName: {
type: Sequelize.STRING,
field: 'first_name',
allowNull: false,
},
lastName: {
type: Sequelize.STRING,
field: 'last_name',
allowNull: true,
},
email: {
type: Sequelize.STRING,
allowNull: false,
},
profileImg: {
type: Sequelize.STRING,
field: 'profile_img',
allowNull: false,
},
password: Sequelize.STRING,
}, {
tableName: 'users',
timestamps: true,
createdAt: 'created_at',
updatedAt: 'updated_at',
getterMethods: {
type: () => 'users',
},
});
Conversation.belongsToMany(User, {
foreignKey: 'conversation_id',
otherKey: 'user_id',
through: 'conversation_user',
timestamps: false,
});
User.belongsToMany(Conversation, {
as: 'conversations',
foreignKey: 'user_id',
otherKey: 'conversation_id',
through: 'conversation_user',
timestamps: false,
});
Yo need to put autoIncrement: true in id field:
id: {
type: Sequelize.INTEGER,
primaryKey: true,
autoIncrement: true,
}
Personally I would advice to skip the id column as sequalize does it automatically for you and works nicely.
hope it helps :)
Problem was my MySQL version (5.6 instead of 5.7), updated it and now I'm getting id's of the created items in the promise.
I'm not sure about how Sequelize is working with id field, I get null if I do instance.id, bug I can get the real value at DB if I do the following:
console.info(instance.id); // null
console.info(instance.get('id')); // 25 => Real ID
console.info(instance.getDataValue('id')); // 25 => Real ID
Something similar is happening with other fields like createdAt and updatedAt.
In order to get the real value at id field and other related fields, I added following logic to Model declaration:
class FooModel extends Model {
// ...
/**
* #inheritdoc
*/
public async save(options?: SaveOptions<TModelAttributes>): Promise<this> {
await super.save(options);
this.loadBaseData();
return this;
}
/**
* #inheritdoc
*/
public async reload(options?: FindOptions<TModelAttributes>): Promise<this> {
await super.reload(options);
this.loadBaseData();
return this;
}
private loadBaseData() {
this.id = this.getDataValue('id');
this.createdAt = this.getDataValue('createdAt');
this.updatedAt = this.getDataValue('updatedAt');
}
}
because if you only build without save it then:
instance.id // null
so you need:
instance.save()
instance.id // someNumber

Categories

Resources