Sequelize allow null set to false on foreign key - javascript

Here is the code below for one of my tables
const { Model, DataTypes } = require('sequelize');
const Algorithm = require('./algorithm');
const Exchange = require('./exchange');
const User = require('./user');
//#JA - This model defines the api keys for each user's exchange
//#JA - For security reasons in case the database gets hacked the keys will be stored using encryption.
module.exports = function(sequelize){
class AlgorithmRule extends Model {}
AlgorithmModel = Algorithm(sequelize);//#JA - Gets a initialized version of Algorithm class
ExchangeModel = Exchange(sequelize);//#JA - Gets initialized version of the Exchange class
UserModel = User(sequelize);//#JA - Gets a initialized version of User class
var AlgorithmRuleFrame = AlgorithmRule.init({
algorithm_id: {
type: DataTypes.INTEGER,
allowNull: false,
references: {
model: AlgorithmModel,
key: 'id',
}
},
exchange_id: {
type: DataTypes.STRING,
references: {
model: ExchangeModel,
key: 'name',
},
},
user_id: {
type: DataTypes.INTEGER,
references: {
model: UserModel,
key: 'id',
},
},
type : { //Partial-Canceled implies that the order was partially filled and then canceled.
type: DataTypes.ENUM('Percent Of Equity','Cash'),
allowNull: false,
defaultValue: 'Percent Of Equity'
},
type_value: { //#JA - This will be either treated as a percentage or 'cash' value for the type chosen for the algorithm.
type: DataTypes.DECIMAL(20,18),
allowNull: false
},
}, {
sequelize,
modelName: 'AlgorithmRule',
indexes: [{ unique: true, fields: ['algorithm_id','exchange_id','user_id'] }]
});
return AlgorithmRuleFrame
};
I'm trying to set this up so that I can allownull:false on algorithm_id and exchange_id and user_id. I want it so there HAS to be values there for any records to be allowed.
I can't even get allowNull:false manually through the database itself. So my first question is, is this even possible?
If it is, how do I do it with sequelize?
I can use the typical hasOne() with foreign key commands because then I can't create a composite unique of the foreign keys. The only way I was able to do this was the way I did using the references: json structure.
How do I allownull:false for a foreignKey reference defined the way I have it?
To be clear something like this will NOT work
Task.belongsTo(User, { foreignKey: { allowNull: false }, onDelete: 'CASCADE' })
This will NOT work because I'm using a composite unique key across 3 foreign keys and in order to do that I need reference to it's name and that is not possible unless it's defined on the table before these commands above our input. Hopefully this makes sense.
Any help is greatly appreciated!

Okay so apparently
algorithm_id: {
type: DataTypes.INTEGER,
allowNull: false,
references: {
model: AlgorithmModel,
key: 'id',
}
},
This code is correct. HOWEVER, if you created the database already and the foreign key was already defined it will NOT change the allowNull via the alter command. You have to COMPLETELY drop the table and THEN it will allow the allowNull:false attribute to work.
This threw me for a loop for a long time, so I help this saves someone else a lot of frustration.

Related

How to use Sequelize associations with .then in controller

