How to define foreign key constraint in postgres using sequelize ORM? - node.js

I am trying to create one to many foreign key constraint between two tables. A client can have many environments.
Here is my snippet in the model.
Client.associate = function(models) {
Client.hasMany(models.Enviornment, {as: 'enviornments', foreignKey: 'clientId'})
};

The solution is here.
The client has many environments. (one to many association )
Here is a code snippet for a model client.
module.exports = (sequelize, DataTypes) => {
let client = sequelize.define ('client', {
id: { type: DataTypes.INTEGER, primaryKey: true, autoIncrement: true, field: 'id' },
...
}, {
associate: models => {
client.hasMany (models.enviornment, {
foreignKey: { name: 'client_id', allowNull: false }
});
},
});
return client;
};
Here is a code snippet for a model enviornment.
module.exports = (sequelize, DataTypes) => {
let enviornment = sequelize.define ('enviornment', {
id: { type: DataTypes.INTEGER, primaryKey: true, autoIncrement: true, field: 'id' },
...
}, {
associate: models => {
enviornment.belongsTo (models.client, {
foreignKey: { name: 'client_id', allowNull: false }
});
},
});
return enviornment;
};

Related

Sequelize model field that I did not add

I have a user, role and their relation model, when I want to insert into the relation model I get this error:
error: column "userUserId" of relation "roles_users_relationships" does not exist.
Can you help with this error?
(sorry if I wrote something wrong, this is my first question on )
This is how my model looks
Role model:
const Schema = (sequelize, DataTypes) => {
const table = sequelize.define(
"roles", {
role_id: {
type: DataTypes.UUID,
defaultValue: sequelize.literal("uuid_generate_v4()"),
primaryKey: true,
},
name: {
type: DataTypes.STRING,
allowNull: false,
}
}, {
timestamps: false
}
);
table.associate = function (models) {
table.belongsToMany(models.users, {
through: "roles_users_relationship",
foreignKey: "role_id",
});
};
return table;
};
Users model:
const Schema = (sequelize, DataTypes) => {
const table = sequelize.define(
"users", {
user_id: {
type: DataTypes.UUID,
defaultValue: sequelize.literal("uuid_generate_v4()"),
primaryKey: true,
},
name: {
type: DataTypes.STRING,
allowNull: true,
}
}, {
timestamps: false
}
);
table.associate = function (models) {
table.belongsTo(models.roles, {
through: "roles_users_relationship",
foreignKey: "user_id",
});
};
return table;
};
Roles Users relationship model:
const Schema = (sequelize, DataTypes) => {
const table = sequelize.define(
"roles_users_relationship", {
user_id: {
type: DataTypes.UUID,
allowNull: false,
},
role_id: {
type: DataTypes.UUID,
allowNull: false,
},
}, {
timestamps: false
}
);
return table;
};
In your through table you should add options in related table field:
references: {
model: User,
key: 'user_id'
}
Otherwise sequelize will do it automatically, like adding foreign key column in this way tableNamePrimaryKeyColumn in your case its 'userUserId'

Node Sequelize Relational Challenge

