3
votes

While trying to use Sequelize JS v4 with ES6 classes I'm having trouble with the execution of the instance methods. For some reason, they seem to not exist although being defined in code.

Here's an example -

Model File

'use strict';
const Sequelize = require("sequelize");

class Model extends Sequelize.Model {

  static init(sequelize, DataTypes) {
    return super.init(
      {
        // properties
      },
      { sequelize }
    );
  }

  static associate(models) {
  }


  async modelMethod() {

  }

}

module.exports = Model;

Model Initiation

let modelClass = require('../models/' + modelFile);

let model = modelClass.init(sequelize, Sequelize);

The model is then being called in a controller file as being the controller's property

async controllerMethod(req, res) {
    let info = await this.model.modelMethod();
    res.send(info);
}

And the error which I receive -

TypeError: this.model.modelMethod is not a function at Controller.controllerMethod (/usr/app/controllers/controller.js:83:41) at Layer.handle [as handle_request] (/usr/app/node_modules/express/lib/router/layer.js:95:5) at next (/usr/app/node_modules/express/lib/router/route.js:137:13) at Route.dispatch (/usr/app/node_modules/express/lib/router/route.js:112:3) at Layer.handle [as handle_request] (/usr/app/node_modules/express/lib/router/layer.js:95:5) at /usr/app/node_modules/express/lib/router/index.js:281:22 at param (/usr/app/node_modules/express/lib/router/index.js:354:14) at param (/usr/app/node_modules/express/lib/router/index.js:365:14) at param (/usr/app/node_modules/express/lib/router/index.js:365:14) at Function.process_params (/usr/app/node_modules/express/lib/router/index.js:410:3) at next (/usr/app/node_modules/express/lib/router/index.js:275:10) at sequelize.models.Session.findOne.then (/usr/app/app.js:44:24) at tryCatcher (/usr/app/node_modules/bluebird/js/release/util.js:16:23) at Promise._settlePromiseFromHandler (/usr/app/node_modules/bluebird/js/release/promise.js:512:31) at Promise._settlePromise (/usr/app/node_modules/bluebird/js/release/promise.js:569:18) at Promise._settlePromise0 (/usr/app/node_modules/bluebird/js/release/promise.js:614:10) at Promise._settlePromises (/usr/app/node_modules/bluebird/js/release/promise.js:693:18) at Async._drainQueue (/usr/app/node_modules/bluebird/js/release/async.js:133:16) at Async._drainQueues (/usr/app/node_modules/bluebird/js/release/async.js:143:10) at Immediate.Async.drainQueues (/usr/app/node_modules/bluebird/js/release/async.js:17:14) at Immediate.args.(anonymous function) [as _onImmediate] (/usr/local/lib/node_modules/pm2/node_modules/event-loop-inspector/index.js:133:29) at runCallback (timers.js:810:20)

Trying to output the classes' methods gets this -

console.log(Object.getOwnPropertyNames(this.model));


[ 'length',
  'prototype',
  'init',
  'associate',
  'name',
  'sequelize',
  'options',
  'associations',
  'underscored',
  'tableName',
  '_schema',
  '_schemaDelimiter',
  'rawAttributes',
  'primaryKeys',
  '_timestampAttributes',
  '_readOnlyAttributes',
  '_hasReadOnlyAttributes',
  '_isReadOnlyAttribute',
  '_dataTypeChanges',
  '_dataTypeSanitizers',
  '_booleanAttributes',
  '_dateAttributes',
  '_hstoreAttributes',
  '_rangeAttributes',
  '_jsonAttributes',
  '_geometryAttributes',
  '_virtualAttributes',
  '_defaultValues',
  'fieldRawAttributesMap',
  'fieldAttributeMap',
  'uniqueKeys',
  '_hasBooleanAttributes',
  '_isBooleanAttribute',
  '_hasDateAttributes',
  '_isDateAttribute',
  '_hasHstoreAttributes',
  '_isHstoreAttribute',
  '_hasRangeAttributes',
  '_isRangeAttribute',
  '_hasJsonAttributes',
  '_isJsonAttribute',
  '_hasVirtualAttributes',
  '_isVirtualAttribute',
  '_hasGeometryAttributes',
  '_isGeometryAttribute',
  '_hasDefaultValues',
  'attributes',
  'tableAttributes',
  'primaryKeyAttributes',
  'primaryKeyAttribute',
  'primaryKeyField',
  '_hasPrimaryKeys',
  '_isPrimaryKey',
  'autoIncrementAttribute',
  '_scope',
  '_scopeNames' ]
2
Try removing the custom init place in the class and directly call the init method of the parent. Also, to use the method, you would need to initialize the class, with something such as new Model or Model.build({}) which would initialize the model and enable you to use the method. - Manish M Demblani
@ManishMDemblani The init method calls the super's method so I assume that that's not the problem, as for new Model I've been trying that without success, but now trying to call new on the return value of init might do the trick. I'll update it if it works - Yehonatan
The reason this is because Sequelize works on the basis of ActiveRecord pattern, which lets you use the same model, when uninitialized to query the database and also contain instances of data from the database once fetched and initialized. - Manish M Demblani
I might need to dig in to it in order to understand it better, thanks! @ManishMDemblani - Yehonatan

2 Answers

4
votes

After reading Manish's comment I've decided to give it a new try.

Apparently, the way for instance methods to work is by initializing the model AFTER calling the .init method.

So -

let model = ModelClass.init(sequelize, Sequelize);
model = new model();

This will allow to call model.modelMethod without a problem

1
votes

The static init function isn't a constructor. It "initializes" the class by setting up its functionality, and returns then returns the class itself.

Here's some sample code to illustrate this:

class User extends Sequelize.Model {
  static init(sequelize, DataTypes) {
    return super.init({
      firstName: {
        type: DataTypes.STRING
      },
      lastName: {
        type: DataTypes.STRING
      }
    }, {
      sequelize
    });
  }

  fullname() {
    return `${this.firstName} ${this.lastName}`;
  }
}

// You don't need to capture the return here, I'm just doing it to show what it is.
const result = User.init(sequelize, Sequelize);
console.log(result === User); // true

const user = new User();
console.log(typeof user.fullname === 'function'); // true