Why doesn't sequelize-cli include the ID in the model file? - node.js

When I run the following command:
sequelize-cli model:create --name User --attributes "dispName:string,email:string,phoneNum1:string"
I end up with the following migration file:
'use strict';
module.exports = {
up: (queryInterface, Sequelize) => {
return queryInterface.createTable('Users', {
id: {
allowNull: false,
autoIncrement: true,
primaryKey: true,
type: Sequelize.INTEGER
},
dispName: {
type: Sequelize.STRING
},
email: {
type: Sequelize.STRING
},
phoneNum1: {
type: Sequelize.STRING
},
createdAt: {
allowNull: false,
type: Sequelize.DATE
},
updatedAt: {
allowNull: false,
type: Sequelize.DATE
}
});
},
down: (queryInterface, Sequelize) => {
return queryInterface.dropTable('Users');
}
};
and the following model file:
'use strict';
module.exports = (sequelize, DataTypes) => {
const User = sequelize.define('User', {
dispName: DataTypes.STRING,
email: DataTypes.STRING,
phoneNum1: DataTypes.STRING
}, {});
User.associate = function(models) {
// associations can be defined here
};
return User;
};
Questions:
Why doesn't the model file contain the definition for id?
If I change the name and/or definition of the primary key field (the key will be generated externally and will be set on an object before it is saved), then do I have to include the definition of the ID field in the model file? Why/why not?
Versions:
[Node: 12.14.1, CLI: 5.5.1, ORM: 5.21.3]
The following do not answer my question:
Should Sequelize migrations update model files?

If you don't declare PK in your model sequelize assumes you have the id PK. This is by design. And yes you can rename your PK in the model. Just don't forget to setup PK in the mode properly according to a real PK in your DB.

Related

sequelize-cli db:migrate doesn't generate association table

I have a User model (with associated migration file created by sequelize-cli):
'use strict';
module.exports = (sequelize, DataTypes) => {
const User = sequelize.define('User', {
dispName: DataTypes.STRING,
email: DataTypes.STRING,
phoneNum1: DataTypes.STRING
}, {});
User.associate = function (models) { // associations added manually
User.belongsToMany(models.Role, { through: 'UserRoles', foreignKey: 'userId' });
};
return User;
};
here's the generated migration file:
'use strict';
module.exports = {
up: (queryInterface, Sequelize) => {
return queryInterface.createTable('Users', {
cognitoId: { // modified: was an auto-incrementing integer
allowNull: false,
primaryKey: true,
type: Sequelize.STRING(100)
},
dispName: {
type: Sequelize.STRING(100) // modified: was just plain STRING
},
email: {
type: Sequelize.STRING(100) // modified: was just plain STRING
},
phoneNum1: {
type: Sequelize.STRING(15) // modified: was just plain STRING
},
createdAt: {
allowNull: false,
type: Sequelize.DATE
},
updatedAt: {
allowNull: false,
type: Sequelize.DATE
}
});
},
down: (queryInterface, Sequelize) => {
return queryInterface.dropTable('Users');
}
};
and a Role model (with associated migration file created by sequelize-cli):
'use strict';
module.exports = (sequelize, DataTypes) => {
const Role = sequelize.define('Role', {
name: DataTypes.STRING
}, {});
Role.associate = function (models) { // associations added manually
Role.belongsToMany(models.User, { through: 'UserRoles', foreignKey: 'roleId' });
};
return Role;
};
here's the generated migration file for Role:
'use strict';
module.exports = {
up: (queryInterface, Sequelize) => {
return queryInterface.createTable('Roles', {
id: {
allowNull: false,
autoIncrement: true,
primaryKey: true,
type: Sequelize.INTEGER
},
name: {
type: Sequelize.STRING(100), // modified: was just plain STRING
unique: true // Added manually
},
createdAt: {
allowNull: false,
type: Sequelize.DATE
},
updatedAt: {
allowNull: false,
type: Sequelize.DATE
}
});
},
down: (queryInterface, Sequelize) => {
return queryInterface.dropTable('Roles');
}
};
When I run sequelize-cli db:migrate, the table UserRoles is not created. Why is that?
The Associations page of the Sequelize manual seems to suggest that defining the associations in the model file is all that is required...
Research:
I seem to have done what is suggested in this answer to the question Sequelize not creating model association columns but doesn't seem to be working for me (the answer isn't accepted either).
Not quite what I need: Querying association tables in Sequelize
Nor this: Sequelize how to use association table?

