Sails js - waterline orm - mysql. Tables autogeneration - mysql

When I create my model in sails - waterline the db is autogenerated.
The problem is that my primary keys are unsigned int(10) and the external keys are int(11) (with sign). In fact the relationship is only in my models and not in db.
A code example is the following:
// A user may only have a single pet
var User = Waterline.Collection.extend({
identity: 'user',
connection: 'local-postgresql',
attributes: {
firstName: 'string',
lastName: 'string',
// Add a reference to Pet
pet: {
model: 'pet'
}
}
});
var Pet = Waterline.Collection.extend({
identity: 'pet',
connection: 'local-postgresql',
attributes: {
breed: 'string',
type: 'string',
name: 'string',
// Add a reference to User
user: {
model: 'user'
}
}
});
In this example my database is generated in the following way:
pet "id" (in pet table) is an autoincrement primary key unsigned int(10)
and "pet" (in user table that is an external key pointing to pet id) is a int(11) (signed int). There is a solution to that problem? thanks

The sails-postgresql adapter does not yet create foreign key relationships in the database itself. This is not incorrect and doesn't affect how you use sails/waterline. Everything will work fine, this is just an optimization that hasn't been implemented yet.
There's an open issue if you'd like to share your thoughts there: https://github.com/balderdashy/sails-postgresql/issues/123

Related

How to make sequelize stop creating automatic tables (SEQUELIZE)

I found out that sequelize is creating tables automatically according to the definition of my model names.
I have the following code:
const DataTypes = require("sequelize");
const sequelize = require("../mysql.js");
const Approver = sequelize.define("approver", {
subordinate_id: {
type: DataTypes.INTEGER,
allowNull: false,
references: {
model: "user",
key: "id",
},
},
leader_id: {
type: DataTypes.INTEGER,
allowNull: false,
references: {
model: "user",
key: "id",
},
},
main_leader_id: {
type: DataTypes.INTEGER,
allowNull: false,
references: {
model: "user",
key: "id",
},
},
});
const connect = async () => {
await Approver.sync();
};
connect();
module.exports = Approver;
every time I run the local server, I get the following message in the terminal:
CREATE TABLE IF NOT EXISTS `approvers` (`id` INTEGER NOT NULL auto_increment , `subordinate_id` INTEGER NOT NULL, `leader_id` INTEGER NOT NULL, `main_leader_id` INTEGER NOT NULL, `createdAt` DATETIME NOT NULL, `updatedAt` DATETIME NOT NULL, PRIMARY KEY (`id`), FOREIGN KEY (`subordinate_id`) REFERENCES `user` (`id`), FOREIGN KEY (`leader_id`) REFERENCES `user` (`id`), FOREIGN KEY (`main_leader_id`) REFERENCES `user` (`id`)) ENGINE=InnoDB;
and I found out that the table creation is generated from the model's define because I put other names in the model and the table created was the same as the one I had named the code.
I don't know why the table that is created is in the plural "approvers" and in the model I put the name "approver" and apparently if I try to put another name the plural doesn't happen as well as the word "approver".
the big problem is that I have migrations and when I run them the table "approver" is created in my database, but when I run the command to start the local server, the sequelize creates one more table. So I end up with 2 tables in the database, "approver" of the migration and "approvers" of the model.
I already tried to put the migration and the model with the plural name "approver" but this causes an error when I try to use the model, the sequelize shows a missing field error when I try to create or update data, it says that the value "updatedAt" is missing, and this only happens because the automatically generated table creates this field, but the funniest thing is that the table was not created in my Dbeaver but the sequelize shows the error of being missing a field, even the model containing the plural name and the migration too...
I would like to get the result that the table is not created with the plural.
does anyone know how to solve this bug?
enter image description here
You have two problems here:
An auto-creation of a table according to a model definition
Pluralization of a table name while auto-creating it
Solutions:
Just remove sync call or the whole piece of the following code:
const connect = async () => {
await Approver.sync();
};
connect();
If you use migrations to create the whole structure and to make modifications to it then you don't need to use sync method of a model or a Sequelize instance.
Pluralization of table names can be turned off by indicating 'freezeTableName: true' in the model's options (see Enforcing table name to be equal to a model name in the official documentation).

