Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

beforeUpdate hook is not being called #4190

Closed
i-manolov opened this issue Jul 26, 2015 · 26 comments
Closed

beforeUpdate hook is not being called #4190

i-manolov opened this issue Jul 26, 2015 · 26 comments

Comments

@i-manolov
Copy link

Hello,
I am trying to call the beforeUpdate hook which uses the same function as beforeCreate hook but it doesn't even get called:

//user model
'use strict';

var bcrypt = require('bcrypt-nodejs');

module.exports = function(sequelize, DataTypes) {
  var User = sequelize.define('User', {
      username: DataTypes.STRING,
      password: DataTypes.STRING,
      email: DataTypes.STRING
    }, {
      instanceMethods: {
        comparePassword: function(candidatePassword, cb) {
          var user = this;
          bcrypt.compare(candidatePassword, user.get('password'), function(err, isMatch) {
            if (err) return cb(err);
            cb(null, isMatch);
          });
        }
      },
      hooks: {
        beforeCreate: hashPassword,
        beforeUpdate: hashPassword
      },
      classMethods: {
        associate: function(models) {
          User.hasMany(models.PasswordReset, {foreignKey: 'userId'});
        }
      }
    } 
  );

  return User;
};


var hashPassword = function(instance, optons, next) {
  console.log(arguments);
  var SALT_FACTOR = 5;

  console.log('in hook');
  console.log('PWD CHANGED? ' + instance.changed('password'));

  if (!instance.changed('password')) return next();

  bcrypt.genSalt(SALT_FACTOR, function(err, salt) {
    if (err) return next(err);

    bcrypt.hash(instance.get('password'), salt, null, function(err, hash) {
      if (err) return next(err);
      instance.set('password', hash);
      console.log(instance.get('password'))
      next();
    });
  });
};

And i try to hash the password before update but the hook doesnt even get called:

    PasswordReset.findOne({ where: { token: req.params.token, expirationDate: {gt: new Date()}}, include: [{model: User}]}).then(function (pwdReset) {
      if (!pwdReset) {
        req.flash('error', 'Password reset token is invalid or has expired.');
        return res.redirect('/forgot');
      }

      User.update({ password: req.body.password }, { where: { id: pwdReset.userId }}).then(function (user){
        req.flash('success', 'Password reset was successful');
        res.redirect('/login');
      });
    }).catch(function (err) {
      return next(err);
    });

Any idea why the password is hashing beforeCreate but not beforeUpdate? What am i doing wrong?

@janmeier
Copy link
Member

Duplicate of #4170 (you are passing the instance and class methods in two separate objects, they should be in the same)

@i-manolov i-manolov changed the title Accessing instance method returns undefined is not a function beforeUpdate hook is not being called Jul 26, 2015
@i-manolov
Copy link
Author

thank you. i forgot to save my updated version of the question. can you please help me as to why beforeUpdate isn't being called but beforeCreate is?

@janmeier
Copy link
Member

You are calling bulk update (which updates several), so you should attach a beforeBulkCreate instead - or passing individualHooks: true in the options object http://docs.sequelizejs.com/en/latest/api/model/#updatevalues-options-promisearrayaffectedcount-affectedrows

@i-manolov
Copy link
Author

AHAAA. thank you for the help! 😃

@DoctypeRosenthal
Copy link

Hello.

I have a question: is it possible to activate the individualHooks-option for all update, create, destroy etc. methods? The only way I found was something like:

db.models.customers.update(args, { where: {id: args.id}, individualHooks: true })

Is there any other way than passing that option at runtime?

@janmeier
Copy link
Member

@DoctypeRosenthal You can add a beforeBulkUpdate hook which modifies the options, setting individualHooks: true

@DoctypeRosenthal
Copy link

DoctypeRosenthal commented Aug 16, 2016

