I am trying to associate my User model with my Organization model, but I'm running into an error that says, Error: user is not associated to organization! despite the fact that I am following the process to associate the User to my Organization. Is it possible that the type of association method that I am using is causing the problem?
User Model (user.js):
var bcrypt = require('bcrypt-nodejs');
module.exports = function(sequelize, DataTypes) {
var User = sequelize.define('user', {
user_id: {
type: DataTypes.INTEGER,
autoIncrement: true,
primaryKey: true
},
firstName: {
type: DataTypes.STRING,
field: 'first_name'
},
lastName: {
type: DataTypes.STRING,
field: 'last_name'
},
email: {
type: DataTypes.STRING,
isEmail: true,
unique: true
},
password: DataTypes.STRING,
organizationId: {
type: DataTypes.INTEGER,
field: 'organization_id',
allowNull: true
}
}, {
freezeTableName: true,
classMethods: {
generateHash: function(password) {
return bcrypt.hashSync(password, bcrypt.genSaltSync(8), null);
},
associate: function(db) {
User.belongsTo(db.Organization, {foreignKey: 'organizationId'});
},
},
instanceMethods: {
validPassword: function(password) {
return bcrypt.compareSync(password, this.password);
},
},
});
return User;
}
Organization model (organization.js):
module.exports = function(sequelize, DataTypes) {
var Organization = sequelize.define('organization', {
organizationId: {
type: DataTypes.INTEGER,
field: 'organization_id',
autoIncrement: true,
primaryKey: true
},
organizationName: {
type: DataTypes.STRING,
field: 'organization_name'
},
admin: DataTypes.STRING,
members: DataTypes.STRING
},{
freezeTableName: true
});
return Organization;
}
index for tables to connect (db-index.js):
var Sequelize = require('sequelize');
var path = require('path');
var config = require(path.resolve(__dirname, '..', '..','./config/config.js'));
var sequelize = new Sequelize(config.database, config.username, config.password, {
host:'localhost',
port:'3306',
dialect: 'mysql'
});
sequelize.authenticate().then(function(err) {
if (!!err) {
console.log('Unable to connect to the database:', err)
} else {
console.log('Connection has been established successfully.')
}
});
var db = {}
db.Organization = sequelize.import(__dirname + "/organization");
db.User = sequelize.import(__dirname + "/user");
db.Records = sequelize.import(__dirname + "/records");
db.User.associate(db);
db.Records.associate(db);
db.sequelize = sequelize;
db.Sequelize = Sequelize;
sequelize.sync();
module.exports = db;
Are of a route call that triggers this error:
appRoutes.route('/sign-up/organization')
.get(function(req, res){
models.User.find({
where: {
user_id: req.user.email
}, attributes: [ 'user_id', 'email'
]
}).then(function(user){
res.render('pages/sign-up-organization.hbs',{
user: req.user
});
})
})
.post(function(req, res, user){
models.Organization.create({
organizationName: req.body.organizationName,
admin: req.body.admin,
user: {
organizationId: req.body.organizationId
}
}, { include: [models.User] }).then(function(){
console.log(user.user_id);
res.redirect('/app');
}).catch(function(error){
res.send(error);
console.log('Error at Post');
})
});
You need to set up the reverse of the association, Organization hasMany Users
Related
I have 2 models, entity and acl,
acl rules can be included/excluded and target is reference to entity ( or special case 'public' )
ACL:
const { DataTypes, Model } = require('sequelize');
class ACL extends Model {}
module.exports = (sequelize) => {
const attributes = {
id: {
type: DataTypes.INTEGER(11),
allowNull: false,
primaryKey: true,
autoIncrement: true,
},
object_unique_id: {
type: DataTypes.STRING(36),
allowNull: false,
},
principal_unique_id: {
type: DataTypes.STRING(36),
allowNull: false,
},
rule: {
type: DataTypes.INTEGER(11),
allowNull: false,
defaultValue: 1,
comment: '1=allow, 0=deny',
},
};
const options = {
modelName: 'acl',
tableName: 'acl',
sequelize, // We need to pass the connection instance
};
return ACL.init(attributes, options);
};
ENTITY:
const { DataTypes, Model } = require('sequelize');
const { Op } = require('sequelize');
class Entity extends Model {}
module.exports = (sequelize, ACL) => {
Entity.init(
{
id: {
type: DataTypes.INTEGER(11).UNSIGNED,
allowNull: false,
primaryKey: true,
autoIncrement: true,
},
unique_id: {
type: DataTypes.STRING(36),
allowNull: false,
},
name: {
type: DataTypes.STRING(50),
allowNull: false,
},
},
{
indexes: [
{
name: 'unique_id',
unique: true,
fields: ['unique_id'],
},
],
tableName: 'entity',
sequelize,
modelName: 'entity',
scopes: {
byPrincipalUID(principalUID) {
const includedCondition = [
{ principal_unique_id: 'public' },
];
if (principalUID) {
includedCondition.push({
principal_unique_id: principalUID,
});
}
return {
include: [
{
model: ACL,
where: {
[Op.and]: [
{ rule: 1 },
{ [Op.or]: includedCondition },
],
},
},
],
};
},
public: {
include: [
{
model: ACL,
where: {
[Op.and]: [
{ rule: 1 },
{ principal_unique_id: 'public' },
],
},
},
],
},
},
}
);
Entity.hasMany(ACL, {
sourceKey: 'unique_id',
foreignKey: 'object_unique_id',
});
return Entity;
};
test cases:
describe('AclService', () => {
it('should works with scopes', async () => {
const { Sequelize } = require('sequelize');
const sequelize = new Sequelize('sqlite::memory:');
const ACL = require('../db/models/acl.model')(sequelize);
const Entity = require('../db/models/entity.model')(sequelize, ACL);
await ACL.sync();
await Entity.sync();
await Entity.create({ name: 'test 1', unique_id: 's1' });
await Entity.create({ name: 'test 2', unique_id: 's2' });
await Entity.create({ name: 'test 3', unique_id: 's3' });
await ACL.create({
object_unique_id: 's1',
rule: 1,
principal_unique_id: 'public',
});
await ACL.create({
object_unique_id: 's2',
rule: 1,
principal_unique_id: 'c1',
});
await ACL.create({
object_unique_id: 's3',
rule: 1,
principal_unique_id: 'p1',
});
const all = await Entity.findAll();
expect(all.length).toBe(3);
const publicSubjects = await Entity.scope('public').findAll();
expect(publicSubjects.length).toBe(1);
const companySubjects = await Entity.scope({
method: ['byPrincipalUID', 'c1'],
}).findAll();
expect(companySubjects.length).toBe(2);
const accountSubjects = await Entity.scope({
method: ['byPrincipalUID', 'p1'],
}).findAll();
expect(accountSubjects.length).toBe(2);
});
it('should works with scopes and deny rule', async () => {
const { Sequelize } = require('sequelize');
const sequelize = new Sequelize('sqlite::memory:');
const ACL = require('../db/models/acl.model')(sequelize);
const Entity = require('../db/models/entity.model')(sequelize, ACL);
await ACL.sync();
await Entity.sync();
await Entity.create({ name: 'test 1', unique_id: 's1' });
await Entity.create({ name: 'test 2', unique_id: 's2' });
await ACL.create({
object_unique_id: 's1',
rule: 1,
principal_unique_id: 'public',
});
await ACL.create({
object_unique_id: 's2',
rule: 1,
principal_unique_id: 'public',
});
await ACL.create({
object_unique_id: 's2',
rule: 0,
principal_unique_id: 'c1',
});
const all = await Entity.findAll();
expect(all.length).toBe(2);
const publicSubjects = await Entity.scope('public').findAll();
expect(publicSubjects.length).toBe(2);
//s2 is denied for c1
const companySubjects = await Entity.scope({
method: ['byPrincipalUID', 'c1'],
}).findAll();
expect(companySubjects.length).toBe(1);
});
});
so with 'Grant' permission all works as expected.
But have no good ideas how to exclude 'Deny' records.
So in second test I have 2 'public' entities, but second one have "deny" rule for c1
I have only one idea, somehow use raw subquery something like
NOT EXISTS(SELECT id FROM acl
WHERE acl.object_unique_id = ${targetTable}.unique_id
AND (acl.rule = 0 AND (
.....
)
)
but not sure how to make it using sequelize
I have got a few sql tables as
export default (sequelize, Sequelize) => {
return sequelize.define('teacher', {
tagline: {
type: Sequelize.TEXT,
},
modeOfPayment: {
type: Sequelize.STRING,
},
modeOfSession: {
type: Sequelize.STRING,
},
preferredTimeZones: {
type: Sequelize.STRING,
},
titleForSessions: {
type: Sequelize.STRING,
},
availableForWork: {
type: Sequelize.BOOLEAN,
},
});
};
export default (sequelize, Sequelize) => {
return sequelize.define('skill', {
name: {
type: Sequelize.STRING,
unique: true,
allowNull: false,
},
});
};
export default (sequelize, Sequelize) => {
return sequelize.define('category', {
name: {
type: Sequelize.STRING,
unique: true,
allowNull: false,
},
});
};
Here is the model relation between them
Teacher.belongsToMany(Skill, {
through: 'skill_teacher',
});
Skill.belongsToMany(Teacher, {
through: 'skill_teacher',
});
Category.hasMany(Skill);
Skill.belongsTo(Category);
Earlier I needed to query count of teachers in each skill, here's my controller for that
// #desc Get skill count for each skill
// #route GET /api/skills/count
// #access Public
const getSkillCount = asyncHandler(async (req, res) => {
try {
const skills = await Skill.findAll({
attributes: [
'id',
'name',
[sequelize.fn('count', sequelize.col('teachers.id')), 'teacherCount'],
],
include: [{ attributes: [], model: Teacher }],
group: ['skill.id'],
});
res.json(skills);
} catch (err) {
console.log(err.message);
res.status(500);
throw new Error(err.message);
}
});
Now, the skills are grouped in categories. So I wanna query a list of skills having their own teacher count grouped in their own categories, which category also having a skillCount column. I tried this but it is not giving my desired results
// #desc Get category and their skills counts
// #route GET /api/categories/skills/count
// #access Public
const getCategorySkillCounts = asyncHandler(async (req, res) => {
try {
const categories = await Category.findAll({
attributes: [
'id',
'name',
[sequelize.fn('count', sequelize.col('skills.id')), 'skillCount'],
],
include: {
model: Skill,
include: [{ model: Teacher }],
attributes: [
'id',
'name',
[sequelize.fn('count', sequelize.col('teachers.id')), 'teacherCount'],
],
group: ['skill.id'],
},
group: ['category.id'],
});
res.json(categories);
} catch (err) {
console.log(err.message);
res.status(500);
throw new Error(err.message);
}
});
I am using sequelize CLI to generate and run db migrations. The issue I am having is the id field set to data type Sequelize.UUID appearing as an autoincrement integer in mysql. Here is my user model and migration:
User Model
'use strict';
module.exports = (sequelize, DataTypes) => {
const User = sequelize.define('User', {
UserName: {type:DataTypes.STRING,unique:true,allowNull:false},
FirstName:{type: DataTypes.STRING,allowNull:true},
LastName: {type:DataTypes.STRING,allowNull:true},
Email: {type:DataTypes.STRING,allowNull:false,unique:true,validate: { isEmail: {msg: "Invalid Email"} }},
Password: {type:DataTypes.STRING,allowNull:false},
Avatar: {type:DataTypes.STRING,allowNull:true},
}, {});
User.associate = function(models) {
User.hasMany(models.RoleUser,
{
foreignKey:'UserId',
as:'userroles',
sourceKey:'id'
}),
User.belongsTo(models.Country,
{
foreignKey:'CountryId',
targetKey:'id'
}),
User.belongsToMany(models.Role,
{
through: 'RoleUser',
foreignkey:'UserId'
})
};
return User;
};
**User Migration file:**
'use strict';
const uuidv4 = require('uuid/v4');
module.exports = {
up: (queryInterface, Sequelize) => {
return queryInterface.createTable('Users', {
id: {
allowNull: false,
primaryKey: true,
type: Sequelize.UUID
defaultValue:uuidv4()
},
UserName: {
type: Sequelize.STRING
},
FirstName: {
type: Sequelize.STRING
},
LastName: {
type: Sequelize.STRING
},
Email: {
type: Sequelize.STRING
},
Password: {
type: Sequelize.STRING
},
Avatar: {
type: Sequelize.STRING
},
createdAt: {
allowNull: false,
type: Sequelize.DATE
},
updatedAt: {
allowNull: false,
type: Sequelize.DATE
}
});
},
down: (queryInterface, Sequelize) => {
return queryInterface.dropTable('Users');
}
};
AFTER MIGRATION, THE HIS FIELD IS CONVERTED TO INT AUTOINCREMENT IN MYSQL:
id: {
allowNull: false,
primaryKey: true,
type: Sequelize.UUID
defaultValue:uuidv4()
},
Any pointer as to why this is happening? Please assist. Even the associations seem not to be formed at all as foreign keys are of type Sequelize.UUID
I fixed the problem by adding the id field on model with primary key property set to true.
id: {
type:DataTypes.UUID,
allowNull:false,
unique:true,
primaryKey:true
},
Its like sequelize will automatically generate id field of type INTEGER AUTOINCREAMENT if the model does not have a field with primary key set to true.
I'm trying to build a simple n:m relationship, the case is, one Doctor can have many Patients and a Patient can have many Doctors...
Doctor Model
module.exports = (sequelize, DataTypes) => {
const Doctor = sequelize.define('Doctor', {
id: {
type: DataTypes.INTEGER,
primaryKey: true,
autoIncrement: true
},
doc_crm: {
type: DataTypes.STRING,
allowNull: false
}
}, {
tableName: 'doctors',
timestamps: false
})
Doctor.associate = function (models) {
Doctor.belongsToMany(models.Patient, { as: 'Patients', through: { model: models.PatientDoctor } })
Doctor.belongsTo(models.User, { foreignKey: 'user_id' })
}
return Doctor
}
Patient Model
module.exports = (sequelize, DataTypes) => {
const Patient = sequelize.define('Patient', {
id: {
type: DataTypes.INTEGER,
primaryKey: true,
autoIncrement: true
}
}, {
tableName: 'patients',
timestamps: false
})
Patient.associate = function (models) {
Patient.belongsToMany(models.Doctor, { as: 'Doctors', through: { model: models.PatientDoctor } })
Patient.belongsTo(models.User, { foreignKey: 'user_id' })
}
return Patient
}
And the joining table is
PatientDoctor
module.exports = (sequelize, DataTypes) => {
const PatientDoctor = sequelize.define('PatientDoctor', {
id: {
type: DataTypes.INTEGER,
primaryKey: true,
autoIncrement: true
}
}, {
tableName: 'patient_doctors',
timestamps: false
})
return PatientDoctor
}
So, when i query the PatientDoctor model passing the Patient and Doctor i get a
{
"name": "SequelizeEagerLoadingError"
}
What i'm doing wrong? i tried a lot of stuff, all without luck.
Thanks in advance!
I am trying to to create an object with a child (association) that has the Id of the created object passed as a value to its property. I have tried to follow the documentation, but there is no value being passed with the SQL command.
Here is the SQL query:
INSERT INTO `organization` (`organization_id`,`organization_name`,`admin`,`updatedAt`,`createdAt`) VALUES (DEFAULT,'dfsadfadsfa','ter#test.cm','2016-01-08 02:23:04','2016-01-08 02:23:04');
No reference to user
Here is the route inserting into organization:
var express = require('express');
var appRoutes = express.Router();
var passport = require('passport');
var localStrategy = require('passport-local').Strategy;
var models = require('../models/db-index');
appRoutes.route('/sign-up/organization')
.get(function(req, res){
models.User.find({
where: {
user_id: req.user.email
}, attributes: [ 'user_id', 'email'
]
}).then(function(user){
res.render('pages/sign-up-organization.hbs',{
user: req.user
});
})
})
.post(function(req, res, user){
models.Organization.create({
organizationName: req.body.organizationName,
admin: req.body.admin,
User: [{
organizationId: req.body.organizationId
}]
}, { include: [models.User] }).then(function(){
console.log(user.user_id);
res.redirect('/app');
}).catch(function(error){
res.send(error);
console.log('Error at Post');
})
});
Here is the form submission:
<div class="container">
<div class="col-md-6 col-md-offset-3">
<form action="/app/sign-up/organization" method="post">
<p>{{user.email}}</p>
<input type="hidden" name="admin" value="{{user.email}}">
<input type="hidden" name="organizationId">
<label for="sign-up-organization">Company/Organization Name</label>
<input type="text" class="form-control" id="sign-up-organization" name="organizationName" value="" placeholder="Company/Organization">
<br />
<button type="submit">Submit</button>
</form>
user.js model:
var bcrypt = require('bcrypt-nodejs');
module.exports = function(sequelize, DataTypes) {
var User = sequelize.define('user', {
user_id: {
type: DataTypes.INTEGER,
autoIncrement: true,
primaryKey: true
},
firstName: {
type: DataTypes.STRING,
field: 'first_name'
},
lastName: {
type: DataTypes.STRING,
field: 'last_name'
},
email: {
type: DataTypes.STRING,
isEmail: true,
unique: true
},
password: DataTypes.STRING,
organizationId: {
type: DataTypes.INTEGER,
field: 'organization_id',
allowNull: true
}
}, {
freezeTableName: true,
classMethods: {
generateHash: function(password) {
return bcrypt.hashSync(password, bcrypt.genSaltSync(8), null);
},
},
instanceMethods: {
validPassword: function(password) {
return bcrypt.compareSync(password, this.password);
},
},
});
return User;
}
organization.js model:
module.exports = function(sequelize, DataTypes) {
var Organization = sequelize.define('organization', {
organizationId: {
type: DataTypes.INTEGER,
field: 'organization_id',
autoIncrement: true,
primaryKey: true
},
organizationName: {
type: DataTypes.STRING,
field: 'organization_name'
},
admin: DataTypes.STRING,
members: DataTypes.STRING
},{
freezeTableName: true,
classMethods: {
associate: function(db) {
Organization.hasMany(db.User, {foreignKey: 'user_id'});
},
},
});
return Organization;
}
db-index.js: where the two are associated:
var Sequelize = require('sequelize');
var path = require('path');
var config = require(path.resolve(__dirname, '..', '..','./config/config.js'));
var sequelize = new Sequelize(config.database, config.username, config.password, {
host:'localhost',
port:'3306',
dialect: 'mysql'
});
sequelize.authenticate().then(function(err) {
if (!!err) {
console.log('Unable to connect to the database:', err)
} else {
console.log('Connection has been established successfully.')
}
});
var db = {}
db.Organization = sequelize.import(__dirname + "/organization");
db.User = sequelize.import(__dirname + "/user");
db.Annotation = sequelize.import(__dirname + "/annotation");
db.Organization.associate(db);
db.Annotation.associate(db);
db.sequelize = sequelize;
db.Sequelize = Sequelize;
sequelize.sync();
module.exports = db;
When I use Sequelize, I usually create a function that creates an instance of the sequelize model using the build method and I use the instance to save that instance to the database. With the returned instance you can do whatever you need.
var instance = models.Organization.build(data);
instance.save().then(function(savedOrgInstance){
savedOrgInstance.createUser(userData).then(function(responseData){
//do whatever you want with the callback })
})
I can't say I've seen a create statement as you've written it. What is the extra include statement for?
That should give that newly created user the association you're looking for.
You should checkout the setAssociaton, getAssociation, createAssociation methods in the docs. http://docs.sequelizejs.com/en/latest/docs/associations/