Problem setting up Sequelize association - query with 'include' is failing

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
}]
});

Creating associations in Sequelize migration

Nodejs. Sequelize 4.41. Try to make 2 models with relation many-to-many through another table. Running with sequelize-cli, for example...
sequelize model:generate --name Camera --attributes name:string,sn:string
Here is models
// Camera model
'use strict';
module.exports = (sequelize, DataTypes) => {
const Сamera = sequelize.define('Сamera', {
name: DataTypes.STRING,
sn: DataTypes.STRING
}, {});
Сamera.associate = function(models) {
// associations can be defined here
Camera.belongsToMany(models.Relay, {through: 'CameraRelay'});
};
return Сamera;
};
And
// Relay model
'use strict';
module.exports = (sequelize, DataTypes) => {
const Relay = sequelize.define('Relay', {
name: DataTypes.STRING,
sn: DataTypes.STRING,
channel: DataTypes.INTEGER
}, {});
Relay.associate = function(models) {
// associations can be defined here
Relay.belongsToMany(models.Camera, {through: 'CameraRelay'});
};
return Relay;
};
In documentation there are phrase
Belongs-To-Many associations are used to connect sources with multiple targets. Furthermore the targets can also have connections to multiple sources.
Project.belongsToMany(User, {through: 'UserProject'});
User.belongsToMany(Project, {through: 'UserProject'});
This will
create a new model called UserProject with the equivalent foreign keys
projectId and userId.
Migrations is
// create-relay
'use strict';
module.exports = {
up: (queryInterface, Sequelize) => {
return queryInterface.createTable('Relays', {
id: {
allowNull: false,
autoIncrement: true,
primaryKey: true,
type: Sequelize.INTEGER
},
name: {
type: Sequelize.STRING
},
sn: {
type: Sequelize.STRING
},
channel: {
type: Sequelize.INTEGER
},
createdAt: {
allowNull: false,
type: Sequelize.DATE
},
updatedAt: {
allowNull: false,
type: Sequelize.DATE
}
});
},
down: (queryInterface, Sequelize) => {
return queryInterface.dropTable('Relays');
}
};
And
//create camera
'use strict';
module.exports = {
up: (queryInterface, Sequelize) => {
return queryInterface.createTable('Сameras', {
id: {
allowNull: false,
autoIncrement: true,
primaryKey: true,
type: Sequelize.INTEGER
},
name: {
type: Sequelize.STRING
},
sn: {
type: Sequelize.STRING
},
createdAt: {
allowNull: false,
type: Sequelize.DATE
},
updatedAt: {
allowNull: false,
type: Sequelize.DATE
}
});
},
down: (queryInterface, Sequelize) => {
return queryInterface.dropTable('Сameras');
}
};
Why it doesn't create model CameraRelay and doesn't create migration for same model when I running migrate?
I guess the misunderstanding is about sync vs migration: great part of documentation you are referring to, is using the sync method to create all tables and associations starting from models.
When you are using migrations, you are creating db all of your table/columns/associations using migration files (and in my hopinion, this is a better way for something that is going to production).
To understand the difference, just look at your camera model vs your camera migration file:
the model has only name and sn properties defined
the migration file has of course name and sn, but it has id, createdAt and updatedAt too.
Migrations are file with the aim of change your db in a safe way, allowing you to rollback to any point in the past.
So, back to your problem, you have to:
create a new migration file to create your new CameraRelay table, with foreign keys to both Camera and Relay tables
update your current Camera migration file with one-to-many relation to CameraRelay table
update your current Relay migration file with one-to-many relation to CameraRelay table
CameraRelay migration example:
'use strict';
module.exports = {
up: (queryInterface, Sequelize) => {
return queryInterface.createTable('CameraRelays', {
cameraId: {
primaryKey: true,
type: Sequelize.INTEGER,
allowNull: false,
references: {
model: 'Relay',
key: 'id'
}
},
relayId: {
primaryKey: true,
type: Sequelize.INTEGER,
allowNull: false,
references: {
model: 'Camera',
key: 'id'
}
},
createdAt: {
allowNull: false,
type: Sequelize.DATE
},
updatedAt: {
allowNull: false,
type: Sequelize.DATE
}
});
},
down: (queryInterface, Sequelize) => {
return queryInterface.dropTable('CameraRelays');
}
};

Node.js Sequelize UUID primary key + Postgres

I try to create database model by using sequelize but I'm facing a problem with model's primary key.
Setting
I'm using Postgres (v10) in docker container and sequalize (Node.js v10.1.0
) for models and GraphQL (0.13.2) + GraphQL-Sequalize (8.1.0) for request processing.
Problem
After creating models by sequelize-cli I've manually tried to replace id column with uuid. Here's my model migration that I'm using.
'use strict';
const DataTypes = require('sequelize').DataTypes;
module.exports = {
up: (queryInterface, Sequelize) => {
return queryInterface.createTable('Currencies', {
uuid: {
primaryKey: true,
type: Sequelize.UUID,
defaultValue: DataTypes.UUIDV4,
allowNull: false
},
name: {
type: Sequelize.STRING
},
ticker: {
type: Sequelize.STRING
},
alt_tickers: {
type: Sequelize.ARRAY(Sequelize.STRING)
},
createdAt: {
allowNull: false,
type: Sequelize.DATE
},
updatedAt: {
allowNull: false,
type: Sequelize.DATE
}
});
},
down: (queryInterface, Sequelize) => {
return queryInterface.dropTable('Currencies');
}
};
Model:
'use strict';
module.exports = (sequelize, DataTypes) => {
const Currency = sequelize.define('Currency', {
uuid: DataTypes.UUID,
name: DataTypes.STRING,
ticker: DataTypes.STRING,
alt_tickers: DataTypes.ARRAY(DataTypes.STRING)
}, {});
Currency.associate = function(models) {
// associations can be defined here
};
return Currency;
};
Due to some problem sequalize executes next expression:
Executing (default): SELECT "id", "uuid", "name", "ticker", "alt_tickers", "createdAt", "updatedAt" FROM "Currencies" AS "Currency" ORDER BY "Currency"."id" ASC;
That leads to "column 'id' doesn't exist" error.
Alternatively, I've tried to fix it by renaming uuid column to id at migration:
...
id: {
allowNull: false,
primaryKey: true,
type: Sequelize.UUID,
defaultValue: Sequelize.UUIDV4()
},
...
And at the model:
'use strict';
module.exports = (sequelize, DataTypes) => {
const Currency = sequelize.define('Currency', {
id: DataTypes.INTEGER,
name: DataTypes.STRING,
ticker: DataTypes.STRING,
alt_tickers: DataTypes.ARRAY(DataTypes.STRING)
}, {});
Currency.associate = function(models) {
// associations can be defined here
};
return Currency;
};
but the result was the following error at the start of the program:
Error: A column called 'id' was added to the attributes of 'Currencies' but not marked with 'primaryKey: true'
Questions
So, is there a way to force sequelize to use UUID as the tables primary key without defining id column?
Is there a way to create columns without id columns?
What possibly caused this errors and how should fix it?
Thanks in advance!
What you have not posted here is your model code. This is what I think has happened
The database has been manually changed from id to uuid.
Your model does not reflect this change.
Hence the query is searching for both id and uuid.
You can fix this my defining uuid in your model like below and making it a primary key
const User = sequelize.define('user', {
uuid: {
type: Sequelize.UUID,
defaultValue: Sequelize.UUIDV1,
primaryKey: true
},
username: Sequelize.STRING,
});
sequelize.sync({ force: true })
.then(() => User.create({
username: 'test123'
}).then((user) => {
console.log(user);
}));
This is just about the only resource I've found online that explains what it takes to set up a UUID column that the database provides defaults for, without relying on the third-party uuid npm package: https://krmannix.com/2017/05/23/postgres-autogenerated-uuids-with-sequelize/
Short version:
You'll need to install the "uuid-ossp" postgres extension, using a sqlz migration
When defining the table, use this defaultValue: Sequelize.literal( 'uuid_generate_v4()' )

Sequelize in Node/Express - 'no such table: main.User` error