Thanks for your quick reply!
Unfortunately, the documentation isn't clear about what gets passed to beforeBulkUpdate (http://sequelize.readthedocs.io/en/v3/api/hooks/#beforebulkupdate).
I thought it would give me an array of the affected records (since beforeUpdate gives you the affected record as its first argument) but that was not the case. So I couldn't perform any further actions with these records. It seems I have to configure { individualHooks: true } every time I call a create, update, delete, restore action :/

@janmeier
Copy link
Member

beforeBulkUpdate gets options - Which means you can set options.individualHooks = true, and then beforeUpdate will be called for each row.

beforeBulkUpdate does not have access to the rows, since we might not even select the rows (when individualHooks is false)

@DoctypeRosenthal
Copy link

DoctypeRosenthal commented Aug 16, 2016

Thanks!

I found the order in which all hooks are called:

(1)
  beforeBulkCreate(instances, options, fn)
  beforeBulkDestroy(options, fn)
  beforeBulkUpdate(options, fn)
(2)
  beforeValidate(instance, options, fn)
(-)
  validate
(3)
  afterValidate(instance, options, fn)
  - or -
  validationFailed(instance, options, error, fn)
(4)
  beforeCreate(instance, options, fn)
  beforeDestroy(instance, options, fn)
  beforeUpdate(instance, options, fn)
(-)
  create
  destroy
  update
(5)
  afterCreate(instance, options, fn)
  afterDestroy(instance, options, fn)
  afterUpdate(instance, options, fn)
(6)
  afterBulkCreate(instances, options, fn)
  afterBulkDestroy(options, fn)
  afterBulkUpdate(options, fn)

http://sequelize.readthedocs.io/en/v3/docs/hooks/

Can I set the individualHooks = true for the beforeUpdate and afterUpdate operations within beforeBulkUpdate? I need to call afterUpdate because I need the modified record.

@janmeier
Copy link
Member

Yes, the option applies to both

@DoctypeRosenthal
Copy link

DoctypeRosenthal commented Aug 16, 2016

Thx man! Maybe someone could point this out more clearly in the documentation...

Great work otherwise! I really appreciate Sequelize!

@L3V147H4N
Copy link

L3V147H4N commented Sep 12, 2016

Has this change? my beforeUpdate hook is not running

const Moment = require('moment');
var Sequelize = require('sequelize');
var Connection = require('../database/Connection');
var bcrypt = require('bcrypt');

var sequelize = Connection.getInstance();

var User = sequelize.define('User', {
  username: { type: Sequelize.STRING, unique: true },
  name: Sequelize.STRING,
  password: Sequelize.STRING,
  token: Sequelize.STRING,
  profile: Sequelize.STRING(4000)
}, {
  timestamps: true,
  tableName: 'User',
  hooks: {
    beforeCreate: (user, options, cb) => {
      bcrypt.hash(user.password, 10, (err, hash) => {
        user.password = hash;

        return cb(null, options);
      });
    },
    beforeUpdate: (user, options, cb) => {
      bcrypt.hash(user.password, 10, (err, hash) => {
        user.password = hash;

        return cb(null, options);
      });
    }
  },
  instanceMethods: {
    comparePassword: function compare (candidatePassword, cb) {
      bcrypt.compare(candidatePassword, this.getDataValue('password'), (err, isMatch) => {
        if (err) {
          cb(err);
        } else {
          cb(null, isMatch);
        }
      });
    }
  },
  getterMethods: {
    createdAt: function getCreadtedAt () {
      return Moment(this.getDataValue('createdAt')).format('DD/MM/YYYY HH:mm:ss');
    }
  }
});

module.exports = User;

im running

return new Promise((resolve, reject) => {
    sequelize.models.User.update({
      password: 1234
    }, {
      individualHooks: true,
      where: {
        id: 1
      }
    })
      .then(resolve)
      .catch(reject);
  });

@L3V147H4N
Copy link

If I run something like:

return new Promise((resolve, reject) => {
    sequelize.models.User.update({
      name: 'ADMIN'
    }, {
      individualHooks: true,
      where: {
        id: 1
      }
    })
      .then(resolve)
      .catch(reject);
  });