Sequelize "null value in column of relation violates not-null constraint" on auto increment field after migrate from MySQL to Postgres

I am trying to migrate from MySQL to Postgres, I used pgLoader to move my data, reading operations seems to work fine but I am having this issue - When I am trying to create a new record using model.create() I am getting SequelizeDatabaseError: null value in column of relation violates not-null constraint
My sequelize looks like that:
const sequelize = new Sequelize(
process.env.DB_NAME,
process.env.DB_USER,
process.env.DB_PASS,
{
dialect: 'postgres',
host: process.env.DB_HOST,
port: process.env.DB_PORT,
schema: process.env.DB_PORT.DB_SCHEMA,
logging: false,
dialectOptions: {
...(process.env.DB_PORT === "5432" ? {} : addSSL())
}
}
);
const exampleModel = sequelize.define(
"exampleModel",
{
ExampleID: {
type: DataTypes.INTEGER,
autoIncrement: true,
primaryKey: true,
},
Name: {
type: DataTypes.STRING,
allowNull: false,
unique: true,
},
},
);
But when I do:
await model.create({
Name: "FooBaz",
});
I am getting that error.
It was working fine with MySQL but now I am facing this issue.
I am not sure what should I do in this case, because it explicitly say that it's null reseting the counter won't have effect.
Edit:
Running sequelize.sync({alter: true})
prints the following
CREATE TABLE IF NOT EXISTS "schema"."exampleModel" ("ExampleID" SERIAL , "Name" VARCHAR(255) NOT NULL UNIQUE, "createdAt" TIMESTAMP WITH TIME ZONE NOT NULL, "updatedAt" TIMESTAMP WITH TIME ZONE NOT NULL, PRIMARY KEY ("ExampleID"));
Followed by some alter tables lines.
But when I checked ExampleID type it's still integer (as it was on MySQL), the alter doesn't change it.
So do I need to convert it serial manually?
Solved!
The problem is that pgLoader doesn't copy autoincrement correctly, to fix it you need to set it manually on postgres using sequence, check here for instructions.
Changing primary key int type to serial
After fixing it, the problem resolved.
Thank you #a_horse_with_no_name

Foreign Key in Sequelize

I have three tables(models): Users,preferences,ideas. Users has a column 'username' as a primary key and I would like to add 'username' as foreign key to the other tables . How is it done in sequelize? I am a noob at sequelize ,so please do answer this. The hasMany,belongsTo were confusing to me. It would be really helpful if someone answers this.
For the two objects: User and Preference, you can specify the relationship as follows:
const User = sequelize.define('User', {
username: Sequelize.STRING,
});
const Preference = sequelize.define('Preference', {
id: Sequelize.INTEGER,
//Below, 'users' refer to the table name and 'username' is the primary key in the 'users' table
user: {
type: Sequelize.STRING,
references: {
model: 'users',
key: 'username',
}
}
});
User.hasMany(Preference); // Add one to many relationship
I would suggest to read the following document to understand better:
Sequelize Foreign Key

ORM: Sequelize: Add Not Equal Constraint for Two Columns