This is my ToWatch Model in toWatch-model.js file which in code has UserModel->ToWatch 1:1 relationship and has ToWatch->MovieModel 1:M relationship.
const Sequelize = require('sequelize');
const sequelize = require('./../common/db-config');
const MovieModel = require ("./movie-model");
const UserModel = require("./user-model");
const Model = Sequelize.Model;
class ToWatch extends Model{}
ToWatch.init({
id: {
type: Sequelize.INTEGER,
allowNull: false,
primaryKey: true,
autoIncrement: true,
field: 'id_towatch'
},
date: {
type: Sequelize.DATE,
allowNull: false,
field: 'date'
},
userId: {
type: Sequelize.INTEGER,
allowNull: false,
field: 'id_user',
references:{
model: UserModel,
key: "id"
}
},
movieId: {
type: Sequelize.INTEGER,
allowNull: false,
field: 'movie_id_towatch',
references:{
model: MovieModel,
key: "id"
}
},
},
{
sequelize,
modelName: 'towatch',
tableName: 'towatch',
timestamps: false
// options
});
//Here is the relation ************
UserModel.hasOne(ToWatch, {
foreignKey: {
type:Sequelize.DataTypes.INTEGER,
allowNull:false,
name:'fk_foreign_key_towatch'
}
});
ToWatch.belongsTo(UserModel);
ToWatch.hasMany(MovieModel, {
foreignKey: {
type:Sequelize.DataTypes.INTEGER,
allowNull:false,
name:'movie_id_towatch'
}
});
MovieModel.belongsTo(ToWatch);
module.exports = ToWatch;
I watched many tutorials, but being my first time trying to make a method that will return everything including something from my other table via ID, I wasn't sure where to put and how to put data that I need in this method, considering it has .then(data=>res.send). Tutorials were doing it other ways by fetching or using async-await, and even documentation didn't help me here. Can somebody tell me what to put and where inside this method, that is inside toWatch-controller.js file for me being able to see let's say all the movie data (title,img,date) ,as an array I think, of the getToWatch method.
const ToWatch = require('./../models/toWatch-model');
module.exports.getToWatch = (req,res) => {
ToWatch.findAll().then(toWatch => {
[WHAT DO I PUT HERE?]
res.send(toWatch);
}).catch(err => {
res.send({
status: -1,
error:err
})
})
}
I need something like this ToWatch{
color:red,
cinema:"MoviePlace",
movieId{title:"Anabel", img:"URL", date:1999.02.23}
As I understood your question right, what you trying to do is return toWatch model instances with including User and Movie models.
To do so you can pass options in findAll() method like below:
ToWatch.findAll({include: [{model: User}, {model: Movie}]})... //and rest of your code
Or alternatively to keep your code clean you can use scopes:
// below the toWatch-model.js file
ToWatch.addScope('userIncluded', {include: {model: User}})
ToWatch.addScope('movieIncluded', {include: {model: Movie}})
And in the getToWatch method:
// you can pass several scopes inside scopes method, its usefull when you want to combine query options
ToWatch.scopes('userIncluded', 'movieIncluded').findAll()... //rest of your code
For more information check out sequelize docs

Sequelize expecting Integer despite of making the type for the field as Float

I have a model as below:
const Sequelize = require("sequelize");
const sequelize = new Sequelize({
database: process.env.PGDATABASE,
username: process.env.PGUSER,
password: process.env.PGPASS,
host: process.env.PGHOST,
port: process.env.PGPORT,
dialect: "postgres"
});
var MessageRating = sequelize.define(
"MessageRating",
{
chatId: {
type: Sequelize.STRING,
primaryKey: true,
allowNull: false
},
ratingType: {
type: Sequelize.ENUM,
values: ["NEGATIVE", "POSITIVE", "NEUTRAL", "MIXED"],
allowNull: false
},
rating: {
type: Sequelize.FLOAT,
allowNull: false
}
},
{
indexes: [
{
unique: false,
fields: ["ratingType"]
}
]
}
);
module.exports = MessageRating;
I'm trying to create a table using this model using the following code :
var messageRatingStored = sequelize
.sync()
.then(() =>
MessageRating.create({
chatId: chatId,
ratingType: amazonComprehendResult.ResultList[0].Sentiment,
rating: roundOffRating
})
)
.then(messageRating => {
console.log("MessageRating json", messageRating.toJSON());
});
Whenever I run the code it throws error: "Unhandled rejection SequelizeDatabaseError: invalid input syntax for integer: "0.82""
https://gist.github.com/BikalNepal/51fd28f877b422fcc16825d61cd95847
I tried using a static integer value (82, 10 etc) instead of "roundOffRating" (which gives decimal) and it stores perfectly without errors. Since I'm not using Integer for the type of rating in the model, why is it still expecting integer?
You updated your Sequelize model, but have you updated the database table itself ?
Using sequelize.sync() only creates missing tables. It does not update existing ones. Using sequelize.sync({force: true}) will drop all tables and recreate them, meaning your field will be effectively a float, but you will lose all data present in the database.
You can update your field in the database, without losing data, with the following alter statement:
ALTER TABLE MessageRating ALTER COLUMN rating TYPE float;
Read more about ALTER TABLE.
A useful tip: Sequelize propagates errors from the database as SequelizeDatabaseError errors. When you see this type of error coming from your code, you can be sure the error comes from the database and not sequelize itself.

Sequelize update with association not updating table for the associated model

I have two tables below:
1. Poll
2. Option
The Poll model is as follows:
const Sequelize = require('sequelize');
let dbService = require('../services/dbService.js');
let Option = require('./options');
let Poll = dbService.define('Poll', {
poll: {
type: Sequelize.STRING,
allowNull: false
},
isAnonymous: {
type: Sequelize.STRING,
allowNull: false
},
startDate: {
type: Sequelize.STRING,
},
endDate: {
type: Sequelize.STRING,
},
active: {
type: Sequelize.BOOLEAN,
}
});
Poll.hasMany(Option);
module.exports = Poll;
The Option model is as follows:
const Sequelize = require('sequelize');
let dbService = require('../services/dbService.js');
let Option = dbService.define("option", {
name: Sequelize.STRING
});
module.exports = Option;
This is my /GET route for api/poll/:id
I want to update the poll. All the other routes are working fine. I am just stuck on updating the model.
The controller for /PUT api/poll/:id
The service for /PUT api/poll/:id
This only updates the Poll table, the Option table's column does not get updated. How can I achieve the update with the one to many association like this?
I have tried this too! Sequelize update with association
I have even done the same as the sequelize docs. But I am unable to achieve what I want. I've been stuck on this for a while. Any kinda help will be appreciated.
Thanks!
For update you can try SequelizeModel.upsert() function.

How do I override a sequelize defaultValue function for testing purposes?

I'm trying to add unit/integration testing to my sequelize project, and I'm running into a problem when moving from postgres dialect to sqlite. I am attempting to override the 'defaultValue' function on 'id'. This results in the syntax for CREATE TABLE being correct, but the original defaultValue is used in the INSERT statement generated by .create().
I have created a minimal sample project that illustrates the problem I'm describing with a failing test.
Here's the relevant code snippet:
User = sequelize.define('User', {
id: {
type: Sequelize.UUID,
field: 'id',
defaultValue: sequelize.literal("uuid_generate_v1mc()"),
primaryKey: true
},
name: {
type: Sequelize.STRING,
field: "first_name"
},
});
// why doesn't setting these attributes override the default value
// provided by User.create?
User.attributes.id.defaultValue='b9c96442-2c0d-11e6-b67b-9e71128cae77';
User.tableAttributes.id.defaultValue='b9c96442-2c0d-11e6-b67b-9e71128cae77';
What's the best way to inject or mock the defaultValue function?
The answer would miss the "why" part, but here is how I made it work:
before(function() {
// Define a table that uses a custom default value
User = sequelize.define('User', {
id: {
type: Sequelize.UUID,
field: 'id',
defaultValue: sequelize.literal("uuid_generate_v1mc()"),
primaryKey: true
},
name: {
type: Sequelize.STRING,
field: "first_name"
},
});
User.attributes.id.defaultValue.val = '"b9c96442-2c0d-11e6-b67b-9e71128cae77"';
return sequelize.sync();
});
Though, I suspect there should be an easier way to achieve the same result.
What is interesting is that if the defaultValue would not be literal or fn and be, say, a string value instead, things would be much easier - we could've just added a hook:
User.beforeCreate(function (user) {
user.dataValues.id = 'b9c96442-2c0d-11e6-b67b-9e71128cae77';
});
I suggest you to seek the answers for the "why" question at the sequelize issue tracker.

Update embedded document mongoose

I'm looking for an easy way of updating an embedded document using mongoose without having to set each specific field manually. Looking at the accepted answer to this question, once you find the embedded document that you want to update you have to actually set each respective property and then save the parent. What I would prefer to do is pass in an update object and let MongoDB set the updates.
e.g. if I was updating a regular (non embedded) document I would do this:
models.User.findOneAndUpdate({_id: req.params.userId}, req.body.user, function(err, user) {
err ? resp.status(500).send(err) : user ? resp.send(user) : resp.status(404).send();
});
Here I don't actually have to go through each property in req.body.user and set the changes. I can't find a way of doing this kind of thing with sub documents as well ?
My Schema is as follows:
var UserSchema = BaseUserSchema.extend({
isActivated: { type: Boolean, required: true },
files: [FileSchema]
});
var FileSchema = new mongoose.Schema(
name: { type: String, required: true },
size: { type: Number, required: true },
type: { type: String, required: true },
});
And I'm trying to update a file based on user and file id.
Do I need to create a helper function to set the values, or is there a MongoDB way of doing this ?
Many thanks.
Well presuming that you have something that has you "filedata" in a variable, and of course the user _id that you are updating, then you wan't the $set operator:
var user = { /* The user information, at least the _id */
var filedata = { /* From somewhere with _id, name, size, type */ };
models.User.findOneAndUpdate(
{ "_id": user._id, "files._id": filedata._id },
{
"$set": {
"name": filedata.name,
"size": filedata.size,
"type": filedata.type
}
},
function(err,user) {
// Whatever in here such a message, but the update is already done.
}
);
Or really, just only $set the fields that you actually mean to "update" as long as you know which ones you mean. So if you only need to change the "size" then just set that for example.

Categories

Resources