Sequelize Associate: User is not associated to Appointments - node.js

Basically I have a user model and appointment model. The two models are linked with a one-to-many relationship. The Appointment table has two columns that are associated with the user model. When ever I try to include the properties of the user table in appointment, I get the above error.
These are my model designs
Appointment Model
export default ({
sequelize
}:{
sequelize: Sequelize
}) => {
const Appointments: ModelDefined<AppointmentsAttribute, AppointmentsCreationAttributes> = sequelize.define('Appointments', {
appointmentId: {
type: DataTypes.UUID,
defaultValue: DataTypes.UUIDV4,
primaryKey: true,
},
client: {
type: DataTypes.STRING,
allowNull: false,
references: {
model: 'Users',
key: 'uid'
}
},
serviceProvider: {
type: DataTypes.STRING,
allowNull: false,
references: {
model: 'Users',
key: 'uid'
}
},
})
return Appointments
}
User Model
export default ({
sequelize
}: {
sequelize: Sequelize
}) => {
const User: ModelDefined<UserAttribute, UserCreationAttributes> = sequelize.define('User', {
uid: {
type: DataTypes.STRING,
allowNull: false,
unique:true,
primaryKey: true
},
firstname: {
type: DataTypes.STRING,
allowNull: false,
},
lastname: {
type: DataTypes.STRING,
allowNull: false,
},
}
})
return User
}
I have associated the user and appointment models with
Users.hasMany(Appointments);
The code that I am trying to use to fetch the appointment data and include the corresponding user value is
db.appointment.findAll({
where: {
client: this.uid
},
include: 'Users'
})