I have a following table. I don't want user to follow himself so I want to add a CHECK constraint.
For example, if this is inserted, I want database to throw an error.
{
userID: 1,
followingID: 1,
}
I can check with Javascript if userID and followingID are equal but I want database to check it.
My MYSQL version is 8.0.17 so I think it is possible to create this constraint with SQL. How can I add this constraint with Sequelize?
There is two solution
1. Using Model wise validation and adding check constraint in database manually:
Model wise validation:
const FollowingModel = sequelize.define("following", {
userId: {
type: Sequelize.INTEGER,
// .. other configuration like `allowNull`
},
followingId: {
type: Sequelize.INTEGER,
// .. other configuration like `allowNull`
}
}, {
validate: {
userShouldNotFollowSelf : function() {
if(this.userId === this.followingId) {
throw Error("User should not follow self") // Use any custom error class if your application has such class.
}
}
}
}
Beware this will allow you create entry in database which does not maintain this constraint.
It is just ORM's application layer check that, this application won't allow any entry where userId and followingId is not same.
Mysql database layer check constraint.
CREATE TABLE `following`
(
`userId` INT NOT NULL,
`followingId` INT NOT NULL,
CONSTRAINT `no_self_following` CHECK (`userId` <> `followingId`)
-- other properties and foreign key constraints.
);
It will ensure that, no such entry inserted where userId and followingId is same.
2. Declaring constraint in sequelize query interface.
This require to declare your model using query interfaces addConstraint as follows
sequelize.getQueryInterface().addConstraint("following", ['userId'], {
type: 'check',
name: "no_self_following"
where: {
userId: {
[Sequelize.Op.ne]: Sequelize.col("followingId")
}
}
});
Run this while all database model is been synced correctly. It will add database level constraint.
Which one to use ?
Approach #1 is more efficient. It is checking within the application without going into the database call, Makes your database less busy.

Sequelize.js foreign key

When using Sequelize.js, the following code doesn't add any foreign key on tables.
var MainDashboard = sequelize.define('main_dashboard', {
title: Sequelize.STRING
}, {
freezeTableName: true
})
MainClient.hasOne(MainDashboard, { foreignKey: 'idClient' })
MainDashboard.hasOne(MainClient, { foreignKey: 'clientId' })
sequelize.sync({ force: true })
Is there any way to force Sequelize.js to add these foreign key constraints?
Before I had the same problem, and solved when I understood the functioning of settings Sequelize.
Straight to the point!
Suppose we have two objects: Person and Father
var Person = sequelize.define('Person', {
name: Sequelize.STRING
});
var Father = sequelize.define('Father', {
age: Sequelize.STRING,
//The magic start here
personId: {
type: Sequelize.INTEGER,
references: 'persons', // <<< Note, its table's name, not object name
referencesKey: 'id' // <<< Note, its a column name
}
});
Person.hasMany(Father); // Set one to many relationship
Maybe it helps you
Edit:
You can read this to understand better:
http://docs.sequelizejs.com/manual/tutorial/associations.html#foreign-keys
For Sequelize 4 this has been updated to the following:
const Father = sequelize.define('Father', {
name: Sequelize.STRING
});
const Child = sequelize.define('Child', {
age: Sequelize.STRING,
fatherId: {
type: Sequelize.INTEGER,
references: {
model: 'fathers', // 'fathers' refers to table name
key: 'id', // 'id' refers to column name in fathers table
}
}
});
Father.hasMany(Child); // Set one to many relationship
Edit:
You can read more on associations at https://sequelize.org/master/manual/assocs.html
You need to add foreignKeyConstraint: true
Try:
MainClient.hasOne(MainDashboard, { foreignKey: 'idClient', foreignKeyConstraint: true })
I just tried to run your code, and the rows seem to be created fine:
CREATE TABLE IF NOT EXISTS `main_dashboard` (`title` VARCHAR(255), `id` INTEGER NOT NULL auto_increment , `idClient` INTEGER, PRIMARY KEY (`id`)) ENGINE=InnoDB;
CREATE TABLE IF NOT EXISTS `main_client` (`id` INTEGER NOT NULL auto_increment, `clientId` INTEGER, PRIMARY KEY (`id`)) ENGINE=InnoDB;
clientId is added to main_client, and idClient is added to main_dashboard
It seems you have slightly confused what the hasOne method does. Each time you call hasOne an association is created, so your code effectively associates the two tables twice. The method you are looking for is belongsTo
If you want each client to have one dashboard, the code would be the following:
MainClient.hasOne(MainDashboard, { foreignKey: 'clientId' })
MainDashboard.belongsTo(MainClient, { foreignKey: 'clientId' })
This creates a clientId field on the main_dashboard table, which relates to the id field of the main_client table
In short belongsTo adds the relation to the table that you are calling the method on, hasOne adds it on the table that is given as argument.
It's amazingly simple.
const MainDashboard = this.sequelize.define('main_dashboard', {/* attributes */}),
MainClient = this.sequelize.define('main_client', {/* attributes */});
MainDashboard.belongsTo(MainClient, { foreignKey: 'clientId' }); // Adds clientId to MainDashboard
It will link this as a foreign key and you may use it as an association. Let me know if I'm missing anything.