I'm trying to build a simple Node/Express app with Sequelize, but when I try to create a new record in my relational database, I am getting the error Unhandled rejection SequelizeDatabaseError: SQLITE_ERROR: no such table: main.User. Basically, I create a user in the Users table and then try to create a related address in the Addresses table - the user is successfully created but it fails with this error when creating the address... where is it getting the main prefix from in the table name? (full error readout below)...
First off, here's a rundown of my program...
My Sequelize version is Sequelize [Node: 6.8.1, CLI: 2.4.0, ORM: 3.29.0], and I used the Sequelize CLI command sequelize init to set up this portion of my project.
I am using SQLite3 for local development, and in config/config.json I have the development db defined as
"development": {
"storage": "dev.sqlite",
"dialect": "sqlite"
}
My user migration:
'use strict';
module.exports = {
up: function(queryInterface, Sequelize) {
return queryInterface.createTable('Users', {
id: {
allowNull: false,
autoIncrement: true,
primaryKey: true,
type: Sequelize.INTEGER
},
first_name: {
type: Sequelize.STRING
},
last_name: {
type: Sequelize.STRING
},
createdAt: {
allowNull: false,
type: Sequelize.DATE
},
updatedAt: {
allowNull: false,
type: Sequelize.DATE
}
});
},
down: function(queryInterface, Sequelize) {
return queryInterface.dropTable('Users');
}
};
and the address migration (abbreviated):
module.exports = {
up: function(queryInterface, Sequelize) {
return queryInterface.createTable('Addresses', {
id: {
allowNull: false,
autoIncrement: true,
primaryKey: true,
type: Sequelize.INTEGER
},
address_line_one: {
type: Sequelize.STRING
},
UserId: {
type: Sequelize.INTEGER,
allowNull: false,
references: {
model: "User",
key: "id"
}
}
})
}
The user model:
'use strict';
module.exports = function(sequelize, DataTypes) {
var User = sequelize.define('User', {
first_name: DataTypes.STRING,
last_name: DataTypes.STRING
}, {
classMethods: {
associate: function(models) {
models.User.hasOne(models.Address);
}
}
});
return User;
};
and the address model:
'use strict';
module.exports = function(sequelize, DataTypes) {
var Address = sequelize.define('Address', {
address_line_one: DataTypes.STRING,
UserId: DataTypes.INTEGER
}, {
classMethods: {
associate: function(models) {
models.Address.hasOne(models.Geometry);
models.Address.belongsTo(models.User, {
onDelete: "CASCADE",
foreignKey: {
allowNull: false
}
});
}
}
});
return Address;
};
finally, my route index.js:
router.post('/createUser', function(req, res){
var firstName = req.body.first_name;
var lastName = req.body.last_name;
var addressLineOne = req.body.address_line_one;
models.User.create({
'first_name': newUser.firstName,
'last_name': newUser.lastName
}).then(function(user){
return user.createAddress({
'address_line_one': newUser.addressLineOne
})
})
So when I try to post to /createUser, the User will successfully be created and the console will say that a new Address has been created (INSERT INTO 'Addresses'...), but the address is NOT created and the following error is logged:
Unhandled rejection SequelizeDatabaseError: SQLITE_ERROR: no such table: main.User
at Query.formatError (/Users/darrenklein/Desktop/Darren/NYCDA/WDI/projects/world_table/wt_test_app_1/node_modules/sequelize/lib/dialects/sqlite/query.js:348:14)
at afterExecute (/Users/darrenklein/Desktop/Darren/NYCDA/WDI/projects/world_table/wt_test_app_1/node_modules/sequelize/lib/dialects/sqlite/query.js:112:29)
at Statement.errBack (/Users/darrenklein/Desktop/Darren/NYCDA/WDI/projects/world_table/wt_test_app_1/node_modules/sqlite3/lib/sqlite3.js:16:21)
I've done this sort of thing with Sequelize once before a few months ago and it was successful, I cannot for the life of me figure out what I'm missing here. Why is the app looking for main.User, and how can I get it to look for the correct table? Thank you!
Aha! A small error has derailed my entire operation. In the migration files, references.model must be pluralized!
references: {
model: "Users",
key: "id"
}
Boom.

Resources