I have three tables (all associated model classnames use PascalCase)
schools school_codes course
------ ------ ------
id (pk) code (pk) name
name school_id (fk) school_code (fk)
I'm trying to define sequelize relations, so that this Course lookup returns the associated School:
const courseWithSchool = await models.Course.findOne({
include: [{
model: models.School,
required: true,
}],
})
The mysql for this is very simple.
mysql> select c.*, s.* from courses c inner join school_codes sc on c.school_code = sc.code inner join schools s on s.id = sc.school_id;
How do I define the relations in sequelize models (without modifying existing schema)? Thanks!
Here are the model definitions I have:
schools.js
module.exports = (sequelize, DataTypes) => {
const School = sequelize.define('School', {
name: DataTypes.STRING,
}, { underscored: true, freezeTableName: true, tableName: 'schools' })
return School
}
course.js
module.exports = (sequelize, DataTypes) => {
const Course = sequelize.define('Course', {
id: {
type: DataTypes.STRING,
primaryKey: true,
},
name: DataTypes.STRING,
school_code: {
type: DataTypes.STRING,
references: {
model: 'school_codes',
key: 'code',
}
}
}, { underscored: true, freezeTableName: true, tableName: 'courses' })
return Course
}
schoolcode.js
module.exports = (sequelize, DataTypes) => {
const SchoolCode = sequelize.define('SchoolCode', {
code:{
type: DataTypes.STRING,
primaryKey: true,
references: {
model: 'courses',
key: 'school_code'
}
},
school_id: {
type: DataTypes.INTEGER,
references: {
model: 'schools',
key: 'id',
},
},
}, { underscored: true, freezeTableName: true, tableName: 'school_codes', })
return SchoolCode
}
I'm just looking for the relations to add to the bottom of each model definition - example...
// School.associate = function (models) {
// School.belongsToMany(models.Course, {
// through: 'school_codes',
// foreignKey: 'school_id',
// otherKey: 'code'
// })
// }
We can keep association in its respective model. I prefer to keep association in respective master table rather than mapping table. The idea is to associate source model to target model and its relationship in both direction. For example let us say source model School has one SchoolCode target model and its reverse relation
//school.model.js
module.exports = (sequelize, DataTypes) => {
const School = sequelize.define('school', {
name: DataTypes.STRING,
}, { underscored: true, freezeTableName: true, tableName: 'schools' })
School.associate = function ({SchoolCode, Course}) {
School.hasOne(SchoolCode, {
foreignKey: 'school_id',
})
SchoolCode.belongsTo(School, {foreignKey: 'school_id'})
School.belongsToMany(Course, { through: SchoolCode , foreignKey : 'school_id'}); //added new
}
return School;
}
//course.model.js
module.exports = (sequelize, DataTypes) => {
const Course = sequelize.define('course', {
id: {
type: DataTypes.STRING,
primaryKey: true,
},
name: DataTypes.STRING,
school_code: {
type: DataTypes.STRING,
references: {
model: 'school_codes',
key: 'code',
}
}
}, { underscored: true, freezeTableName: true, tableName: 'courses' })
Course.associate = function ({SchoolCode, School}) {
Course.hasMany(SchoolCode, {
foreignKey: 'code',
})
Course.belongsToMany(School, { through: SchoolCode, foreignKey : 'code'}); //added new
}
return Course;
}
Finally the third model of SchoolCode (Mapping table).
Note that we don't have to add a reference school_code. It is a primaryKey code of same table. We use references mainly to define the foreign keys, no need for reverse definition here.
Hence commented that part from code below.
module.exports = (sequelize, DataTypes) => {
const SchoolCode = sequelize.define('SchoolCode', {
code:{
type: DataTypes.STRING,
primaryKey: true,
// references: {
// model: 'courses',
// key: 'school_code'
// }
},
school_id: {
type: DataTypes.INTEGER,
references: {
model: 'school',
key: 'id',
},
},
}, { underscored: true, freezeTableName: true, tableName: 'school_codes', })
return SchoolCode
}
References : https://sequelize.org/master/manual/assocs.html
You can define relations like
SchoolCode.belongsTo(School, { foreignKey: 'school_id', targetKey: 'id' });
Course.belongsTo(SchoolCode, { foreignKey: 'school_code', targetKey: 'code' });

Saving Sequelize Record/Referencing it in Another Model