Then the beforeUpdate hook runs and hashes the password, but NOT on the password update itself

@L3V147H4N
Copy link

L3V147H4N commented Sep 12, 2016

NEVERMIND! for some reason it's working now ..... nargles

@vohoangnhut
Copy link

const updateUserByEmail = (usrNm,usrEml,usrPsw) => {
    return User.update({
                    usrNm: usrNm,
                    usrPsw: usrPsw
                }, {
                    where: {usrEml: usrEml},
                    individualHooks: true
                }
    );
}

Can you help my case ?
It came to beforeUpdate hook but
that update code just updated usrPsw only. it's ignore another field. I dont know why.

@L3V147H4N
Copy link

L3V147H4N commented Mar 21, 2017

My problem was that the Hook only affects individual Instances for ex:

let myUser = User.findById(1); myUser.update() // or save

when updating from the model, for ex:
User.update({}, { where: // })

the hook doesnt run because it's an individual hook, you need to set the option individualHooks

User.update({
    password: 'blabla123'
  }, {
    individualHooks: true,
    where: {
      username: 'admin'
    }
  });

or update the User from the user Instance rather than the Model

@jedwards1211
Copy link
Contributor

@janmeier when the where clause has a single value for the primary key, couldn't sequelize automatically treat that as an individual update instead of a bulk update?

@Aditya94A
Copy link

Aditya94A commented Oct 14, 2017

@jedwards1211 Oh my god yes. That would be great!

Why is so much of sequeilize so un-intuitive? And why does documentation not tell us about any such quirks?

I have to go treasure hunting for clues on Google/SO/Github to do anything with this library.

@DoctypeRosenthal
Copy link

@AdityaAnand1I agree! Sequelize is relatively unintuitive in comparison. Wish there was an orm like SQLAlchemy for node...

@elcitrovmtgrande
Copy link

Hi guys from 2018 ! Thank you for this topic, it helped me a lot a year later !
See you :)

@anton-piliugin
Copy link

This "individualHooks: true" defenitely should be in a documentation with simple example, i wasted about an hour to find the problem

@marianyfs
Copy link

user.model.js:

    'use strict';
    
    const bcrypt = require('bcryptjs');
    
    const hashCostFactor = 1;
    
    const hashPassword = async (user) => {
      if (user.changed('password')) {
        user.password = await bcrypt.hash(user.password, hashCostFactor);
      }
      return user
    }
    
    module.exports = (sequelize, DataTypes) => {
      const user = sequelize.define(
        'user',
        {
          username: {
            type: DataTypes.STRING(255),
            allowNull: false,
            unique: true,
          },
          password: {
            type: DataTypes.STRING(255),
            allowNull: false,
          },
        },
        {
          tableName: 'user',
        }
      );
    
      user.beforeCreate(
        async (user) => await hashPassword(user)
      );
    
      user.beforeUpdate(
        async (user) => await hashPassword(user)
      );
    
      /**
       * Checks if the parameter matches stored password
       * @param password
       */
      user.prototype.validatePassword = function validatePassword(password) {
        if(!password || !this.password)
          return false;
        return bcrypt.compareSync(password, this.password);
      };
    
      return user;
    }

On Sequelize update call:

updateUserById.js:

    module.exports = async ({ id, ...user }, options) =>
     await models.user.update(user,
        {
          where: {
            id
          },
          individualHooks: true,
          ...options
        }
      );
    };

models is from Sequelize models defined

@SimoneLeoni1987
Copy link

Those docs for this library is an hot mess...

@GaneshPL
Copy link

Thanks @janmeier, saved my day.

@scelloo-nathanielbabalola

Thank you this just saved me after like an hour, I even tried going through the instance way. By finding one from the DB
Then doing user.password = password then user.save()

@tskxz
Copy link

tskxz commented May 19, 2024

await Member.update(data, {where: {id: id}, individualHooks: true});

Adding individualHooks: true at my controller did the job

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests