Problem with Sequelize and specifying foreign key in an association - node.js

I have placements that belong to an App. The foreign_key on the placements table is appId (not AppId). I have specified as follows but get the error:
Unhandled rejection SequelizeDatabaseError: column Placements.AppId does not exist
My Placement Model:
'use strict';
const App = require('./').App
module.exports = (sequelize, DataTypes) => {
const Placement = sequelize.define('Placement', {
name: DataTypes.STRING,
isActive: DataTypes.BOOLEAN,
}, {
tableName: 'placements'
});
Placement.associate = function(models) {
Placement.belongsTo(models.App, { foreignKey: 'appId'})
// associations can be defined here
};
return Placement;
};
How do I tell Sequelize that the foreignKey is appId and not AppId?

Hmm... I don't see nothing wrong with your code. Which version of Sequelize are you using?
Just a couple of non-related comments:
You don't need to import App, it is already in models object.
I think you don't need tableName parameter either.

Try to use the field property
Placement.belongsTo(models.App, {
foreignKey: {
name: 'AppId',
field: 'appId'
}
}
)

You need to have the AppId columns present in App model. Explicitly passing { foreignKey: 'appId'}) to belongsTo makes Sequelize use the key as it is.
So you need to create an App model which has -
const App = sequelize.define('App', {
AppId: DataTypes.INTEGER,
..
}, {
tableName: 'App'
});
Another options would be to change this
Placement.belongsTo(models.App, { foreignKey: 'appId'})
to
Placement.belongsTo(models.App)
This way Sequelize automatically handles all relations between the models.

Related

Why when I generate a model with sequelize has a different structure than others that includes a class instead of a const?

I'm new with sequelize and I've seen many tutorials on how to create models with sequelize, but when I did, this is the structure that created it.
'use strict';
const {
Model
} = require('sequelize');
module.exports = (sequelize, DataTypes) => {
class User extends Model {
/**
* Helper method for defining associations.
* This method is not a part of Sequelize lifecycle.
* The `models/index` file will call this method automatically.
*/
static associate(models) {
// define association here
}
};
Users.init({
nickname: DataTypes.STRING,
password: DataTypes.STRING
}, {
sequelize,
modelName: 'Users',
});
return User;
};
But the other structure that I see is "most popular" is this.
'use strict';
module.exports = (sequelize, DataTypes) => {
const User = sequelize.define('User', {
nickname: DataTypes.STRING,
password: DataTypes.STRING
},{
underscored: true
});
return User;
};
How can I generate a model with the second structure using "sequelize model:generate" in console?
Thanks for your time.
According to the official documentation both methods to define a model are equal to each other. define is more popular because it's an older approach then classes and that's all. I recommend to use a newer one because I suppose it will have a longer life cycle and support.

Sequelize doesn't read the new modelname after I do refactor (remove s in end modelname)

I've a backend app built in Express with Sequelize ORM.
Here is my code;
user.js (model)
'use strict';
module.exports = (sequelize, DataTypes) => {
const user = sequelize.define('user', {
username: DataTypes.STRING,
}, {
// tableName: 'user'
});
user.associate = function(models) {
// associations can be defined here
};
return user;
};
user.js (controller):
const User = require('../models').user;
module.exports = {
getUser: function (req, res) {
User.findAll().then(value => {
res.json(value);
})
}
}
When I start the project, it return error Unhandled rejection SequelizeDatabaseError: relation "users" does not exist. As you can see my code above, I've set the model as user not users, and the table in db also user not users. It's only work fine if I add the tableName: 'user' in the model file.
NOTE: By the default, when I do create model with sequelize, the file name and model define is users, but I refactor file name and define inside model into user
Why does this happen?
This is default behavior - Sequelize automatically transforms model name to plural. In order to disable that you should freeze table name in model definition (or just set table name explicitly like you are actually doing):
freezeTableName: true,

How to return the result set of sequelize query in camel case fashion?

I have defined a table schema in the database in an underscored fashion but I want to return the result set API response in camel case fashion. I know I can process the underscored object returned by sequelize and convert it into camelcase fashion. Is there any functionality to return the response of a query in camelcase fashion in sequelize itself?
To archieve this you need to use field when defining your model.
module.exports = (sequelize, DataTypes) => {
const yourTable = sequelize.define('yourTable', { // table name use it for Sequelize
camelCase: { //camelCase name that you'll use with sequelize.
field: 'under_score', //underscore name on yor database.
type: DataTypes.STRING
},
keyId: { //need to the same with association
field: 'key_id',
type: DataTypes.INTEGER
},
}, {
tableName: 'your_table', // then name of the table on the db
underscored: true,
});
yourTable.associate = (models) => {
yourTable.belongsTo(models.otherTable, {
as: 'Something',
foreignKey: 'key_id', //put attention here and keyId above.
onDelete: 'cascade'
});
}
}

Try to create HABTM connection Sequelize.js