I have a postrgresql/Sequelize model called Segment, which belongs to many models:
module.exports = (sequelize, DataTypes) => {
const Segment = sequelize.define(
'segment',
{
id: {
type: DataTypes.INTEGER,
autoIncrement: true,
primaryKey: true
},
provider_id: {
type: DataTypes.INTEGER,
references: {
model: 'provider',
key: 'id'
}
},
summary_id: {
type: DataTypes.INTEGER,
references: {
model: 'summary',
key: 'id'
}
},
audience_id: {
type: DataTypes.INTEGER,
references: {
model: 'audience',
key: 'id'
}
},
onboarding_id: {
type: DataTypes.INTEGER,
references: {
model: 'onboarding',
key: 'id'
}
}
},
{
// disable the modification of table names; By default, sequelize will automatically
// transform all passed model names (first parameter of define) into plural.
// if you don't want that, set the following
freezeTableName: true,
tableName: 'segment'
}
);
Segment.associate = models => {
Segment.belongsTo(models.Provider, { foreignKey: 'id' });
Segment.belongsTo(models.Summary, { foreignKey: 'id' });
Segment.belongsTo(models.Audience, { foreignKey: 'id' });
Segment.belongsTo(models.Onboarding, { foreignKey: 'id' });
};
return Segment;
};
The models that segment has associations to (ie provider_id, summary_id, audience_id, onboarding_id) look like this:
Provider:
module.exports = (sequelize, DataTypes) => {
const Provider = sequelize.define(
'provider',
{
id: {
type: DataTypes.INTEGER,
autoIncrement: true,
primaryKey: true
},
providerName: {
type: DataTypes.STRING
},
email: {
type: DataTypes.STRING
},
privacyPolicy: {
type: DataTypes.STRING
}
},
{
freezeTableName: true,
tableName: 'provider'
}
);
Provider.associate = models => {
Provider.hasMany(models.Segment, { foreignKey: 'provider_id' });
};
return Provider;
};
Summary:
module.exports = (sequelize, DataTypes) => {
const Summary = sequelize.define(
'summary',
{
id: {
type: DataTypes.INTEGER,
autoIncrement: true,
primaryKey: true
},
summaryName: DataTypes.STRING,
standardIdName: DataTypes.STRING,
description: DataTypes.STRING,
},
{
freezeTableName: true,
tableName: 'summary'
}
);
Summary.associate = models => {
Summary.hasMany(models.Segment, { foreignKey: 'summary_id' });
};
return Summary;
};
Audience:
module.exports = (sequelize, DataTypes) => {
const Audience = sequelize.define(
'audience',
{
id: {
type: DataTypes.INTEGER,
autoIncrement: true,
primaryKey: true
},
refreshCadence: DataTypes.STRING,
sourceLookbackWindow: DataTypes.STRING
},
{
freezeTableName: true,
tableName: 'audience'
}
);
Audience.associate = models => {
Audience.hasMany(models.Segment, { foreignKey: 'audience_id' });
};
return Audience;
};
Onboarding:
module.exports = (sequelize, DataTypes) => {
const Onboarding = sequelize.define(
'onboarding',
{
id: {
type: DataTypes.INTEGER,
autoIncrement: true,
primaryKey: true
},
onboardingExpansions: DataTypes.STRING,
onboardingAudiencePrecision: DataTypes.STRING
},
{
freezeTableName: true,
tableName: 'onboarding'
}
);
Onboarding.associate = models => {
Onboarding.hasMany(models.Segment, { foreignKey: 'onboarding_id' });
};
return Onboarding;
};
My question is: what should come first when creating and saving a Segment record? Do I create and save each one of the other models first (provider, summary, audience, onboarding), and then create/save a Segment with references to those ids? I don't really know what the order of events should be in this situation. Any help is much appreciated! Thanks!
TLDR:
In order to create an instance of Segment, you must have all 4 foreign keys reference exist records on the referenced tables(provider, summary, audience and onboarding).
Explanation:
provider, summary, audience and onboarding tables are independent.
However, Segment model is not independent.
Segment model has 4 columns which are foreign keys.
From PostgresSql Tutorial:
A foreign key is a field or group of fields in a table that uniquely
identifies a row in another table. In other words, a foreign key is
defined in a table that references to the primary key of the other
table.
The table that contains the foreign key is called referencing table or
child table. And the table to which the foreign key references is
called referenced table or parent table.
It means that a foreign key is a constraint that the column should reference the primary key of the referenced table.
So, you must create all the resources of a created row of Segment.

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

Update with association using Sequelize.js

I am trying to update with association using sequelize.js.
I have tried give example on stackoverflow namely the following links:
Sequelize update with association
Sequelize update with association
Updating attributes in associated models using Sequelize
all of these links did not get me to the goal i am trying to accomplish.
My model is as follow, I have a country module and a city module. a country has many cities. please refer to the module bellow.
Please advise.
country.js file
module.exports = function (sequelize, DataTypes) {
var country= sequelize.define('COUNTRY', {
COUNTRY_ID: {
type: DataTypes.INTEGER,
primaryKey: true,
autoIncrement: true
},
COUNTRY_NAME: DataTypes.STRING,
COUNTRY_CURRENCY: DataTypes.STRING
}, {
freezeTableName: true,
classMethods: {
associate: function (models) {
COUNTRY_ID.hasMany(models.CITIES, {
foreignKey: 'COUNTRY_ID'
})
}
},
instanceMethods: {
updateAssociation: function (onSuccess, onError) {
country.findAll({
where: {
COUNTRY_ID: req.params.country_id
},
include: [
{
model: sequelize.import('./cities.js'),
}
]
})
})
.then(country =>{
const updatePromises = country.map(countries =>{
return countries.updateAttributes(req.body);
});
const updatePromisescities = list.CITY.map(cities =>{
return cities.updateAttributes(req.body.CITYs[0]);
});
return sequelize.Promise.all([updatePromises, updatePromisescities ])
}).then(onSuccess).error(onError);
}
}
});
return country;
};
city.js file
module.exports = function (sequelize, DataTypes) {
var CITY = sequelize.define('LIST_CODE', {
CITY_ID: {
type: DataTypes.INTEGER,
primaryKey: true
},
COUNTRY_ID: {
type: DataTypes.INTEGER,
primaryKey: true
}
}, {
freezeTableName: true,
timestamps: false,
classMethods: {
associate: function (models) {
// associations can be defined here
CITY.belongsTo(models.COUNTRY, {
foreignKey: 'COUNTRY_ID'
})
}
}
});
return CITY;
};

Resources