Sequelize always use associations for models whose association method was called. i.e. if you call Model1.hasMany(Model2) then you can execute queries like:
Model1.findAll({
include: [{
model: Model2
}]
}
and NOT vice versa like this:
Model2.findAll({
include: [{
model: Model1
}]
}
If you wish to request appointment with users as an associated model then you need to add a reversed association from Appointments to Users like this:
Users.hasMany(Appointments);
Appointments.belongsTo(Users);

Related

How to create an object of a relationship in sequelize?

Hey I need to create a relationship in sequelize. I have the model and is well created in the database.
I'll show you my models but it's not very relevant.
Seller Model
const Sellers = db.define("sellers", {
id: {
type: Sequelize.INTEGER,
primaryKey: true,
autoIncrement: true,
},
name: Sequelize.STRING,
surname: Sequelize.STRING,
});
Sellers.hasMany(Clients);
module.exports = Sellers;
Client Model
const Client = db.define("clients", {
id: {
type: Sequelize.INTEGER,
primaryKey: true,
autoIncrement: true,
},
name: Sequelize.STRING,
creationDate: Sequelize.DATE,
client_type: Sequelize.STRING,
});
module.exports = Client;
What I want to do is simply make a relation between the client and seller. In the database a SellerId is added in the client table because of sequelize hasMany() method. What I want to do is just be able to pass the id to the ORM when creating a client so it makes the relationship automatically to the seller table.
Sequelize Documentation about this
Thank you for taking your time to read this. I hope you can help me! Have a great day.
First of all I'd prefer to define a foreign key column in a model and indicate it in associations explicitly. And of course you need to add another association from clients to sellers - belongsTo and call both of associations outside models to be able to reference them to each other.
Client model file:
const Client = db.define("clients", {
id: {
type: Sequelize.INTEGER,
primaryKey: true,
autoIncrement: true,
},
name: Sequelize.STRING,
creationDate: Sequelize.DATE,
client_type: Sequelize.STRING,
sellerId: {
type: Sequelize.INTEGER,
allowNull: false // or true if this association is optional
},
});
some database.js file where you should register all associations:
....
Sellers.hasMany(Clients, { foreignKey: 'sellerId' });
Clients.belongsTo(Sellers, { foreignKey: 'sellerId' });
And then you can create a client indicating a seller's id:
const seller = await Seller.findOne({
where: {
name: 'Peter'
}
})
const newClient = await Client.create({
name: 'John',
sellerId: seller.id,
// other fields here
})

Sequelize.js - choosing the right relationship

I'm using NodeJS and sequelize.
I have user table with a column named duel_id, and each user can be assigned to one duel at a time.
Each duel can have multiple users in it.
I have the following User Model:
const User = Model.define(
'User',
{
user_id: {
type: DataType.INTEGER,
primaryKey: true,
},
username: {
type: DataType.STRING(255),
},
character: {
type: DataType.INTEGER,
},
duel_id: {
type: DataType.INTEGER,
},
},
{
indexes: [{ fields: ['user_id', 'username'] }],
tableName: 'users',
timestamps: false,
},
);
User.hasOne(Duel, { as: 'duel', foreignKey: 'id', sourceKey: 'duel_id' });
with the following Duel model:
const Duel = Model.define(
'DuelRoom',
{
id: {
type: DataType.INTEGER,
primaryKey: true,
autoIncrement: true,
},
round_id: {
type: DataType.INTEGER,
},
status: {
type: DataType.STRING,
},
turn_of_user_id: {
type: DataType.INTEGER,
},
winner: {
type: DataType.STRING,
},
},
{
indexes: [{ fields: ['id'] }],
tableName: 'duel_rooms',
timestamps: true,
},
);
The above code works and return the user and the associated duel if he has one.
I want also to return all the users associate to the same duel.
I tried to connect the relationship with hasMany/ belongsTo with no success. The following errors appears:
Error: DuelRoom.hasMany called with something that's not a subclass of Sequelize.Model
I want to be able to query to get the data like this:
user: {
user_id,
username
duel: {
round_number
players: [{user_id, username}]
}
}
Get the current user with the duel info, with all players associated with the same duel_id as an array named players.
Any idea of how I can define such a relation using sequelize to return all users associated to the user duel?
If a User model has dual_id then you should use belongTo from User to DualRoom instead of hasOne:
User.belongsTo(Duel, { as: 'duel', foreignKey: 'duel_id' });
If you wish to have users collection in a Duel model then this will work with the following hasMany:
Duel.hasMany(User, { as: 'users', foreignKey: 'duel_id' });
Take into account that you should register all associations AFTER all model registrations like I advised in this answer
After all this setup you can get what you wish by executing a query like this:
const user = await User.findOne({
where: {
user_id: id
},
include: [{
model: Duel,
as: 'duel',
include: [{
model: User,
separate: true,
as: 'users'
}]
}]
})
As each user can have a duel and one duel can be associated with many users. It is a one-to-many association, so, you should try:
Duel.hasMany(User);
User.belongsTo(Duel);

Correct way of doing relationships in sequelize model

Recently we moved to Sequelize ORM from Knex due to which we have existing database to which models are map to . Everything working fine except model relationships.
So I have a users table and roles table. Each user can have one role but each role can be assigned to many users. To map these to models, I created model files that is as follows.
User Model:
const Sequelize = require('sequelize');
const sequelize = require('../utility/dbConnection');
const roles = require('./Roles');
module.exports = sequelize.define("User", {
id: {
type: Sequelize.INTEGER(11),
primaryKey: true,
autoIncrement: true,
allowNull: false
},
firstName: Sequelize.STRING(255),
middleName: Sequelize.STRING(255),
lastName: Sequelize.STRING(255),
email: {
type: Sequelize.STRING(255),
unique: true
},
phoneNumber: {
type: Sequelize.STRING(255),
unique: true
},
is_phonenumber_verified: {
type: Sequelize.BOOLEAN,
default: false
},
is_email_verified: {
type: Sequelize.BOOLEAN,
default: false
},
roleID: {
type: Sequelize.INTEGER
},
password: Sequelize.STRING(255)
// }, {
// defaultScope: {
// attributes: { exclude: ['password'] }
// }
}, {
tableName: 'users'
},{
classMethods: {
associate: function() {
this.hasOne(roles,{foreignKey: 'roleID'})
}
}
}
);
roles Model:
const Sequelize = require('sequelize');
const sequelize = require('../utility/dbConnection');
module.exports = sequelize.define("Role", {
id: {
type: Sequelize.INTEGER(11),
primaryKey: true,
autoIncrement: true,
allowNull: false
},
name: {
type: Sequelize.STRING(255),
unique: true
},
},{
tableName: 'roles'
});
Now in the controller file, I want to fetch user details and role name for which code snippet are as follows
const userModel = require('../../models/Users');
const rolesModel = require('../../models/Roles');
let user = await userModel.findOne({ where: { email: req.body.email } },{include:[{model:rolesModel}]});
The problem is it only fetches rolesID from the Users table but not roles name from roles table .
Can anyone help me on this what I am doing wrong here ?
You have To Write Your Include Condition Like This
const userModel = require('../../models/Users');
const rolesModel = require('../../models/Roles');
let user = await userModel.findOne({
where: {
email: req.body.email
},include:
[{ model: rolesModel }]
});
you can use belongsTo relation instead of using hasOne
add this relation in roles table and remove from users table
classMethods: {
associate: function() {
this.belongsTo(users,{foreignKey: 'roleID'})
}
}
You can see this Sequelize Associations docs to know more about Associations and relations

Problem setting up Sequelize association - query with 'include' is failing

I'm new to Sequelize and trying to test if an n:m association I set up between two models, User and Podcast, is working. When I try to run this query, I get some kind of DB error that isn't specific about what's wrong:
User.findOne({
where: { id: id },
include: [{ model: Podcast }]
});
Does anyone know what I'm messing up? I suspect there's something wrong in how I've set up the association, like I'm referencing the names of tables slightly incorrectly, but the migration to create the association worked.
Here's my User.js model file:
module.exports = (sequelize, DataTypes) => {
const User = sequelize.define('User', {
name: {
type: DataTypes.STRING,
allowNull: false
},
email: {
type: DataTypes.STRING,
allowNull: false,
unique: true
},
photo: {
type: DataTypes.STRING
}
});
User.associate = function(models) {
// associations can be defined here
User.belongsToMany(models.Podcast, {
through: 'user_podcast'
});
};
return User;
};
And here's my Podcast.js file:
'use strict';
module.exports = (sequelize, DataTypes) => {
const Podcast = sequelize.define('Podcast', {
id: {
type: DataTypes.STRING,
primaryKey: true,
allowNull: false
},
title: {
type: DataTypes.STRING,
allowNull: false
},
thumbnail: {
type: DataTypes.STRING
},
website: {
type: DataTypes.STRING
}
});
Podcast.associate = function(models) {
// associations can be defined here
Podcast.belongsToMany(models.User, {
through: 'user_podcast'
});
};
return Podcast;
};
And here's the migration I ran to join the two tables:
'use strict';
module.exports = {
up: function(queryInterface, Sequelize) {
return queryInterface.createTable('user_podcast', {
id: {
allowNull: false,
autoIncrement: true,
primaryKey: true,
type: Sequelize.INTEGER
},
userId: {
type: Sequelize.INTEGER,
references: {
model: 'Users',
key: 'id'
}
},
podcastId: {
type: Sequelize.STRING,
references: {
model: 'Podcasts',
key: 'id'
}
},
createdAt: {
allowNull: false,
type: Sequelize.DATE
},
updatedAt: {
allowNull: false,
type: Sequelize.DATE
}
});
},
down: function(queryInterface, Sequelize) {
return queryInterface.dropTable('user_podcast');
}
};
And here's the project on Github for further reference:
https://github.com/olliebeannn/chatterpod
You don't need to create a migration for the M:N table. Now you have something wrong on your user_podcast model. If you are setting a M:N relation between to tables your primary key will be the combination between the foreign key from these two models. If you still want a single id primary key for your table, then you won't use belongsToMany instead use hasMany on user and podcast models pointing to a new model user_podcast.
As far as I see on your first query, it seems that you really need a M:N relation so you can define the model as you do with user and podcast like this:
module.exports = (sequelize, DataTypes) => {
const UserPodcast = sequelize.define('user_podcast', {
userId: {
// field: 'user_id', #Use 'field' attribute is you have to match a different format name on the db
type: DataTypes.INTEGER
},
podcastId: {
// field: 'podcast_id',
type: DataTypes.INTEGER
},
});
UserPodcast.associate = function(models) {
models.User.belongsToMany(models.Podcast, {
as: 'podcasts', //this is very important
through: { model: UserPodcast },
// foreignKey: 'user_id'
});
models.Podcast.belongsToMany(models.User, {
as: 'users',
through: { model: UserPodcast },
// foreignKey: 'podcast_id'
});
};
return UserPodcast;
};
I do prefer to have the belongsToMany associations on the save function where I define the join model, and you have to notice that I used as: attribute on the association. This is very important because this will help sequelize to know which association are you referring on the query.
User.findOne({
where: { id: id },
include: [{
model: Podcast,
as: 'podcasts' //here I use the previous alias
}]
});

Sequelize create through association

I'm working on a create method for an association between two classes. The sequelize documentation indicates that this can be done in one step using includes
IntramuralAthlete.create(intramuralAthlete,{
include: [Person]
}).then((data,err)=>{
if(data)res.json(data);
else res.status(422).json(err);
}).catch(function(error) {
res.status(422).json({message: "failed to create athlete", error: error.message});
});
My model association looks like this
var Person = require('../models').person;
var IntramuralAthlete = require('../models').intramuralAthlete;
IntramuralAthlete.belongsTo(Person);
And the value of intramural athlete when I log it is
{
person:
{ firstName: 'Test',
lastName: 'User',
email: 'test#user.com'
},
grade: '12th',
organizationId: 1
}
But I get the error notNull Violation: personId cannot be null. This error makes it sound like something is wrong with the way I'm indicating to Sequelize that I'm intending to create the personId in that same call.
Is there something wrong in the way I indicate to the create statement what associated tables to create with the IntramuralAthlete?
Thanks!
EDIT:
I have also tried with the following structure with the same result
{
Person: {
firstName: 'Test',
lastName: 'User',
email: 'test#user.com'
},
grade: '12th',
organizationId: 1
}
My model is as follows:
module.exports = function(sequelize, DataTypes) {
return sequelize.define('intramuralAthlete', {
id: {
type: DataTypes.INTEGER(11),
allowNull: false,
primaryKey: true,
autoIncrement: true
},
createdAt: {
type: DataTypes.DATE,
allowNull: false,
defaultValue: sequelize.literal('CURRENT_TIMESTAMP')
},
updatedAt: {
type: DataTypes.DATE,
allowNull: false,
defaultValue: sequelize.literal('CURRENT_TIMESTAMP')
},
grade: {
type: DataTypes.STRING,
allowNull: true
},
age: {
type: DataTypes.INTEGER(11),
allowNull: true
},
school: {
type: DataTypes.STRING,
allowNull: true
},
notes: {
type: DataTypes.STRING,
allowNull: true
},
guardianId: {
type: DataTypes.INTEGER(11),
allowNull: true,
references: {
model: 'contact',
key: 'id'
}
},
personId: {
type: DataTypes.INTEGER(11),
allowNull: false,
references: {
model: 'person',
key: 'id'
}
},
mobileAthleteId: {
type: DataTypes.INTEGER(11),
allowNull: true,
references: {
model: 'mobileAthlete',
key: 'id'
}
},
organizationId: {
type: DataTypes.INTEGER(11),
allowNull: false,
references: {
model: 'organization',
key: 'id'
}
}
}, {
tableName: 'intramuralAthlete'
});
};
I suppose that your models are named Person and IntramuralAthlete (first arguments of sequelize.define method). In this case, when you create an association like yours, and do not define the as attribute, your create data object should look as follows
{
Person: {
firstName: 'Test',
lastName: 'User',
email: 'test#user.com'
},
grade: '12th',
organizationId: 1
}
If you want to use person instead (just as in your code), you should define the association a little bit differently
IntramuralAthlete.belongsTo(Person, { as: 'person' });
Then, you would have to perform some changes in the create query in the include attribute of the options like this
IntramuralAthlete.create(data, {
include: [
{ model: Person, as: 'person' }
]
}).then((result) => {
// both instances should be created now
});
EDIT: Trick the save() method with empty value of personId
You can maintain the allowNull: false if you do something like that
{
person: {
// person data
},
personId: '', // whatever value - empty string, empty object etc.
grade: '12th',
organizationId: 1
}
EDIT 2: Disable validation when creating.
This case assumes that the validation is turned off. It seems like a bad idea to omit model validation, however there still maintains the database table level validation - defined in migrations, where it can still check if personId value was set
IntramuralAthlete.create(data, {
include: [
{ model: Person, as: 'person' }
],
validate: false
}).then((result) => {
// both instances should be created now
});
In this case the data object can be as in your example - without the personId attribute. We omit the model level validation which allows to pass null value, however if during the save() method it would still be null value - database level validation would throw an error.
first of all, when you associatea a model with belongsTo, sequelize will add automatically the target model primary key as a foreign key in the source model. in most of cases you don't need to define it by yourself, so in your case when you define IntramuralAthlete.belongsTo(Person) sequelize adds PersonId as a foreign key in IntramuralAthlete. your IntramuralAthlete model should looks like:
module.exports = function(sequelize, DataTypes) {
return sequelize.define('intramuralAthlete', {
grade: {
type: DataTypes.STRING,
allowNull: true
},
age: {
type: DataTypes.INTEGER(11),
allowNull: true
},
school: {
type: DataTypes.STRING,
allowNull: true
},
notes: {
type: DataTypes.STRING,
allowNull: true
}
});
};
now you can create an intramuralAthlete like your code above. for example:
let data = {
Person: {
firstName: 'Test',
lastName: 'User',
email: 'test#user.com'
},
grade: '12th',
notes: 'test notes'
}
IntramuralAthlete.create(data, {include: [Person]}).then((result) => {
// both instances should be created now
});
be carefull with the model name.
second I suppose that your IntramuralAthlete model has more than one belongsTo association. just you need to define them as the previous one association and sequelize will add their primary keys as foreign keys in the IntramuralAthlete model.
third, when you define a model, sequelize adds automatically an id datafield as a primary key and autoincrement and also adds createdAt and updatedAt datafields with a default CURRENT_TIMESTAMP value, so you don't need to define them in your model

Resources