I'm trying to create a HABTM relationship with Sequelize but I can't get it done.... I still receive an error message:
return (tableName1.toLowerCase() < tableName2.toLowerCase()) ? (tableName1
^
TypeError: Cannot call method 'toLowerCase' of undefined
I have a User model, a Book model and an UserBooks model. And ofcourse my database contains a "users" table, "user_books" table and "books" table.
UserBooks model:
module.exports = function(schema, DataTypes) {
var UserBooks = schema.define('UserBooks', {
}, {
tableName: 'user_books', // this will define the table's name
timestamps: false // this will deactivate the timestamp columns
});
UserBooks.sync();
return UserBooks;
};
User model:
module.exports = function(schema, DataTypes) {
var User = schema.define('User', {
keywords: DataTypes.STRING
}, {
tableName: 'users', // this will define the table's name
timestamps: false ,// this will deactivate the timestamp columns
syncOnAssociation:false
});
User.hasMany(Book, { foreignKey: 'user_id', through: UserBooks });
User.sync();
return User;
};
Book model:
module.exports = function(schema, DataTypes) {
var Book = schema.define('Book', {
keywords: DataTypes.STRING
}, {
tableName: 'books', // this will define the table's name
timestamps: false ,// this will deactivate the timestamp columns
syncOnAssociation:false
});
Book.hasMany(User, { foreignKey: 'book_id', through: UserBooks });
Book.sync();
return Book;
};
In your User model you are trying to create an association with a model that is not defined in that scope. In User.js, you only have access to User, not Book or UserBooks which are undefined. Thats whats causing your error.
You can either create associations in the place where you import all your models into your app, or in the models file by importing the models you want to associate with (bevare of circular imports). Your user model could be changed to:
module.exports = function(schema, DataTypes) {
var Book = schema.import(__dirname + '/book');
var UserBooks = schema.import(__dirname + '/userbooks');
var User = schema.define('User', {
keywords: DataTypes.STRING
}, {
tableName: 'users', // this will define the table's name
timestamps: false ,// this will deactivate the timestamp columns
syncOnAssociation:false
});
User.hasMany(Book, { foreignKey: 'user_id', through: UserBooks });
Book.hasMany(User, { foreignKey: 'book_id', through: UserBooks });
return User;
};
For another example of how to do it, see http://sequelizejs.com/articles/express#minimal-express-app
Also, I've removed the call to User.sync from your code. Sync is an async call, while import is sync. This means that your are defining your model, starting to sync it to the DB, and then returning it, before you know that it has finished syncing. This means you could potentially be trying to work create instances with it before the table has been created. Instead, you should use sequelize.sync to sync all your models at once, and attach a callback to wait for the sync to finish (see the link I posted for a code example)

Sequelize migrations

I am new to Sequelize and would like to know if anyone knows if there is a way to avoid the code duplication in Sequelize when using the migration functionality?
What I mean is that in the migration you would have something like this:
//migration-xyz.js
module.exports = {
up: function(migration, DataTypes, done) {
migration.createTable('Users',
{
name: DataTypes.STRING,
surname: DataTypes.STRING
});
},
down: function(migration, DataTypes, done) {
// logic for reverting the changes
}
}
But then the model would look something like this:
//user.js
module.exports = function(sequelize, DataTypes) {
var User = sequelize.define('User', {
name: DataTypes.STRING,
surname: DataTypes.STRING
});
return User;
};
Is there a way to get rid of the code duplication?
Thanks for the help! :)
As #WiredPrairie referenced in his/her comment, migrations and the attributes on a model are distinct things. Often we will create a model that has certain attributes and the associated migration to create the corresponding table. Later on more attributes will be added to the model and we will need to create a migration that adds columns for just the new attributes.
A way to remove the duplication would be to use sequelize.sync which creates your database from the model files. This has downsides though and should not be used on complex applications that are being deployed in different environments.
Maybe in the future the sequelize-cli will have a create:migration --model [modelfile] option where it will create the table for the corresponding model. This would have the same "duplication" of code but would make things a little faster.
When you initially ran the migrations via sequelize-cli
it set up the folder structure as:
-config
-config/config.json
-migrations
-models
-models/index.js
If you make sure your model file:
//user.js
module.exports = function(sequelize, DataTypes) {
var User = sequelize.define('User', {
name: DataTypes.STRING,
surname: DataTypes.STRING
});
return User;
};
is in the models directory, it will automatically be picked up when you
require('./models');
So, I've used the following code to import the new models for that revision of the migration:
//migrations-xyz.js
var Models = require('../models');
module.exports = {
up: function (queryInterface, Sequelize) {
/*
Add altering commands here.
Return a promise to correctly handle asynchronicity.
Example:
return queryInterface.createTable('users', { id: Sequelize.INTEGER});
*/
var tables = _.map(Models.sequelize.models, function(def){
var model = Models[def],
options = model.options,
tableName = model.tableName,
attributes = model.tableAttributes;
return queryInterface.createTable(tableName, attributes, options);
});
return Promise.all(tables);
},
down: function (queryInterface, Sequelize) {
/*
Add reverting commands here.
Return a promise to correctly handle asynchronicity.
Example:
return queryInterface.dropTable('users');
*/
return Models.sequelize.drop({logging: true});
}
};

Resources