I'm using nodejs v8.9.0 + expressjs v4.14.0 + sequelize v3.25.0 to build a web app in which people can publish articles.
I have these models
/** ARTICLE MODEL **/
module.exports = function(sequelize, DataTypes) {
var Article = sequelize.define('articles', {
title: {
type: DataTypes.STRING(255),
field: 'title'
},
content: {
type: DataTypes.TEXT,
field: 'content'
}
anonymous: {
type: DataTypes.BOOLEAN,
field: 'anonymous'
}
},
{
classMethods: {
Article .belongsTo(
models.users,
{
foreignKey: 'userId'
}
);
}
})
And
/** USER MODEL **/
module.exports = function(sequelize, DataTypes) {
var User = sequelize.define('users', {
firstName: {
type: DataTypes.STRING(50),
field: 'firstName'
},
lastName: {
type: DataTypes.STRING(200),
field: 'lastName'
},
email: {
type: DataTypes.STRING(200),
field: 'email'
},
},
{
classMethods: {}
})
Users can publish articles anonymously or not
Now when I'm querying articles list, I want to include User model only if anonymous is false.
How to do that ?
Not so elegant solution, but work:
Article.findAll({
include: [{
model: User,
required: false,
where: sequelize.where(sequelize.literal('anonymous'), false)
}],
})
Related
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
I have defined two database models in Sequelize.js:
User:
module.exports = (sequelize, DataTypes) => {
const model = sequelize.define('User', {
email: {
type: DataTypes.STRING,
unique: true,
allowNull: false
},
password: {
type: DataTypes.CHAR,
length: 60,
allowNull: false
}
}
});
model.associate = models => {
model.belongsToMany(models.Role, {
hooks: true,
through: 'user_roles'
})
};
return model;
};
Role:
module.exports = (sequelize, DataTypes) => {
const model = sequelize.define('Role',
{
name: {
type: DataTypes.STRING,
unique: false,
allowNull: false
}
}
);
model.associate = models => {
model.belongsToMany(models.User, {
hooks: true,
through: 'user_roles'
});
};
return model;
};
Table user_roles is create automatically. When I query for User I also want to include roles array in the result. I do so like this:
const user = await User.findOne({
where: {
email: data.email
},
attributes: ['id', 'password', 'email'],
include: [{
model: 'user_roles',
attributes: ['key']
}]
});
But I get error: TypeError: include.model.getTableName is not a function what means that model: must be real model and can't do just table name. But how to get data from this table then?
I also tried:
const user = await User.findOne({
where: {
email: data.email
},
attributes: ['id', 'password', 'email'],
include: [{
model: Role,
attributes: ['key']
}]
});
But this gives me weird object with too much data:
Roles: [
{
key: 'USER',
user_roles: {
createdAt: 2019-09-17T16:19:44.000Z,
updatedAt: 2019-09-17T16:19:44.000Z,
RoleId: 1,
UserId: 4
}
}
]
I need just the key: 'USER'
Try adding through: { attributes: [] }.
const user = await User.findOne({
where: {
email: data.email
},
attributes: ['id', 'password', 'email'],
through: { attributes: [] },
include: [{
model: Role,
attributes: ['key']
}]
});
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
}]
});
I'm using nodejs v8.9.0 + expressjs v4.14.0 + sequelize v3.25.0 to build a web app in which people can ask questions each others. A user can ask a public or a private(for specifics users) question
Here is the user model:
/** USER MODEL **/
module.exports = function(sequelize, DataTypes) {
var User = sequelize.define('users', {
firstName: {
type: DataTypes.STRING(50),
field: 'firstName'
},
lastName: {
type: DataTypes.STRING(200),
field: 'lastName'
},
email: {
type: DataTypes.STRING(200),
field: 'email'
},
},
{
classMethods: {
User.belongsToMany(
models.questions,
{
through: 'QuestionRecipient', foreignKey: 'userId'
}
);
}
})
Here is the question model:
/** QUESTION MODEL **/
module.exports = function(sequelize, DataTypes) {
var Question = sequelize.define('questions', {
content: {
type: DataTypes.TEXT,
field: 'content'
},
target: {
type: DataTypes.INTEGER,
field: 'target',
defaultValue: 1 // 1 = public / 2 = private
}
},
{
classMethods: {
Question.belongsToMany(
models.users,
{
through: 'QuestionRecipient', foreignKey: 'questionId'
}
);
}
})
Now when I'm querying questions, I want to include users through 'QuestionRecipient' only if question.target = 2 which means the question is for specifics users
How to do this query ?
Thanks
I am new to Sails and creating a simple application.
I am stuck with data model now.
User model is as follows:
module.exports = {
attributes: {
firstName: {
type: 'string'
},
lastName: {
type: 'string'
},
email: {
type: 'email',
required: true
},
password: {
type: 'String'
},
passwordSalt: {
type: 'String'
},
projects:{
collection: 'ProjectMember',
via: 'userId'
}
}
};
I need one more Model called TinyUser which gets some of attributes from User (Like Foreign key to User), so that I can access TinyUser instead of accessing User directly.
TinyUser Model is as follows:
module.exports = {
tableName: 'User',
attributes: {
firstName:{
type: 'string'
},
lastName: {
type: 'string'
},
email: {
type: 'email'
}
}
};
ProjectMember model is as follows:
module.exports = {
attributes: {
projectId: {
model: 'Project'
},
userId: {
model: 'TinyUser'
},
projectRole: {
model: 'ProjectRole'
},
}
};
In Sails, Is there anyway I can save data in TinyUser without actually creating but just holding some of attributes of User table data?
To Make it clear, If there is another table called Task and it is trying to access user data then TinyUser is better option as a model rather than User as it holds only required info. for task rather than storing all other fields which User does.
Is there any way I can fix this?
Thanks In Advance
Do you mean you need to have the "TinyUser" inherited from the "User" model?
You can do it like this.
/api/services/baseUserModel.js:
module.exports = {
attributes: {
firstName: 'string',
lastName: 'string',
email: {
type: 'email',
required: true
},
password: 'string',
passwordSalt: 'string',
projects: {
collection: 'ProjectMember',
via: 'userId'
}
}
};
/api/models/TinyUser.js:
var baseUserModel = require('../services/baseUserModel'),
_ = require('lodash');
module.exports = _.merge({
tableName: 'User',
attributes: {
firstName: 'string',
lastName: 'string',
email: {
type: 'email',
required: true
}
}
}, baseUserModel);
And the ProjectMember model should be:
module.exports = {
attributes: {
projectId: {
model: 'project'
},
userId: {
model: 'tinyuser'
},
projectRole: {
model: 'projectrole'
}
}
};