Bug Description
after running sequelize sync with options alter: true, all foreign keys in tables set to null.
await sequelize.sync({ alter: true }).then(async (result) => {
await initDataUserGroup();
await initDataUserAdmin();
await initDataGateType();
});
and here my one model which has foreign key :
const { DataTypes } = require("sequelize");
const { sequelize } = require("../services/DatabaseService");
const UserGroup = require("./UserGroup");
const Biodata = require("./Biodata");
const User = sequelize.define("user", {
username: {
type: DataTypes.STRING,
allowNull: false,
unique: true,
validate: {
notNull: {
msg: "Username must not be empty",
},
notEmpty: true,
},
},
email: {
type: DataTypes.STRING,
allowNull: false,
unique: true,
validate: {
notNull: true,
notEmpty: true,
isEmail: true,
},
},
password: {
type: DataTypes.STRING,
allowNull: false,
validate: {
notNull: true,
notEmpty: true,
},
},
});
User.UserGroup = User.belongsTo(UserGroup);
User.Biodata = User.hasOne(Biodata, {
foreignKey: "userId",
as: "biodata",
});
module.exports = User;
What do you expect to happen?
It should not set all foreign keys to null after running sync with options { alter: true }
What is actually happening?
userGroupId in table user is null and userId in table biodata is also null.
Environment
Sequelize version: ^6.6.5
Node.js version: 12.9.1
Dialect: SQLite (SQLite3)
From my issue : https://github.com/sequelize/sequelize/issues/13464
anyone can help me please?
after struggling for a couple of days, I managed to resolve it by disabling and re-enabling foreign keys before and after syncing :
await sequelize.query("PRAGMA foreign_keys = false;");
await sequelize
.sync({ alter: true })
.then(async (result) => {
await initDataUserGroup();
await initDataGateType();
})
.catch((errorMessage) => console.error("sync error = ", errorMessage));
await sequelize.query("PRAGMA foreign_keys = true;");
Feel free to inform me a better solution than the current one.
Related
I have this already created two tables called User and Profile.
This is how my model for User looks like..
const Sequelize = require("sequelize");
const db = require("../db");
const User = db.define("User", {
id: {
type: Sequelize.INTEGER,
primaryKey: true,
autoIncrement: true,
},
name: {
type: Sequelize.STRING,
allowNull: true,
},
email: {
type: Sequelize.STRING,
allowNull: false,
unique: true,
validator: {
isEmail: true,
},
},
});
module.exports = User;
and model for Profile looks like..
const Sequelize = require("sequelize");
const User = require("./User");
const db = require("../db");
const Profile = db.define("Profile", {
image: {
type: Sequelize.STRING,
},
description: {
type: Sequelize.TEXT,
},
});
module.exports = Profile;
Now I want to define a one-to-one relationship between User and Profile such that user will recieve a profileId column.
so i am defining it like this
Profile.hasOne(User, {
foreignKey: {
allowNull: false,
},
});
User.belongsTo(Profile);
Now i am not able to figure out how to write migrations for the newly added foreign key
can anyone help me please..
Thanks.
I got the answer. for someone who is confused like me here is the answer
since the User table already exists, migrations for the foreignkey will look like this
module.exports = {
async up(queryInterface, Sequelize) {
return await queryInterface.addColumn("Users", "ProfileId", {
type: Sequelize.INTEGER,
references: {
model: "Profiles",
key: "id",
},
});
},
async down(queryInterface, Sequelize) {
return await queryInterface.removeColumn("Users", "ProfileId", {
type: Sequelize.INTEGER,
references: {
model: "Profiles",
key: "id",
},
});
},
};
the Users in addColumn and removeColumn is the name of the table in which foreignkey was added.
the ProfileId is the name for foreignkey which you would have specified in hasOne.
hope this helps..
I wrote a BeforeCreate hook in my sequelize model. when i hit create user route then it saying user.user_id can't be null and even before create hook function not executing. I have followed documentation of sequelize.They have mentioned same as I use.I wrote a BeforeCreate hook in my sequelize model. when i hit create user route then it saying user.user_id can't be null and even before create hook function not executing. I have followed documentation of sequelize.They have mentioned same as I use.
const sequelize = require("kvell-db-plugin-sequelize").dbInstance;
const Sequelize = require("kvell-db-plugin-sequelize").dbLib;
const shortid = require("shortid");
const User = sequelize.define(
"user",
{
id: {
type: Sequelize.INTEGER,
autoIncrement: true,
allowNull: false
},
user_id: {
type: Sequelize.STRING,
allowNull: false,
primaryKey: true,
unique: true
},
user_fname: {
type: Sequelize.STRING,
allowNull: false
},
user_lname: {
type: Sequelize.STRING,
allowNull: false
},
user_fullName: {
type: Sequelize.VIRTUAL,
get() {
return `${this.user_fname} ${this.user_lname}`;
},
set(value) {
throw new Error("Do not try to set the `fullName` value!");
}
},
user_email: {
type: Sequelize.STRING,
allowNull: false,
primaryKey: true,
validate: {
isEmail: true
},
unique: {
args: true,
msg: "Email address already in use!"
}
},
user_credential: {
type: Sequelize.STRING,
allowNull: false
},
user_roles: {
type: Sequelize.ARRAY(Sequelize.STRING),
allowNull: false,
defaultValue: ["Admin"]
},
admin: {
type: Sequelize.BOOLEAN,
allowNull: false,
defaultValue: true
},
user_img: {
type: Sequelize.STRING,
allowNull: true
}
},
{
timestamps: true
}
);
User.beforeCreate(async (user, options) => {
console.log("inside hooks");
let id = `user_${shortid.generate()}`;
user.user_id = id;
});
const toJSON = User.prototype.toJSON;
User.prototype.toJSON = function({ attributes = [] } = {}) {
const obj = toJSON.call(this);
if (!attributes.length) {
return obj;
}
return attributes.reduce((result, attribute) => {
result[attribute] = obj[attribute];
return result;
}, {});
};
module.exports = User;
The real answer (alluded to, but not explicitly stated, in one of the comments) is that beforeCreate hooks are applied after model validation.
This means if you have any field in your model (eg id ) which cannot be null, Sequelize will evaluate this prior to applying the beforeCreate field. In your case, Sequelize never gets as far as the beforeCreate hook, because the null id is failing validation every time.
Your accepted answer works around this by setting allowNull = true, thus circumventing the validation of your (briefly) null id. But the better option (rather than to distort your model by allowing null id) is almost certainly to instead use the correct hook: beforeValidate. This hook is applied before the model criteria are evaluated.
It is a very simple change:
User.beforeValidate(async (user, options) => {
console.log("inside hooks");
let id = `user_${shortid.generate()}`;
user.user_id = id;
});
NB: async is redundant here.
user_id: {
type: Sequelize.STRING,
allowNull: false,
primaryKey: true,
unique: true
}
In this section I have set user_id allowNull = false beforeCreate execute after the beforeValidate hook. But in beforeValidate hooks it was throwing error cause of user.user_id===null so i allowed allowNull===true and now it's working.
Try to remove async in this piece of your code:
async (user, options)
I am unable to update row using sequelize. It seems like it's skipping the query because no logs are generated in the terminal when updating. Other operations like insert and destroy are working fine.
UPDATE CODE
const uuid = require('uuid/v4');
const db = require('../../models');
const TempToken = db.tempTokens;
exports.generateToken = async (req) =>{
'use strict';
const token = uuid();
// let transaction = await db.sequelize.transaction();
try{
console.log(req.userId); /////////// DATA prints in console
console.log(req.device.type); /////////// DATA prints in console
const insert = {
user: req.userId,
device: req.device.type,
};
await TempToken.update({valid: false}, {where:{id: 9 } });
insert['token'] = token;
insert['deviceConfig'] = JSON.stringify(req.device);
await TempToken.create(insert);
return token;
}
catch (e){
return e;
}
};
TempTokens model -
/* jshint indent: 2 */
module.exports = function(sequelize, DataTypes) {
return sequelize.define('tempTokens', {
id: {
type: DataTypes.INTEGER(11),
allowNull: false,
primaryKey: true,
autoIncrement: true,
},
user: {
type: DataTypes.STRING(225),
allowNull: true,
},
token: {
type: DataTypes.STRING(225),
allowNull: false,
},
device: {
type: DataTypes.STRING(225),
allowNull: true,
},
deviceConfig: {
type: DataTypes.TEXT,
allowNull: true,
},
valid: {
type: DataTypes.BOOLEAN,
allowNull: false,
default: true,
},
}, {
tableName: 'tempTokens',
});
};
Data in req.userId and req.device.type are valid. And, id:9 is also valid and present in table.
Terminal log is -
Executing (default): INSERT INTO `tempTokens` (`id`,`user`,`token`,`device`,`deviceConfig`,`createdAt`,`updatedAt`) VALUES (DEFAULT,?,?,?,?,?,?);
Any help will be appreciated
I think it should be defaultValue not default:
valid: {
type: DataTypes.BOOLEAN,
allowNull: false,
defaultValue: true,
}
i'm using NodeJS & Sequelize for a school project and i'm struggling on making associations w/ sequelize work. I tried a couple of things before but nothing that made my day.
Basically the thing is that a user can have several playlists (hasMany).
And a playlist belongs to a user (belongsTo).
My error is:
Association with alias "playlist" does not exist on users
Here are my models:
/* USER MODEL */
const Sequelize = require('sequelize');
const { db } = require('../utils/db');
const User = db.define('users', {
id: {
allowNull: false,
autoIncrement: true,
primaryKey: true,
type: Sequelize.INTEGER,
},
userID: {
type: Sequelize.INTEGER,
allowNull: false,
field: 'user_id',
},
firstName: {
type: Sequelize.STRING,
field: 'first_name',
allowNull: false,
},
}, {
underscored: true,
tableName: 'users',
freezeTableName: true, // Model tableName will be the same as the model name
});
module.exports = {
User,
};
/* PLAYLIST MODEL */
const sequelize = require('sequelize');
const { db } = require('../utils/db');
const Playlist = db.define('playlist', {
id: {
allowNull: false,
autoIncrement: true,
primaryKey: true,
type: sequelize.INTEGER,
},
name: {
type: sequelize.STRING,
field: 'name',
allowNull: false,
},
coverUrl: {
type: sequelize.STRING,
field: 'cover_url',
allowNull: true,
},
ownerId: {
type: sequelize.INTEGER,
allowNull: false,
references: {
model: 'users',
key: 'user_id',
},
},
}, {
underscored: true,
tableName: 'playlist',
freezeTableName: true,
});
module.exports = {
Playlist,
};
Here is how i load my models:
const { Credentials } = require('./credentials');
const { User } = require('./users');
const { Playlist } = require('./playlist');
function loadModels() {
User.associate = (models) => {
User.hasMany(models.Playlist, { as: 'playlist' });
};
Playlist.associate = (models) => {
Playlist.belongsTo(models.User, { foreignKey: 'owner_id', as: 'owner' });
};
Credentials.sync({ force: false });
User.sync({ force: false });
Playlist.sync({ force: false });
}
module.exports = {
loadModels,
};
And finally here is my query where i get this error:
const express = require('express');
const { auth } = require('../../middlewares/auth');
const { Playlist } = require('../../models/playlist');
const { User } = require('../../models/users');
const router = express.Router();
router.get('/playlist', [], auth, (req, res) => {
User.findOne({
where: { userID: req.user.user_id }, include: 'playlist',
}).then((r) => {
console.log(r);
});
});
module.exports = router;
I'm trying to get all the playlist that belongs to a user.
I removed all the useless code (jwt check etc..)
So when i'm doing a get request on /playlist I get:
Unhandled rejection Error: Association with alias "playlist" does not exist on users.
I understand the error but don't understand why i get this.
What did I miss, any ideas ?
Thanks,
I finally fixed it by re-make all my models and definitions with migrations.
I had the same problem and the solution was that Sequelize pluralize the models name so in my case "playlist" does not exist on users because Sequelize pluralized my model so I had to put "Playlists" instead.
I have 2 models users and tags, both are associated through another model called usersTags and all 3 models have paranoid set with custom timestamps. I understand that associating models will add additional methods to work on the associations to all associated models, so i am wanting to making a simple setTags call for users, the docs shows that if in the array in the method does not contain the element that is stored in the database it should be removed, otherwise it should be created/restored.
So i try to restore a previously removed tag but for some reason it fails. The models are defined as following:
Users
module.exports = function(sequelize, DataTypes) {
const Users = sequelize.define("users", {
id: {
type: DataTypes.INTEGER(11),
allowNull: false,
primaryKey: true,
autoIncrement: true
},
username: {
type: DataTypes.STRING(100),
allowNull: false,
validate: {
len: {
args: [3, 100],
msg: "String length is not in this range"
}
}
},
password: {
type: DataTypes.STRING(100),
allowNull: false,
field: "password_hash"
}
}, {
tableName: "users",
createdAt: "create_time",
updatedAt: "update_time",
deletedAt: "delete_time",
paranoid: true
});
Users.associate = function(models) {
// Add this association to include tag records
this.belongsToMany(models.tags, {
through: {
model: models.usersTags,
unique: true
},
foreignKey: "users_id",
constraints: false
});
};
return Users;
};
Tags
module.exports = function(sequelize, DataTypes) {
const Tags = sequelize.define("tags", {
id: {
type: DataTypes.INTEGER(11),
allowNull: false,
primaryKey: true,
autoIncrement: true
},
name: {
type: DataTypes.STRING(45),
allowNull: false
}
}, {
tableName: "tags",
createdAt: "create_time",
updatedAt: "update_time",
deletedAt: "delete_time",
paranoid: true
});
Tags.associate = function(models) {
this.belongsToMany(models.users, {
through: {
model: models.usersTags,
unique: true
},
foreignKey: "tags_id",
constraints: false
});
};
return Tags;
};
usersTags
module.exports = function(sequelize, DataTypes) {
const UsersTags = sequelize.define("usersTags", {
users_id: {
type: DataTypes.INTEGER(11),
allowNull: false,
primaryKey: true,
references: {
model: "users",
key: "id"
}
},
tags_id: {
type: DataTypes.INTEGER(11),
allowNull: false,
primaryKey: true,
references: {
model: "tags",
key: "id"
}
}
}, {
tableName: "users_tags",
createdAt: "create_time",
updatedAt: "update_time",
deletedAt: "delete_time",
paranoid: true,
indexes: [
{
unique: true,
fields: ["users_id", "tags_id"]
}
]
});
return UsersTags;
};
Test
let _user;
models.users.findOne({where: {id: 100}})
.then(user => {
_user = user;
return _user.setTags([1]); // Successfully create association tag with id 1
})
.then(() => _user.setTags([])) // Successfully remove all associated tags
.then(() => _user.setTags([1])); // Should restore association tag with id 1 but fails
Executed query
app:database Executing (default): SELECT `id`, `username`, `first_name`, `last_name`, `birthday`, `description`, `location`, `email`, `type`, `image_path` FROM `users` AS `users` WHERE ((`users`.`delete_time` > '2018-08-28 19:40:15' OR `users`.`delete_time` IS NULL) AND `users`.`id` = 100); +0ms
app:database Executing (default): SELECT `users_id`, `tags_id`, `create_time`, `update_time`, `delete_time` FROM `users_tags` AS `usersTags` WHERE ((`usersTags`.`delete_time` > '2018-08-28 19:40:15' OR `usersTags`.`delete_time` IS NULL) AND `usersTags`.`users_id` = 100); +6ms
app:database Executing (default): INSERT INTO `users_tags` (`users_id`,`tags_id`,`create_time`,`update_time`) VALUES (100,1,'2018-08-28 19:40:15','2018-08-28 19:40:15'); +7ms
For some reason the tag search query is failing to retrieve the tag that contains the delete_time set and therefore the last query is insert instead of update, i know the workaround would be to set paranoid to false but i have to keep track of all activities, i know another workaround would be to create a custom model method to handle this but i still want to know if there is a way to achieve this without having to create an additional custom method
your code in not in a correct async order so your _user global variable is not initiated,I think this is the correct order :
let _user;
models.users.findOne({where: {id: 100}})
.then(user => {
_user = user;
_user.setTags([]).then(()=>{
_user.setTags([1])
})
})