I got some trouble understanding how associations works with Sequelize. I am working on a project which have almost the same features that Reddit and therefore I am trying to associate the User table to the Post table as a 1:N associations.
User Model:
const { Sequelize, Model, DataTypes } = require ('sequelize');
const db = require('../config/db');
const Post = require('./Post')
class User extends Model{}
User.init({
id: {
type: DataTypes.INTEGER,
allowNull: false,
primaryKey: true,
autoIncrement: true,
},
username:{
type: DataTypes.STRING,
unique: true
},
email:{
type: DataTypes.STRING
},
password:{
type: DataTypes.STRING
},
isAdmin:{
type: DataTypes.BOOLEAN,
defaultValue: false
}
}, {sequelize: db, modelName:'User'}
);
User.hasMany(Post,{as: 'posts', foreignKey: 'id'});
User.sync();
module.exports = User;
Post Model:
const { Sequelize, Model, DataTypes } = require ('sequelize');
const db = require('../config/db');
const User = require('./User');
class Post extends Model{}
Post.init({
id: {
type: DataTypes.INTEGER,
allowNull: false,
primaryKey: true,
autoIncrement: true,
},
text:{
type: Sequelize.STRING
},
image:{
type: Sequelize.STRING
},
likes:{
type: Sequelize.INTEGER,
}
}, {sequelize: db, modelName:'Post'}
)
Post.sync();
module.exports = Post;
When I launch my app, I can see that it mention that post have the foreign key id but still I don't have hany column that link User to Post in my DB. What I am missing?
Executing (default): CREATE TABLE IF NOT EXISTS `Posts` (`id` INTEGER NOT NULL auto_increment , `text` VARCHAR(255), `image` VARCHAR(255), `likes` INTEGER, `createdAt` DATETIME NOT NULL, `updatedAt` DATETIME NOT NULL, PRIMARY KEY (`id`), **FOREIGN KEY (`id`)** REFERENCES `Users` (`id`) ON DELETE CASCADE ON UPDATE CASCADE) ENGINE=InnoDB;
First, you need to indicate the correct value in the foreignKey option, it should be a field like user_id in Post model:
User.hasMany(Post,{as: 'posts', foreignKey: 'user_id'});
Second, you need to move association definitons and sync calls (and cross-refrences of models) from model modules outside. You need to register all models and only after that to define all their associations and the last action would be calling sync methods.
See the question and my answer here to get an idea how to do it.
Related
I want to translate this psql table creation query to sequelize:
PSQL:
CREATE TABLE categories
(
id INTEGER NOT NULL PRIMARY KEY,
name CHARACTER VARYING(50) NOT NULL UNIQUE,
description CHARACTER VARYING(100) NOT NULL
);
CREATE TABLE posts
(
id INTEGER NOT NULL PRIMARY KEY,
title CHARACTER VARYING(50) NOT NULL,
content CHARACTER VARYING(100) NOT NULL,
from_category CHARACTER VARYING(50) NOT NULL,
CONSTRAINT fk_from_category
FOREIGN KEY(from_category)
REFERENCES categories(name)
)
Its a simple fk association, with varchar type.
I have read sequelize docs, but i still don't know how to change the relation from primary keys to varchar.
From what i read, this is what you can do with associations:
Post.belongsTo(Category, {
foreignKey: {
onDelete: ...,
onUpdate: ...,
validate: {...},
},
});
and thats all i could find about on youtube too..
I would be really happy if you can help me. I have spent too much time on this already, but i want it to work!
Follow this example using belongsTo:
class Category extends Model { }
Category.init({
id: {
type: DataTypes.INTEGER,
primaryKey: true
},
name: {
type: DataTypes.STRING,
unique: true,
allowNull: false
},
description: {
type: DataTypes.STRING,
allowNull: false
}
}, { sequelize, modelName: 'categories' });
class Post extends Model { }
Post.init({
id: {
type: DataTypes.INTEGER,
primaryKey: true
},
title: {
type: DataTypes.STRING,
allowNull: false
},
content: {
type: DataTypes.STRING,
allowNull: false
},
from_category: {
type: DataTypes.STRING,
allowNull: false
}
}, { sequelize, modelName: 'posts' });
Post.belongsTo(Category, {
foreignKey: 'from_category',
targetKey: 'name'
});
Read more about to understand with more details in the official docs.
I am new to the backend field, I have been looking for solutions on how to make the model of two related tables, One-To-Many relationship of an existing database, but each example confuses me more.
CREATE TABLE my_db.person (
id INTEGER NOT NULL PRIMARY KEY AUTO_INCREMENT,
name VARCHAR(255) NOT NULL
created_at DATETIME,
craeted_by INTEGER, -- account_id
);
CREATE TABLE my_db.account (
id INTEGER NOT NULL PRIMARY KEY AUTO_INCREMENT,
email VARCHAR(255) UNIQUE,
password VARCHAR(255),
person_id INTEGER NOT NULL,
role INT NOT NULL
);
This is the Foreign Key that I want to make, the same
ALTER TABLE my_db.account
ADD CONSTRAINT fk_account_person_id
FOREIGN KEY (person_id)
REFERENCES my_db.person (id);
ON DELETE CASCADE
ON UPDATE CASCADE;
I did this but it doesn't work
person.ts
import { DataTypes } from 'sequelize';
import { db } from '../../db/connection';
import { Account } from './account';
export const Person = db.define( 'person', {
id: {
type: DataTypes.INTEGER,
allowNull: false,
primaryKey: true,
autoIncrement: true
},
name: {
type: DataTypes.STRING,
allowNull: false
},
created_at: {
type: DataTypes.DATE,
allowNull: true
},
created_by : {
type: DataTypes.INTEGER,
allowNull: true
}
}, {
timestamps: false,
});
Person.hasMany( Account, {
foreignKey: 'person_id',
onDelete: 'cascade',
onUpdate: 'cascade'
}
account.ts
import { DataTypes } from 'sequelize';
import { db } from '../../db/connection';
import { Person } from './Person';
export const Account = db.define( 'account', {
id: {
type: DataTypes.INTEGER,
allowNull: false,
primaryKey: true,
autoIncrement: true
},
email: {
type: DataTypes.STRING,
allowNull: false,
unique: true
},
password: {
type: DataTypes.STRING,
allowNull: false
},
person_id: {
type: DataTypes.INTEGER,
allowNull: false
},
role: {
type: DataTypes.INTEGER,
allowNull: false
}
}, {
timestamps: false,
});
Account.belongsTo( Person, {
foreignKey: 'person_id',
onDelete: 'CASCADE',
onUpdate: 'CASCADE'
}
Official Documentation
I thought of using the example from the documentation, however, it changes the name to the FK, but it does not show the exact table
enter image description here
enter image description here
clubId or TeamId?
enter image description here
In case the name of the attribute is not needed, does it infer it?
What if I have more than one FK to the same table? How would you infer those two without a constraint?
I did this, but I don't know where to put the constraint, since it already exists.
The database is in production, I cannot make changes to it, and I do not want a new constraint to be created
I am currently working a project using NodeJS and postgresql with Sequelize as the ORM.
I have two tables; and address table and a user table. I want the address primary key to be a foreign key in the user table.
THIS is how I have defined the association.
Address.hasOne(User);
User.belongsTo(Address, {
foreignKey:{
type:Sequelize.IMTEGER,
name: "address_id",
allowNull: false,
},
});
This is the definition of my address table using sequelize;
exports default (sequelize, DataTypes) =>{
const Address = sequelize.define("address ",{
address_id:{
type: DataTypes.INTEGER,
autoIncrement:true,
primaryKey: true,
unique:true
},
address_name:{
type: DataTypes.INTEGER,
allowNull: false,
validate:{
notNull:{
msg: "address name required!"
}
}
});
return Address;
}
And this is the user table:
exports default (sequelize, DataTypes) =>{
const User = sequelize.define("user ",{
user_id:{
type: DataTypes.INTEGER,
autoIncrement:true,
primaryKey: true,
unique:true
},
name:{
type: DataTypes.STRING,
allowNull: false,
validate:{
notNull:{
msg: "Name required!"
}
}
});
return User;
}
But when I run the synchronization, all the other columns get created except for the address_id column I expect in the User table.
I need help!
Thank everyone in advance
At least you need to indicate the same value for foreignKey option in both associations:
Address.hasOne(User, { foreignKey: 'address_id' });
User.belongsTo(Address, , { foreignKey: 'address_id' });
And it's better to explicitly define address_id in User model:
address_id:{
type: DataTypes.INTEGER,
notNull: true
},
So, we have a table named category_has_serviceprovider that has a composite primary key (idCategory, idUser) and we have a table timeslots with a primary key (idTimeSlots).
What we want to do is: we want to build a N:M table in sequelize using the table category_has_serviceprovider and timeslots. The result should be the table category_x_timeslots that you can see in the next image:
As you can see it has a composite primary key (idTimeSlots, idCategory, idUser), but when we do it in sequelize we get the next image:
So, you can see that it is missing the idUser from category_has_serviceprovider. Can we achieve this in sequelize?
The code that we have for the category_x_timeslots table is:
const Category_has_ServiceProvider = require("../../category_has_serviceProvider/api/category_has_serviceProvider");
const TimeSlots = require("../../timeslots/api/timeslots");
const category_x_TimeSlots = dbconfig.sequelize.define('Category_x_TimeSlots', {
occupied : {
type: dbconfig.Sequelize.BOOLEAN,
allowNull: false
},
experience : {
type: dbconfig.Sequelize.INTEGER,
allowNull: false
}
}, {
freezeTableName: true,
timestamps: false
});
Category_has_ServiceProvider.belongsToMany(TimeSlots, {foreignKey: 'idCategory', through: category_x_TimeSlots});
TimeSlots.belongsToMany(Category_has_ServiceProvider, {foreignKey: 'idTimeSlots',through: category_x_TimeSlots});
category_x_TimeSlots.sync()
module.exports = category_x_TimeSlots;
Tell me if you need to see the other 2 tables implementation.
I think composite foreign keys are only supported at query level and they are not created at the database. As an alternative for the database i would suggest the junction table to have a autoIncrement primary key ( id) and add indexes at the foreign keys columns which should be manually defined.
const Category_has_ServiceProvider = require("../../category_has_serviceProvider/api/category_has_serviceProvider");
const TimeSlots = require("../../timeslots/api/timeslots");
const category_x_TimeSlots = dbconfig.sequelize.define('Category_x_TimeSlots', {
id: {
type: DataTypes.INTEGER,
primaryKey: true,
autoIncrement: true,
allowNull: false
},
occupied: {
type: dbconfig.Sequelize.BOOLEAN,
allowNull: false
},
experience: {
type: dbconfig.Sequelize.INTEGER,
allowNull: false
},
idCategory: {
type: DataTypes.INTEGER,
allowNull: false
},
idUser: {
type: DataTypes.INTEGER,
allowNull: false
},
idTimeSlots: {
type: DataTypes.INTEGER,
allowNull: false
},
}, {
freezeTableName: true,
timestamps: false,
indexes: [{
unique: true,
fields: ['idCategory', 'idUser', 'idTimeSlots']
}]
});
Category_has_ServiceProvider.belongsToMany(TimeSlots, {foreignKey: ['idCategory', 'idUser'], through: category_x_TimeSlots});
TimeSlots.belongsToMany(Category_has_ServiceProvider, {foreignKey: 'idTimeSlots',through: category_x_TimeSlots});
category_x_TimeSlots.sync()
module.exports = category_x_TimeSlots;
How do I define a model for which created_at and updated_at are provided rather than generated?
I'm importing data from somewhere that already has data for created_at and updated_at fields that I would like to preserve rather than generating whenever the object is created/updated by sequelize (our secondary store).
I've tried every likely permutation of model definition and options to get this to work and still sequelize overwrites my fields with it's own timestamps: {silent: true}, etc...
To be clear, the input data has createdAt and updatedAt and I'd like to use sequelize's bulkCreate(), etc such that input values provided are used and stored on the model as created_at and updated_at rather than generated by sequelize.
This is my current model definition:
const Lead = sequelize.define('lead', {
objectId: {
type: Sequelize.STRING,
field: 'objectId',
primaryKey: true,
allowNull: false
},
firstName: {
type: Sequelize.STRING,
field: 'first_name'
},
lastName: {
type: Sequelize.STRING,
field: 'last_name'
},
phoneNumber: {
type: Sequelize.STRING,
field: 'phone_number'
},
createdAt: {
type: Sequelize.DATE,
field: 'created_at',
},
updatedAt: {
type: Sequelize.DATE,
field: 'updated_at'
}
}, {
freezeTableName: true, // Model tableName will be the same as the model name
timestamps: false,
underscored: true
});
The option is
timestamps: false,
Where timestamps is written fully lowercase
Update:
From looking at the code of the bulkCreate() function, it looks that it always updates the timestamps, so, without a patch this is not possible right now