I have an exports file that includes all the sequelize-models and then defines the relationship among the models. It looks something like:
// Snippet from the global init file
for (let modelFile of modelFileList) {
// ... Some code ...
// Require the file
appliedModels[modelName] = require(`../${MODEL_DIR}/${modelFile}`).call(null, _mysql);
}
//Define the relationship between the sql models
_defineRelationship(appliedModels);
function _defineRelationship(models) {
models._planAllocationModel.belongsTo(models._subscriptionModel, {
foreignKey: 'subscription_id',
targetKey: 'subscription_id'
});
}
But when I try to include the model like:
_subscriptionModel.findAll({
where: {
start_date: {
_lte: today // Get all subscriptions where start_date <= today
}
},
limit,
include: [
{
model: _planAllocationModel
}
]
});
There is an error thrown by sequelize: SequelizeEagerLoadingError: tbl_plan_allocation is not associated to tbl_subscription_info! What could be the reason for this? I have already initialized the relationshipt between the 2 models.
I was able to solve the problem. The relationship was defined as belongsTo which had to be changed to hasOne because of the type of join applied in the findAll query.
Related
I am new to sequilizejs and basically am trying to refactor code that i've written in the controller and came across classMethods and instanceMethods. I see instance methods defined like so:
/lib/model/db/users.js
module.exports = function(sequelize, DataTypes) {
var instance_methods = get_instance_methods(sequelize);
var User = sequelize.define("User", {
email : {
type : DataTypes.STRING,
allowNull : false
},
}, {
classMethods: class_methods,
instanceMethods : instance_methods,
});
return User;
};
function get_instance_methods(sequelize) {
return {
is_my_password : function( password ) {
return sequelize.models.User.hashify_password( password ) === this.password;
},
};
function get_class_methods(sequelize) {
return {
hashify_password : function( password ) {
return crypto
.createHash('md5')
.update(
password + config.get('crypto_secret'),
(config.get('crypto_hash_encoding') || 'binary')
)
.digest('hex');
},
};
My understanding of the above is that classMethods are generic functions defined for the whole model and instanceMethods are basically a reference to a given row in a table/model, am i right in assuming this ? this would be my primary question.
Also i don't see any reference of classMethods and instanceMethods in the docs HERE. I only found this previous answer HERE. That provides a somewhat comprehensive understanding of the difference between instanceMethods and classMethods.
Basically i'am just trying to confirm weather my understanding matches the intended usage for class vs instance methods and also links to the official docs for the same would be highly appreciated.
The official way to add both static and instance methods is using classes like this:
class User extends Model {
static classLevelMethod() {
return 'foo';
}
instanceLevelMethod() {
return 'bar';
}
getFullname() {
return [this.firstname, this.lastname].join(' ');
}
}
User.init({
firstname: Sequelize.TEXT,
lastname: Sequelize.TEXT
}, { sequelize });
See Models as classes
Your understand is correct. In short: classes can have instances. Models are classes. So, Models can have instances. When working with an instance method, you will notice the this — which is the context, which refers to that particular class/model instance.
Hence, if you have a User model that has:
an instance method called is_my_password
a class model called hashify_password
User.hashify_password('123') will return the hashed version of 123. The User instance is not needed here. hashify_password is general function attached to the User model (class).
Now, if you'd like to call is_my_password() you do need a User instance:
User.findOne({...}).then(function (user) {
if (user.is_my_password('123')) {
// ^ Here we call `is_my_password` as a method of the user instance.
...
}
}).catch(console.error)
In general, when you have functions that do not need the particular model instance data, you will define them as class methods. They are static methods.
And when the function works with the instance data, you define it as instance method to make it easier and nicer to call.
I have a belongs to many relationship with a joined table
courseTree.associate = function (models) {
models.courses.belongsToMany(models.courses, {
through: 'course_tree',
as: 'children',
foreignKey: 'parent_id',
otherKey: 'child_id'
});
};
Currently when I run a find I get all my courses back, even the courses that are children, this is expected behavior but I want to have another scope where I can request only the courses with children.
In my scope I have the following where:
scopes: {
parents: {
where: {
children: { [Op.not] : null }
}
}
}
But the Rest Api gives me the following output
"name": "GeneralError",
"message": "column courses.children does not exist",
"code": 500
In the documentation I can't find any way to do this, I tried sequelize.literal and the in operator but without success. Does anyone knows how this is done, I'm sure I'm missing something.
Solved this by using Sequelize.literal
courses.addScope('getParents', {
where: {
[Op.and]: Sequelize.literal('"courses"."id" NOT IN (SELECT "course_tree"."child_id" FROM "course_tree")')
}
})
Another problem I ran into was that the child was the same model as the parent, in which case the where is also applied on the child. I solved this by adding where: false in the include.
so i'm having problems with sequelize's one to many relationship, my associations are defined like this:
X.hasMany(Y, { as: 'Ys' });
Y.belongsTo(X, { as: 'X' });
and my findAll is here:
return X.findAll(
{
where: {
something: something,
},
include: [{ model: db.Y, as: 'Ys' }]
}
);
and this is producing the error:
"error": "Y (Ys) is not associated to X!"
Not quite sure what i'm doing wrong here :/
There's some confusion on your associations
logically 'X' has many 'Ys', association should be X.hasMany(Y, {as: 'Ys'});
'Y' belongs to 'X' should be Y.hasMany(X, {as: 'X'});
I have two table: Conversations and ConversationParticipants.
I need to get the list of conversations that both users 1 and 2 participate too.
In MySQL, the query would be this:
SELECT conversation_participants.conversation_id FROM conversation_participants
JOIN conversations t1
ON t1.conversation_id = conversation_participants.conversation_id
AND conversation_participants.user_id = 11
JOIN conversation_participants t2
ON t1.conversation_id = t2.conversation_id
AND t2.user_id = 2
However, in Sequelize I cannot understand how to set models relations so that I can make a single query. I tried this without success (please note that this is almost pseudo code, reported as such for clarity):
var Conversation = sequelize.define('conversations', {
conversationId: Sequelize.INTEGER.UNSIGNED,
});
var ConversationParticipant = sequelize.define('conversation_participants', {
participationId: Sequelize.INTEGER.UNSIGNED,
userId: Sequelize.INTEGER.UNSIGNED,
conversationId : Sequelize.INTEGER.UNSIGNED,
});
Conversation.hasMany(ConversationParticipant, { as : 'Participants'});
and then
ConversationParticipant.findAll({
where : { userId : 1 },
include : [{
model : Conversation,
as : 'conversation',
include : [{
model : ConversationParticipant,
as : 'Participants',
where : { userId : 2 }
}]
}]
I get the following error:
Error: conversation_participants is not associated with conversations!.
Any idea?
One way you could do it is find the conversations that have participants with user 1 or 2, and afterwards filter the conversations that have both of them:
const userIdsFilter = [1,2];
//find Conversations that have user 1 OR user 2 as participants,
Conversation.findAll({
include : [{
model : ConversationParticipant,
as : 'conversation_participants',
where : { userId : { $in: userIdsFilter }}
}]
}).then((conversations) =>
{
//filter the conversations that have the same length participants
//as the userIdsFilter length (that way it excludes the ones
//that have just one of the users as participants)
return conversations.filter((conversation) =>
conversation.conversation_participants.length === userIdsFilter.length);
}).then((conversations) =>
{
//do what you need with conversations..
})
You are missing the belongsTo association in the ConversationParticipant definition. Sequelize model needs explicit association since you are trying to access Conversation via the ConversationParticipant instance even though Conversation is associated ConversationParticipant is not.
Something like this in addition to hasMany association:
ConversationParticipant.belongsTo(Conversation, { as : 'conversation',
foreignKey: 'conversationId', targetKey: 'conversationId'});
And then remove the conversationId field definition from ConversationParticipant model since belongsTo also creates that for you.
I am having a hard time making use of BelongsTo relationship in a query.
Here is the relationship:
models.Area.hasMany(models.Airport);
models.Airport.belongsTo(models.Area, {foreignKey: 'areaId', as: 'area'});
Now I try to run a query:
models.Airport
.findAll( {
where: { active: 1 },
include: { model: models.Area }
})
I get an error:
Error: area is not associated to airport!
What am I doing wrong?
I figured it out. I can't say I fully understand the problem, but here it is.
When creating the relationship, I use as: 'area'.
The result is, I need to add as: 'area' to the include statement:
models.Airport
.findAll( {
where: { active: 1 },
include: { model: models.Area, as: 'area' }
})
Once the two match, things go better.