I am using sequelize on node.js.
My model object for Asset:
module.exports =
class Asset extends Model {
static init(sequelize) {
return super.init(
{
AssetID: {
field: "asset_id",
type: DataTypes.INTEGER(11),
allowNull: false,
primaryKey: true,
autoIncrement: true
},
AssetName: {
field: "asset_name",
type: DataTypes.STRING(40),
allowNull: false
},
SKU: {
field: "sku",
type: DataTypes.STRING(30)
}
},
Object.assign({
sequelize,
tableName: "assets",
})
);
}
static associate(models) {
Asset.Listings = this.hasMany(models.Listing,
{
as: 'Listings',
foreignKey: {name: 'AssetID', field: 'asset_id'},
sourceKey: 'AssetID'
})
}
};
And the listing definition is:
module.exports =
class Listing extends Model {
static init(sequelize) {
return super.init(
{
ListingID: {
field: "listing_id",
type: DataTypes.INTEGER(11),
allowNull: false,
primaryKey: true,
autoIncrement: true
},
IsPreferred: {
field: "isPreferred",
type: DataTypes.BOOLEAN,
allowNull: false,
defaultValue: false
},
Price: {
field: "price",
type: DataTypes.DOUBLE(5,2),
allowNull: false,
validate: { min: 0 }
},
ListingType: {
field: "listing_type",
type: DataTypes.STRING(20),
allowNull: false,
defaultValue: "Buy",
validate: {
isIn: [['Buy', 'Rent']]
}
}
},
Object.assign({
sequelize,
tableName: "listings",
})
);
}
this is my create code:
await Asset.create(newRecord, {include: [{ association: Asset.Listings } ]})
.then(dbRecord => {
logger.debug(`New Asset created`);
return dbRecord;
})
.catch(err => {
logger.warn(`Asset could not be created. ${JSON.stringify(newRecord)}`, err);
throw createServiceError(`Create Asset`, err);
})
Now...my problem is that when I create a new Asset record with the listing association, in case that the creation of the listing fails (for example on the type validation, say I put "abc"), the listing will not be created (as the value is invalid) but the Asset is created.
I would expect that the entire transaction will be reverted.
It is a bit more strange because the create call ends up in the catch block.
Any ideas?
Thanks,
Asaf
You can use transaction.
Sorry for formating I'm giving answer from phone.
sequelize.transaction(transaction => {
Asset.create(newRecord, {include: [{ association: Asset.Listings } ], transaction})
})
.then(function() {
console.log('success');
})
.catch(function(err) {
console.log(err);
})
})
Related
When I try to insert new category, I got this error:
error: column "image" does not exist
sql: 'INSERT INTO "Categories" ("id","createdAt","updatedAt") VALUES (DEFAULT,$1,$2) RETURNING "id","image","title","createdAt","updatedAt";'
The problem is that it doesn't insert name and other values and returns columns belong to post table.
My guesses are the problem of sequelize-cli and sequelize version or missing something in models or migrations.
I only insert values into name, createdAt and updatedAt column:
await Category.create({
name: req.body.name,
createdAt: new Date(),
updatedAt: new Date()
});
My category model:
const { Model } = require("sequelize");
module.exports = (sequelize, DataTypes) => {
class Category extends Model {
static associate(models) {
Category.hasMany(models.Post, { as: "posts", foreignKey: "categoryId" });
}
}
Category.init(
{
name: DataTypes.STRING
},
{
sequelize,
modelName: "Category"
}
);
return Category;
};
My Post Model:
const { Model } = require("sequelize");
module.exports = (sequelize, DataTypes) => {
class Post extends Model {
static associate(models) {
Post.belongsTo(models.Category, { foreignKey: "categoryId", onDelete: "CASCADE", as: "category" });
}
}
Post.init(
{
title: DataTypes.STRING,
image: DataTypes.STRING,
content: DataTypes.TEXT,
categoryId: DataTypes.INTEGER
},
{
sequelize,
modelName: "Post"
}
);
return Post;
};
Post migration:
module.exports = {
async up(queryInterface, Sequelize) {
await queryInterface.createTable("Posts", {
id: {
allowNull: false,
autoIncrement: true,
primaryKey: true,
type: Sequelize.INTEGER
},
title: {
type: Sequelize.STRING
},
image: {
type: Sequelize.STRING
},
content: {
type: Sequelize.TEXT
},
categoryId: {
type: Sequelize.INTEGER,
allowNull: false,
onDelete: "CASCADE",
references: {
model: "Categories",
key: "id"
}
},
createdAt: {
allowNull: false,
type: Sequelize.DATE
},
updatedAt: {
allowNull: false,
type: Sequelize.DATE
}
});
},
Category migration:
module.exports = {
async up(queryInterface, Sequelize) {
await queryInterface.createTable("Categories", {
id: {
allowNull: false,
autoIncrement: true,
primaryKey: true,
type: Sequelize.INTEGER
},
name: {
type: Sequelize.STRING
},
createdAt: {
allowNull: false,
type: Sequelize.DATE
},
updatedAt: {
allowNull: false,
type: Sequelize.DATE
}
});
},
I couldn't find solution for this, therefor I used sequelize.query
I am trying to create a many to many relationship between user table and role table through userroles table.
After table creation the db looks just fine, I tried pretty much everything I found on the sequelize documentation and previous answers here, nothing seems to work.
I am getting this error: EagerLoadingError [SequelizeEagerLoadingError]: UserRoles is not associated to User!
Any idea of what am I doing wrong ? please help!
class User extends Model {
static associate(models) {
User.belongsToMany(models.Role, {
foreignKey: "user_id",
through:'UserRoles',
as:"users"
});
}
}
User.init(
{
user_id: {
type: DataTypes.UUID,
defaultValue: DataTypes.UUIDV4,
primaryKey: true,
unique: true,
},
email: {
type: DataTypes.STRING,
allowNull: false,
unique: true,
},
password: {
type: DataTypes.STRING,
allowNull: false,
},
name: {
type: DataTypes.STRING,
allowNull: false,
},
phone: {
type: DataTypes.STRING,
allowNull: false,
},
},
{
sequelize,
modelName: "User",
}
);
class Role extends Model {
static associate(models) {
Role.belongsToMany(models.User, {
foreignKey: "role_id",
through:'UserRoles',
as:"roles"
});
}
}
Role.init(
{
role_id: {
type: DataTypes.UUID,
defaultValue: DataTypes.UUIDV4,
primaryKey: true,
unique:true
},
role_name: {
type: DataTypes.STRING,
allowNull: false,
unique: true,
},
role_desc: {
type: DataTypes.STRING,
allowNull: false,
},
},
{
sequelize,
modelName: "Role",
}
);
class UserRoles extends Model {
static associate(models) {
}
}
UserRoles.init(
{
userroles_id: {
type: DataTypes.INTEGER,
autoIncrement: true,
primaryKey: true,
},
// user_id: {
// type: DataTypes.UUID,
// defaultValue: DataTypes.UUIDV4,
// },
// role_id: {
// type: DataTypes.UUID,
// defaultValue: DataTypes.UUIDV4,
// },
},
{
sequelize,
modelName: "UserRoles",
}
);
const signup = (req, res) => {
console.log(req.body);
console.log("signup entry");
if (
!req.body.role ||
!req.body.email ||
!req.body.password ||
!req.body.name ||
!req.body.phone
) {
res.status(400).send({
msg: "Please pass role, email, password and name.",
});
} else {
sequelize.models.User.findOne({
where: {
email: req.body.email,
},
})
.then((duplicateemailfound) => {
if (duplicateemailfound) {
console.log(duplicateemailfound);
return res.status(400).json({
success: false,
message: "Email already registered",
});
} else {
let userRole = req.body.role.toLowerCase();
console.log("userRole:", userRole);
sequelize.models.Role.findOne({
where: {
role_name: userRole,
},
})
.then((foundRole) => {
// console.log(foundRole);
if (foundRole == null) {
return res.status(400).json({
success: false,
role: "null or not found",
});
}
// console.log("foundRole", foundRole); // .role_id
let addUser = {
email: req.body.email,
password: req.body.password,
name: req.body.name,
phone: req.body.phone,
role_id: foundRole.role_id,
};
sequelize.models.User.create(addUser, {
include: [{ model: sequelize.models.UserRoles }],
})
.then((newUser) => {
console.log("new user", newUser);
return res.status(201).json({
success: true,
newuser: newUser,
});
})
.catch((error) => {
console.log(error);
res.status(400).json({
success: false,
// message: "Duplicate Email was Found",
error: error.errors[0].message,
error: error,
});
});
})
.catch((error) => {
console.log(error);
res.status(400).json({
error: error,
msg: "bbb",
});
});
}
})
.catch((err) => {
console.log(err);
});
}
};
You create some a class for each Model and extend them with Model class of sequelize, this if fine.
Now, you define a static method inside the class named associate(model) where you define the rule for that class. This is fine because you used static which is required here to since it will be a property of the class, not of an object.
Then you call the initialize method (a in-built method of class Model). In the same way you need to call your defined associate.
Here is a problem, because in the structure that you have now, you can't call that method in it's own class file, becuase you need the other Model to pass it as parameter.
So there are 2 solutions:
Import your User model inside Role model file and call the static method, like this:
const User = require('User')
class Role extends Model {
static associate(model) {
Role.belongsToMany(model, {
foreignKey: "role_id",
through:'UserRoles',
as:"roles"
});
}
}
Role.init(
{
role_id: {
type: DataTypes.UUID,
defaultValue: DataTypes.UUIDV4,
primaryKey: true,
unique:true
},
role_name: {
type: DataTypes.STRING,
allowNull: false,
unique: true,
},
role_desc: {
type: DataTypes.STRING,
allowNull: false,
},
},
{
sequelize,
modelName: "Role",
}
);
Role.associate(User);
This will use your User model to pass it to the static method and finally to run the belongsToMany
Second solution would be to create an index file for your Models, where you import both of them and you can simply run that belongsToMany there, and then simply import that index file in the main file of your application, something like this:
User.js
const index = require('./../index.js');
const Sequelize = require('sequelize');
const Model = Sequelize.Model;
const sequelize = index.sequelize;
class User extends Model{}
User.init({
username: {
type: Sequelize.STRING,
allowNull: false,
unique: true
},
password: {
type: Sequelize.STRING,
allowNull: false
},
role: {
type: Sequelize.STRING,
allowNull: false
}
},{
sequelize,
modelName: 'user'
});
module.exports = {
User: User
}
Role.js will look the same but with your own model.
and index.js would look like this:
const Sequelize = require('sequelize');
const sequelize = new Sequelize(process.env.DB_NAME, process.env.DB_USERNAME, process.env.DB_PASSWORD, {
host: process.env.DB_HOST,
dialect: process.env.DB_DIALECT
});
exports.sequelize = sequelize;
const user = require('./models/User');
const role= require('./models/Role');
role.belongsToMany(user, {
foreignKey: "role_id",
through:'UserRoles',
as:"roles"
});
sequelize.sync(user);
sequelize.sync(role);
exports.db = {
user: user,
role: role
}
I'm newbie with sequelize and I'm trying to assiacite two tables. Users and Projects. I have defined a model for each one called UserModel and ProjectModel and when I try to associate projects with user I've got this error:
belongsTo called with something that's not a subclass of
Sequelize.Model
These are the files where I define each model:
user.js
module.exports = (sequelize, DataType) => {
const UserModel = sequelize.define("user", {
id: {
type: DataType.INTEGER,
primaryKey: true,
autoIncrement: true,
allowNull: false,
},
email: {
type: DataType.STRING,
allowNull: false,
isEmail: {
msg: "The format of the e-mail is not correct"
},
validate: {
notNull: {
msg: "E-mail cannot be empty"
}
}
},
name: {
type: DataType.STRING,
is: /^[a-zA-Z ]+$/i,
allowNull: false,
validate: {
notNull: {
msg: "Name cannot be empty"
}
}
},
surname: {
type: DataType.STRING,
is: /^[a-zA-Z ]+$/i,
allowNull: false,
validate: {
notNull: {
msg: "Surname cannot be empty"
}
}
}
});
UserModel.associate = (models) => {
UserModel.hasMany(models.ProjectModel, {
foreignKey: "userID"
})
}
return UserModel;
};
project.js
module.exports = (sequelize, DataType) => {
const ProjectModel = sequelize.define("project", {
id: {
type: DataType.INTEGER,
primaryKey: true,
autoIncrement: true,
allowNull: false,
},
name: {
type: DataType.STRING,
is: /^[a-zA-Z ]+$/i,
allowNull: false,
validate: {
notNull: {
msg: "Name cannot be empty"
}
}
},
body: {
type: DataType.TEXT,
allowNull: false,
validate: {
notNull: {
msg: "Body cannot be empty"
}
}
},
status: {
type: DataType.ENUM("active", "inactive", "declined", "completed"),
allowNull: false,
validate: {
notNull: {
msg: "Status cannot be empty"
}
}
},
userID: {
type: DataType.INTEGER,
allowNull: false,
validate: {
notNull: {
msg: "userID cannot be empty"
}
},
references: {
model: UserModel,
key: "id"
}
}
});
ProjectModel.associate = (models) => {
ProjectModel.belongsTo(models.UserModel, {
foreignKey: "userID"
});
}
ProjectModel.belongsTo(UserModel, {
foreignKey: "userID"
});
return ProjectModel;
}
What am I doing wrong?
Finally, I have found the solution to my problem. In this association, for example:
ProjectModel.associate = (models) => {
ProjectModel.belongsTo(models.UserModel, {
foreignKey: "userID"
});
}
With the variable models.UserModel I was making reference to the variable which is returned by the model. Instead of this, you have to write the name of the table. So, in my case the correct function is:
ProjectModel.associate = (models) => {
ProjectModel.belongsTo(**models.users**, {
foreignKey: "userID"
});
}
Don't import model in another model file. like if you wrote A.belongsTo(B) file,then don't import A file into B file.
PROBLEM RESUME:
I'm having trouble when I try to do a findOne or findAll.
At the findOne or findAll answer I catch all the informations from the user but in the answer there aren't any data of "t_roles" associated to this user.
But the stranger issue is that if I use raw: true inside the findOne for example, the informations of roles are shown.
I Have two models
User:
const dbUser = {
a_id: {
primaryKey: true,
autoIncrement: true,
type: Sequelize.BIGINT,
},
a_date_created: {
type: Sequelize.DATE,
allowNull: false,
},
a_first_name: {
type: Sequelize.STRING,
allowNull: false,
},
a_last_name: {
type: Sequelize.STRING,
allowNull: false,
},
a_email: {
type: Sequelize.TEXT,
unique: true,
allowNull: false,
},
a_password: {
type: Sequelize.STRING,
allowNull: false,
},
a_birthday: {
type: Sequelize.DATE,
allowNull: false,
},
a_is_active: {
type: Sequelize.BOOLEAN,
allowNull: false,
},
};
User.init(dbUser, {
sequelize: db,
modelName: 't_user',
timestamps: false,
tableName: 't_users',
});
User.associate = (models) => {
console.log('ASSOCIADO')
User.belongsToMany(models.Role, {
through: { model: UserRole, unique: false },
as: 'roles',
foreignKey: 'a_user',
otherKey: 'a_role',
});
};
and Role:
const dbRole = {
a_id: {
primaryKey: true,
autoIncrement: true,
type: Sequelize.BIGINT,
},
a_role: {
type: Sequelize.STRING,
allowNull: false,
},
};
Role.init(dbRole, {
sequelize: db,
modelName: 't_role',
timestamps: false,
tableName: 't_roles',
});
Role.associate = (models) => {
Role.belongsToMany(models.User, {
through: { model: UserRole, unique: false },
as: 'UserOfRoles',
foreignKey: 'a_role',
otherKey: 'a_user',
});
};
As you can see I'm associating them using another model, UserRole:
const dbUserRole = {
a_id: {
primaryKey: true,
autoIncrement: true,
type: Sequelize.BIGINT,
},
a_role: {
type: Sequelize.INTEGER,
allowNull: false,
primaryKey: false,
references: {
model: Role,
key: 'a_id',
},
onDelete: 'CASCADE',
},
a_user: {
type: Sequelize.INTEGER,
allowNull: false,
references: {
model: User,
key: 'a_id',
},
onDelete: 'CASCADE',
},
};
UserRole.init(dbUserRole, {
sequelize: db,
modelName: 't_user_role',
timestamps: false,
tableName: 't_user_role',
});
UserRole.associate = (models) => {
UserRole.belongsTo(models.User, { targetKey: 'a_id', foreignKey: 'a_user' });
UserRole.belongsTo(models.Role, { targetKey: 'a_id', foreignKey: 'a_role' });
};
To create a user with a role (admin) I do like the code below:
onst createAdmin = async (body) => {
try {
const userResult = await createUser(body);
if (userResult.error) {
return {
ok: false,
error: userResult.error,
};
}
const isAdmin = await UserRole.create({
a_role: 1,
a_user: userResult.a_user_id,
});
return {
ok: true,
};
} catch (error) {
return {
ok: false,
error,
};
}
Seems to be working fine, because the user are being created, and the association using the "t_user_role" too, because the data is also being created at the table.
As I sad at the problem resume, my trouble is when I'm trying to do a findOne or findAll.
For example, when I try the code below, I catch all the informations from the user but in the answer there aren't any data of "t_roles" associated to this user.
const { body } = req;
try {
const result = await User.findOne({
where: {
a_id: 1,
},
include: [
{
association: 'roles',
attributes: ['a_role'],
through: {
attributes: [],
},
},
],
});
console.log('====================================');
console.log(JSON.stringify(result, null, 2));
console.log('====================================');
} catch (error) {
console.error(error);
}
If I use raw: true inside the findOne for example, the informations of roles are shown, so I presume that the association is correct.
I really appreciate any help to find what I'm missing here.
Thanks
Well, after days working on and trying different ways to solve this problem, a friend of mine just helped me starting again the entire project, following the documentation of Sequelize and the exact structure we did before, but a little bit more simple, and surprisingly ... worked. So I suppose that was something with migrations ore models or whatever, but we can't really say.
In my project I am using MySQL database and Sequelize Js,
I have two models created:
Post code model:
module.exports = function(sequelize, Sequelize) {
var Post_code = sequelize.define('post_code', {
id: {
autoIncrement: true,
primaryKey: true,
type: Sequelize.INTEGER(11)
},
code: {
type: Sequelize.STRING(16),
allowNull: false,
unique: true
},
city: {
type: Sequelize.STRING(45),
allowNull: false
}
},{
freezeTableName: true,
underscored: true
});
Post_code.associate = function(models) {
models.post_code.hasMany(models.building);
};
return Post_code;
}
Building model:
module.exports = function(sequelize, Sequelize) {
var Building = sequelize.define('building', {
id: {
autoIncrement: true,
primaryKey: true,
allowNull: false,
type: Sequelize.INTEGER(11)
},
name: {
type: Sequelize.STRING(100),
allowNull: false
},
number: {
type: Sequelize.INTEGER(11),
allowNull: false
},
address: {
type: Sequelize.STRING(255),
allowNull: false
},
latitude: {
type: Sequelize.DECIMAL(9,6),
allowNull: false
},
longitude: {
type: Sequelize.DECIMAL(9,6),
allowNull: false
}
},{
freezeTableName: true,
underscored: true
});
Building.associate = function (models) {
models.building.belongsTo(models.post_code, {
foreignKey: {
allowNull: false
}
});
models.building.hasMany(models.flat);
};
return Building;
};
They are in relation one to many, it follows that Post code has many Buildings:
I want to add new building to database, when POST request is send to this route:
"/post-codes/:post_code_id/buildings"
I have access to post_code_id but I don't know how to correctly associate post_code model with building.
I was trying to do something like this:
models.post_code.findById(req.params.post_code_id)
.then(function(postCode){
postCode.setBuilding(
models.building.create({
}).then(function(building){});
);
});
But without result. I would be grateful if someone could explain how to make inserts correctly.
I haven't heard about the include option on create, but you can do something like this:
db.post_code.find({
where: {
id: req.params.post_code_id
}
}).then(post_code => {
if (!post_code) {
return res.status(404).send({
message: 'No post_code with that identifier has been found'
});
} else {
//create here your building
}
}).catch(err => {
return res.jsonp(err)
});
I don't know if this is the best way to do it, but it verify first if the post_code exists.