Sequelize: refer a foreign key between 2 models - mysql

I'm trying to refer a foreign key between 2 models. but I'm getting this error:
throw new AssociationError(`${source.name}.belongsToMany(${target.name}) requires through option, pass either a string or a model`);
AssociationError [SequelizeAssociationError]: Order.belongsToMany(User) requires through option, pass either a string or a model
I check some similar questions but it didn't help.
I started working with Sequelize today so please give an example to answer.
db.js
const fs = require("fs");
const path = require("path");
const Sequelize = require("sequelize");
const basename = path.basename(module.filename);
const db = {};
const sequelize = new Sequelize("name", "user", "password", {
host: "localhost",
dialect: "mysql",
logging: false,
});
fs.readdirSync(__dirname).filter(file =>
(file.indexOf(".") !== 0) &&
(file !== basename) &&
(file.slice(-3) === ".js"))
.forEach(file => {
const model = sequelize.import(path.join(__dirname, file));
db[model.name] = model;
});
Object.keys(db).forEach(modelName => {
if (db[modelName].associate) {
db[modelName].associate(db);
}
});
db.sequelize = sequelize;
db.Sequelize = Sequelize;
module.exports = db;
user.js
const Sequelize = require("sequelize");
module.exports = (sequelize, DataTypes) => {
const User = sequelize.define("User",
{
id: {
type: Sequelize.BIGINT,
autoIncrement: true,
allowNull: false,
primaryKey: true,
},
email: Sequelize.STRING(60),
password: Sequelize.STRING(60),
fullName: {
type: Sequelize.STRING(60),
allowNull: true,
},
},
);
User.associate = function (models) {
models.User.belongsToMany(models.Order);
};
return User;
};
order.js
const Sequelize = require("sequelize");
module.exports = (sequelize, DataTypes) => {
const Order = sequelize.define("Order",
{
id: {
type: Sequelize.BIGINT,
autoIncrement: true,
allowNull: false,
primaryKey: true,
},
title: Sequelize.STRING(60),
orderNumber: Sequelize.STRING(60),
status: Sequelize.STRING(60),
amount: Sequelize.STRING(60),
trackingCode: Sequelize.STRING(60),
},
);
Order.associate = function(models) {
models.Order.belongsToMany(models.User);
};
return Order;
};
when I use lowercase for define name like this:
const User = sequelize.define("user",
{
...
}
);
const User = sequelize.define("order",
{
...
}
);
I get this error:
TypeError: Cannot read property 'belongsToMany' of undefined
sequelize: 5.21.6

I answered similar question here. Please take a look at it. Also pay attention to comments and to the link at the end of the discussion.
If you'll have any questions after reading feel free to ask them in comments to this answer.

Related

how to store logs into mysql db using express-winston in Node.js

I am trying to store logs into mysql db(with Sequelize) using express-winston in Node.js.
According to doc(https://www.npmjs.com/package/winston-sql-transport), I should do the following:
const { Logger } = require('winston');
const { SQLTransport } = require('./../lib/winston-sql-transport');
const logger = new Logger({
transports: [
new SQLTransport({
tableName: 'winston_logs',
})]
});
module.exports = logger;
I apply above code in app.js, but not successful.
app.js
const { SQLTransport } = require('./../lib/winston-sql-transport'); // //Error: Cannot find module './../lib/winston-sql-transport'
const mysqlOption = {
tableName: 'winston_logs'
}
app.use(
expressWinston.logger({
transports: [
new SQLTransport(mysqlOption)
],
format: winston.format.combine(
winston.format.timestamp({ format: timezoned }),
winston.format.json(),
),
metaField: null,
expressFormat: true,
statusLevels: false,
level: function (req, res) {
var level = "";
if (res.statusCode < 400) { level = "info"; }
if (res.statusCode >= 400) { level = "warn"; }
if (res.statusCode >= 500) { level = "error"; }
return level;
}
})
)
I tried to change the import to const { SQLTransport } = require('winston-sql-transport');
Resulting error:
Error: You have to define client
at new SQLTransport (/server/node_modules/winston-sql-transport/lib/winston-sql-transport.js:40:13)
I found that people rarely talk about this package.
So I wonder if there is any way to do it?
Update 1:
I updated mysqlOption as suggested by the comment
const { SQLTransport } = require('winston-sql-transport');
const mysqlOption = {
tableName : "winston_logs",
client: 'mysql',
connection: {
host: '127.0.0.1:3306',
user: 'root',
password: '',
database: 'mydb'
}
}
The logs successfully appear in Console, but nothing store in my database.
Update 2:
Since I am using Sequelize, I'm going to provide the schema for the logs table
// See http://docs.sequelizejs.com/en/latest/docs/models-definition/
// for more of what you can do here.
const Sequelize = require('sequelize');
const DataTypes = Sequelize.DataTypes;
module.exports = function (app) {
const sequelizeClient = app.get('sequelizeClient');
const logs = sequelizeClient.define('winston_logs', {
id: {
type: DataTypes.INTEGER(10),
allowNull: false,
autoIncrement: true,
primaryKey: true
},
level: {
type: DataTypes.STRING(45),
allowNull: false
},
message: {
type: DataTypes.TEXT,
allowNull: false
},
meta: {
type: DataTypes.STRING(255),
allowNull: false
},
hostname: {
type: DataTypes.STRING(255),
allowNull: false
},
timestamp: {
type: DataTypes.DATE,
allowNull: false
},
}, {
hooks: {
beforeCount(options) {
options.raw = true;
}
}
});
logs.associate = function (models) {
};
return logs;
};
Finally giving up to store logs in mysql db, since mysql transport package seems a bit outdated.
Instead, I store the logs locally, using winston-daily-rotate-file
It can set the frequency of rotation, Maximum size of the file etc.
link: https://github.com/winstonjs/winston-daily-rotate-file

Sequelize N:M ParentModel.setChilds(ChildArray) is not a function (Magic Method?)

In my program, I'd like to update many cateogories has a parent table named Column. Also, a category can contain of many columns.(N:M)
So I tried to update a category to the post, but it failed. Here is the code I've tried
let updateColumn = async function(req, res) {
let column_id = req.body.id;
let column_title = req.body.title;
let column_content_text = req.body.contentText;
let column_content_html = req.body.contentHTML;
let column_categorys = req.body.categoris;
let column_tags = req.body.tags;
let column_thumbnail_url = process.env.CDN_COLUMN_ENDPOINT;
column_thumbnail_url += column_title;
column_thumbnail_url += '/';
column_thumbnail_url += '0.png';
var category_array = [];
var tag_array = [];
await sequelize.transaction(async transaction => {
try {
await Column.update({
column_title: column_title,
column_content_text: column_content_text,
column_content_html: column_content_html,
column_thumbnail_url: column_thumbnail_url,
}, {
where: {
id: column_id,
},
}, {transaction: transaction});
const column = await Column.findOne({
where: {
id: column_id,
},
transaction,
});
for(var i in column_categorys) {
const [category, created] = await Category.findOrCreate({
where : {
category_name: column_categorys[i],
},
transaction,
});
category_array.push(category);
}
for(var j in column_tags) {
const [tag, created] = await Tag.findOrCreate({
where : {
tag_name: column_tags[j],
},
transaction,
});
tag_array.push(tag);
}
await column.setCategorys(
category_array,
{ transaction },
);
await column.setTags(
tag_array,
{ transaction },
);
res.json({
responseCode: 400,
responseMsg: 'Success',
});
} catch (err) {
console.error(err);
res.json({
responseCode: 404,
responseMsg: 'Fail',
});
}
});
}
column_info.js
const Sequelize = require('sequelize');
module.exports = class Column extends Sequelize.Model {
static init(sequelize) {
return super.init({
column_title: {
type: Sequelize.STRING(200),
allowNull: false,
unique: true,
},
column_content_text: {
type: Sequelize.STRING(10000),
allowNull: false,
},
column_content_html: {
type: Sequelize.STRING(10000),
},
column_thumbnail_url: {
type: Sequelize.STRING(300),
},
}, {
sequelize,
timestamps: true,
underscored: true,
modelName: 'Column',
tableName: 'columns',
paranoid: true,
charset: 'utf8',
collate: 'utf8_general_ci',
});
}
static associate(db) {
db.Column.belongsToMany(db.Category, {
through: 'ColumnCategory',
unique: false,
});
db.Column.belongsToMany(db.Tag, {
through: 'ColumnTag',
unique: false,
});
}
};
category_info.js
const Sequelize = require('sequelize');
module.exports = class Category extends Sequelize.Model {
static init(sequelize) {
return super.init({
category_name: {
type: Sequelize.STRING(50),
allowNull: false,
unique: true,
},
}, {
sequelize,
timestamps: false,
underscored: true,
modelName: 'Category',
tableName: 'categorys',
paranoid: false,
charset: 'utf8',
collate: 'utf8_general_ci',
});
}
static associate(db) {
db.Category.belongsToMany(db.Column, {
through: 'ColumnCategory',
unique: false,
});
}
};
model/index.js
const Sequelize = require('sequelize');
const User = require('./user');
const Baby = require('./baby_info');
const UserCase = require('./user_case');
const Column = require('./column_info');
const Tag = require('./tag_info');
const Category = require('./category_info');
const env = process.env.NODE_ENV || 'development';
const config = require(__dirname + '/../config/config')[env];
const db = {};
const sequelize = new Sequelize(config.database, config.username, config.password, config);
db.sequelize = sequelize;
db.User = User;
db.Baby = Baby;
db.UserCase = UserCase;
db.Column = Column;
db.Tag = Tag;
db.Category = Category;
User.init(sequelize);
Baby.init(sequelize);
UserCase.init(sequelize);
Column.init(sequelize);
Tag.init(sequelize);
Category.init(sequelize);
User.associate(db);
Baby.associate(db);
UserCase.associate(db);
Column.associate(db);
Tag.associate(db);
Category.associate(db);
module.exports = db;
error log
0|app | 21-01-04 16:09:26: TypeError: column.setCategorys is not a function
0|app | 21-01-04 16:09:26: at /home/ubuntu/bebenity/routes/column/column.ctrl.js:66:20
0|app | 21-01-04 16:09:26: at processTicksAndRejections (internal/process/task_queues.js:93:5)
0|app | 21-01-04 16:09:26: at async /home/ubuntu/bebenity/node_modules/sequelize/lib/sequelize.js:1090:24
0|app | 21-01-04 16:09:26: at async updateColumn (/home/ubuntu/bebenity/routes/column/column.ctrl.js:21:3)
Could you tell me what part of this code I've used relationship queries incorrectly?
Add 1
Actually, I've had a similar experience.
How to add child row in related table?? (Sequelzie N:M Relationship)
In that example, the method findOrCreate's callback value has a problem.
But, in this question, the method findOne's callback value is clearly Model Object! I have already checked through column instanceof Column that the column is the Column model instance.(It is true)
But it is a little bit different between model with Create method and findOne
Create Callback
findOne Callback
The solution was very simple...
Revising my column.setCategorys to column.setCategories was that...
OMG

TypeError: require(...) is not a function while Using sequalize with node js

I am beginner in web dev and trying to create a Full stack project using Mysql Express React Node js.
Facing this TypeError issue while using Sequalize with node js.
I'm new to this and I can't understand the problem. Can someone please explain it to me and help me find a solution?
(PS: If you guys need any other file let me know)
server js
const express = require("express");
const bodyParser = require("body-parser");
const cors = require("cors");
const app = express();
var corsOptions = {
origin: "http://localhost:8081"
};
app.use(cors(corsOptions));
// parse requests of content-type - application/json
app.use(bodyParser.json());
// parse requests of content-type - application/x-www-form-urlencoded
app.use(bodyParser.urlencoded({ extended: true }));
const db = require("./models");
db.sequelize.sync({ force: true }).then(() => {
console.log("Drop and re-sync db.");
});
// simple route
app.get("/", (req, res) => {
res.json({ message: "Welcome." });
});
require("./src/routes/routes")(app);
// set port, listen for requests
const PORT = process.env.PORT || 3000;
app.listen(PORT, () => {
console.log(`Server is running on port ${PORT}.`);
});
index js
const dbConfig = require("../config/db.config.js");
const Sequelize = require("sequelize");
const sequelize = new Sequelize(dbConfig.DB, dbConfig.USER, dbConfig.PASSWORD, {
host: dbConfig.HOST,
dialect: dbConfig.dialect,
operatorsAliases: false,
pool: {
max: dbConfig.pool.max,
min: dbConfig.pool.min,
acquire: dbConfig.pool.acquire,
idle: dbConfig.pool.idle
}
});
const db = {};
db.Sequelize = Sequelize;
db.sequelize = sequelize;
db.devices = require("./devices.js")(sequelize, Sequelize);
module.exports = db;
devices js
module.exports = (sequelize, Sequelize) => {
const Device = sequelize.define("devices", {
serialno: {
type: Sequelize.INTEGER
},
brand: {
type: Sequelize.STRING
},
modelname: {
type: Sequelize.STRING
}
});
return Device;
};
error
db.devices = require("./devices.js")(sequelize, Sequelize);
^
TypeError: require(...) is not a function
at Object.<anonymous> (C:\Users\theta\Documents\project\src\models\index.js:22:37)
at Module._compile (internal/modules/cjs/loader.js:1138:30)
at Object.Module._extensions..js (internal/modules/cjs/loader.js:1158:10)
at Module.load (internal/modules/cjs/loader.js:986:32)
at Function.Module._load (internal/modules/cjs/loader.js:879:14)
at Module.require (internal/modules/cjs/loader.js:1026:19)
at require (internal/modules/cjs/helpers.js:72:18)
at Object.<anonymous> (C:\Users\theta\Documents\project\src\server.js:19:12)
at Module._compile (internal/modules/cjs/loader.js:1138:30)
at Object.Module._extensions..js (internal/modules/cjs/loader.js:1158:10)
Try to change this on index.js:
db.devices = require("./devices.js")(sequelize, Sequelize);
To this:
const path = require('path')
db.devices = require(path.join(__dirname, 'devices'))(sequelize, Sequelize);
Here's what works for me in TypeScript (using sequelize version 6.17.0):
config.ts
import { Sequelize } from 'sequelize'
const dbName = 'test-db'
const dbUser = 'test-user'
const dbPass = '123123'
const dbHost = 'some-host'
// new Sequelize(name, user, password, options)
export const sequelize = new Sequelize(dbName, dbUser, dbPass, {
host: dbHost,
dialect: 'postgres',
port: 3306,
dialectOptions: {
multipleStatements: true,
},
pool: {
max: 5,
min: 0,
idle: 10000,
},
logging: false,
})
Let's say we have a models/ folder with three files: User.ts, Member.ts and index.ts.
models/User.ts → we must use module.exports here
module.exports = (sequelize: any, DataTypes: any) => {
const User = sequelize.define(
'user',
{
id: {
type: DataTypes.UUID,
defaultValue: DataTypes.UUIDV4,
allowNull: false,
primaryKey: true,
},
email: {
type: DataTypes.STRING(250),
allowNull: false,
unique: true,
},
password: {
type: DataTypes.STRING,
allowNull: true,
defaultValue: null,
},
},
{
timestamps: true,
underscored: true,
tableName: 'user',
indexes: [{ unique: true, fields: ['email'] }],
}
)
User.associate = (models: any) => {
User.hasOne(models.Member, { as: 'Member', foreignKey: 'id' })
}
return User
}
models/index.ts
import fs from 'fs'
import path from 'path'
import Sequelize from 'sequelize'
import { sequelize } from './config.js'
// current file basename
const basename = path.basename(__filename)
// our db object
const db: any = {}
// so we don't reload unecessarily
let loaded = false
const createModels = () => {
// if already loaded, return cached object
if (loaded) return db
// create an array of model files' basenames
const filenames = fs.readdirSync(__dirname).filter((file: string) => {
return (
// filter out the current `index.ts` file
file.indexOf('.') !== 0 && file !== basename && file.slice(-3) === '.ts'
)
})
filenames.map((file: any) => {
// use `require` to load our models
const model = require(path.join(__dirname, file))(
sequelize,
Sequelize.DataTypes
)
db[model.name] = model
})
// run `.associate` if applicable
Object.keys(db).map((model) => {
if (db[model].associate) {
db[model].associate(db)
}
})
// attach both our instance and Sequelize to our db object
db.sequelize = sequelize
db.Sequelize = Sequelize
loaded = true
return db
}
export default createModels()
export { createModels }
Let me know how it goes.
If you are tying to import any file for example a font file, then create a mockFile that just have
// mockFile.ts or mockFile.js
export default {};
and then in jest.config.js
moduleNameMapper: {
'^.+\\.(woff2)$': '<rootDir>/<path to file>/mockFile.ts',
},
This will resolve to the mockFil when it will try to import a font file. You can also add other file extentions like '^.+\\.(jpg|png|woff2)$: '<rootDir>/<path to file>/mockFile.ts'.

How to add associations to Models generated from Sequelize-Auto

For an existing MySQL database, I used Sequelize-auto package to generate the models. But the associations don't come with model classes.
I have an MySQL database and I'm using it for NodeJS web project. Also I'm using Sequelize as the ORM. Since database is already there I wanted to generate the model classes as the entities. So I used sequelize-auto
https://github.com/sequelize/sequelize-auto to generate the model classes. But when they were generated attributes have been correctly set but the associations doesn't come up with the model classes. So I have faced problem when fetching the data from the database.
Here are the two model classes that were generated with sequlize-auto. There are two table in the database named as department and category. department.js and category.js are the two model classes that were generated
department.js
module.exports = function(sequelize, DataTypes) {
return sequelize.define('department', {
department_id: {
type: DataTypes.INTEGER(11),
allowNull: false,
primaryKey: true,
autoIncrement: true
},
name: {
type: DataTypes.STRING(100),
allowNull: false
},
description: {
type: DataTypes.STRING(1000),
allowNull: true
}
}, {
tableName: 'department',
timestamps: false,
});
};
category.js
module.exports = function(sequelize, DataTypes) {
return sequelize.define('category', {
category_id: {
type: DataTypes.INTEGER(11),
allowNull: false,
primaryKey: true,
autoIncrement: true
},
department_id: {
type: DataTypes.INTEGER(11),
allowNull: false
},
name: {
type: DataTypes.STRING(100),
allowNull: false
},
description: {
type: DataTypes.STRING(1000),
allowNull: true
}
}, {
tableName: 'category',
timestamps: false,
});
};
So what else needs to be done in order to get the associations and to fetch data successfully. Can some one help me here. Table structure is as following.
1) inside of your models folder create an index.js file and add the following code
import Sequelize from 'sequelize';
const fs = require('fs');
const path = require('path');
const basename = path.basename(__filename);
const db = {};
// #ts-ignore
const sequelize = new Sequelize('dbname', 'dbUser', 'password', {
host: '127.0.0.1',
port: 'PORT',
dialect: 'mysql',
define: {
freezeTableName: true,
timestamps: false,
},
pool: {
max: 5,
min: 0,
acquire: 30000,
idle: 10000,
},
// <http://docs.sequelizejs.com/manual/tutorial/querying.html#operators>
operatorsAliases: false,
});
const tableModel = {};
fs.readdirSync(__dirname)
.filter(file => file.indexOf('.') !== 0 && file !== basename && file.slice(-3) === '.js')
.forEach(file => {
const model = sequelize.import(path.join(__dirname, file));
db[model.name] = model;
tableModel[model.name] = model;
});
Object.getOwnPropertyNames(db).forEach(modelName => {
const currentModel = db[modelName];
Object.getOwnPropertyNames(currentModel.rawAttributes).forEach(attributeName => {
if (
Object.prototype.hasOwnProperty.call(
currentModel.rawAttributes[attributeName],
'references'
) &&
Object.prototype.hasOwnProperty.call(
currentModel.rawAttributes[attributeName].references,
'model'
) &&
Object.prototype.hasOwnProperty.call(
currentModel.rawAttributes[attributeName].references,
'key'
)
) {
if (
!(
currentModel.rawAttributes[attributeName].references.model &&
currentModel.rawAttributes[attributeName].references.key
)
) {
console.log(
`*SKIPPED* ${modelName} ${attributeName} references a model ${currentModel.rawAttributes[attributeName].references.model} with key ${currentModel.rawAttributes[attributeName].references.key}`
);
return;
}
console.log(
`${modelName} ${attributeName} references a model ${currentModel.rawAttributes[attributeName].references.model} with key ${currentModel.rawAttributes[attributeName].references.key}`
);
const referencedTable =
tableModel[currentModel.rawAttributes[attributeName].references.model];
currentModel.belongsTo(referencedTable, { foreignKey: attributeName });
referencedTable.hasMany(currentModel, { foreignKey: attributeName });
}
});
});
// #ts-ignore
db.sequelize = sequelize;
// #ts-ignore
db.Sequelize = Sequelize;
// eslint-disable-next-line eol-last
module.exports = db;
2) inside of your resolver just reference the above:
const db = require('../assets/models/index');
To add to #CodingLittles answer which he based from here.
I added the following for making many to many assosiations:
enum Junctions {
user = 'user',
roles = 'roles',
}
enum JuntiontThrough {
userroles = 'userroles',
}
interface JunctionObject {
junctionBelongsTo?: any;
}
const checkIfAttrExists= (obj, value) => {
return Object.prototype.hasOwnProperty.call(obj, value);
};
const checkRefrence = (obj, attr, value) => {
return obj.rawAttributes[attr].references[value];
};
export const getJunction = (junc: Junctions): JunctionObject => {
const junctions = {
user: {
junctionBelongsTo: [
{ key: Junctions.roles, value: juntiontThrough.userroles }
],
},
roles: {
junctionBelongsTo: [{ key: Junctions.user, value: juntiontThrough.userroles }],
},
}[junc];
if (!junctions) return {};
return junctions;
};
const models = Object.getOwnPropertyNames(db);
models.forEach(modelName => {
const currentModel = db[modelName];
const junction = getJunction(modelName as Junctions);
if (!_.isEmpty(junction)) {
// eslint-disable-next-line array-callback-return
junction.junctionBelongsTo.reduce((key, value) => {
currentModel.belongsToMany(db[value.key], {
through: db[value.value],
});
}, {});
}
const attributes = Object.getOwnPropertyNames(currentModel.rawAttributes);
attributes.forEach(attributeName => {
if (
checkIfAttrExists(currentModel.rawAttributes[attributeName], 'references') &&
checkIfAttrExists(currentModel.rawAttributes[attributeName].references, 'model') &&
checkIfAttrExists(currentModel.rawAttributes[attributeName].references, 'key')
) {
if (
!(
checkRefrence(currentModel, attributeName, 'model') &&
checkRefrence(currentModel, attributeName, 'key')
)
) {
return;
}
const referencedTable =
tableModel[currentModel.rawAttributes[attributeName].references.model];
if (!(modelName.toString() in juntiontThrough)) {
console.log(
`${modelName} ${attributeName} references a model ${currentModel.rawAttributes[attributeName].references.model} with key ${currentModel.rawAttributes[attributeName].references.key}`
);
currentModel.belongsTo(referencedTable, { foreignKey: attributeName });
referencedTable.hasMany(currentModel, { foreignKey: attributeName });
}
}
});
})
Note that for the many to many relations to work you have to add the relations manually like I did in the getJunction function
I followed the pattern demonstrated in the typescript example on sequelize-auto (init-models.ts) - within the existing initModels function, works fine in js.
export function initModels(sequelize: Sequelize) {
Product.initModel(sequelize);
Supplier.initModel(sequelize);
Supplier.hasMany(Product, { as: "products", foreignKey: "supplierId"});
return {...

SequelizeEagerLoadingError: (parent) is not associated to (child)!

I am building an application using sequelize. I currently have 3 tables; a User, a Tour, and a Location. The Location has a n:1 relationship with the Tour. The Tour has a n:1 relationship with the user.
Without the User association, the other two tables work fine. Once I add in the user association (and I have tried to do so through a migration AND by dropping and then recreating my entire database), I get a SequelizeEagerLoadingError: Location is not associated with Tour!
Here are my models:
module.exports = function(sequelize, DataTypes) {
var Location = sequelize.define("Location", {
title: {
type: DataTypes.STRING,
allowNull: false
},
description: {
type: DataTypes.TEXT,
allowNull: false,
validate: {
len: [500]
}
},
address: {
type: DataTypes.TEXT,
allowNull: false
}
});
Location.associate = function(models) {
Location.belongsTo(models.Tour, {
onDelete: "cascade"
});
};
return Location;
};
module.exports = function(sequelize, DataTypes) {
var Tour = sequelize.define("Tour", {
title: {
type: DataTypes.STRING,
allowNull: false
},
description: {
type: DataTypes.TEXT,
allowNull: false,
validate: {
len: [1, 1000]
}
},
neighborhood: {
type: DataTypes.STRING,
allowNull: false
},
URL: {
type: DataTypes.TEXT,
allowNull: false,
validate: {
len: [1, 1000]
}
},
numberOfStops: DataTypes.INTEGER,
duration: {
type: DataTypes.INTEGER,
allowNull: false
},
tags: DataTypes.STRING
});
Tour.associate = function(models) {
Tour.hasMany(models.Location);
};
Tour.associate = function(models) {
Tour.belongsTo(models.User);
};
return Tour;
};
var bcrypt = require("bcrypt-nodejs");
module.exports = function(sequelize, DataTypes) {
var User = sequelize.define("User", {
name: {
type: DataTypes.STRING,
allowNull: false
},
email: {
type: DataTypes.STRING,
allowNull: false,
unique: true,
validate: {
isEmail: true
}
},
password: {
type: DataTypes.STRING,
allowNull: false
}
});
User.prototype.validPassword = function(password) {
return bcrypt.compareSync(password, this.password);
};
User.hook("beforeCreate", function(user) {
user.password = bcrypt.hashSync(
user.password,
bcrypt.genSaltSync(10),
null
);
});
User.associate = function(models) {
User.hasMany(models.Tour);
};
return User;
};
And here is the include statement where it is failing, and where we establish the link with the tourId to the location:
app.get("/tour/:id", function(req, res) {
db.Tour.findOne({
where: { id: req.params.id },
include: [db.Location]
}).then(function(tour) {
res.render("tour", {
tour: tour
});
});
});
var API = {
saveTour: function(tour) {
return $.ajax({
headers: {
"Content-Type": "application/json"
},
type: "POST",
url: "api/tours",
data: JSON.stringify(tour)
});
},
saveLocations: function(locations) {
return $.ajax({
headers: {
"Content-Type": "application/json"
},
type: "POST",
url: "api/locations",
data: JSON.stringify(locations)
});
},
getUserId: function() {
return $.ajax({
type: "GET",
url: "api/user_data"
});
}
};
var tour = {
Users: thisUser.getUserId(),
title: title,
description: description,
neighborhood: neighborhood,
URL: URL,
duration: duration,
tags: tags
};
// console.log(tour);
if (!errors.length) {
// Post our tour to the Tours table, then reveal the form and set our local tour object.
API.saveTour(tour).then(function(tour) {
document.getElementById("submit-tour").remove();
document.getElementById("tourstopssection").style.display = "block";
thisTour.setId(tour.id);
});
}
}
// Function takes in the newly created tour object, grabs DOM values for each.
function addTourLocations(e) {
e.preventDefault();
// Grab and process all of our tour stops.
var locationElements = document.getElementsByClassName("tourstop");
var areStopErrors = false;
var locations = [];
// Loop over every location element on the DOM.
for (var j = 0; j < locationElements.length; j++) {
var children = locationElements[j].children;
// Initialize this location with the tour id; we'll pass in data...
var thisLocation = {
TourId: thisTour.getId()
};
// ... by looping over the DOM children and grabbing their form values.
for (var k = 0; k < children.length; k++) {
if (
children[k].classList.value.includes("stoptitle") &&
children[k].value
) {
var stopTitle = children[k].value;
thisLocation.title = stopTitle;
}
if (
children[k].classList.value.includes("stopaddress") &&
children[k].value
) {
var stopAddress = children[k].value;
thisLocation.address = stopAddress;
}
if (
children[k].classList.value.includes("stopdescription") &&
children[k].value
) {
var stopDescription = children[k].value;
thisLocation.description = stopDescription;
}
}
// Push this location into our locations array.
locations.push(thisLocation);
Finally, this is how the app/db are synced:
require("dotenv").config();
var express = require("express");
var session = require("express-session");
var exphbs = require("express-handlebars");
var helpers = require("./lib/helpers");
var db = require("./models");
var passport = require("./config/passport");
var app = express();
var PORT = process.env.PORT || 3000;
// Middleware
app.use(express.urlencoded({ extended: true }));
app.use(express.json());
app.use(express.static("public"));
var hbs = exphbs.create({
defaultLayout: "main",
helpers: helpers // Require our custom Handlebars helpers.
});
//Sessions are used to keep track of our user's login status
app.use(
session({ secret: "keyboard cat", resave: true, saveUninitialized: true })
);
app.use(passport.initialize());
app.use(passport.session());
app.use(function(req, res, next) {
res.locals.user = req.user; // Set a local variable for our user.
next();
});
// Handlebars
app.engine("handlebars", hbs.engine);
app.set("view engine", "handlebars");
// Routes
require("./routes/apiRoutes")(app);
require("./routes/htmlRoutes")(app);
var syncOptions = { force: false };
// If running a test, set syncOptions.force to true
// clearing the `testdb`
if (process.env.NODE_ENV === "test") {
syncOptions.force = true;
}
// Starting the server, syncing our models ------------------------------------/
db.sequelize.sync(syncOptions).then(function() {
app.listen(PORT, function() {
console.log(
"==> 🌎 Listening on port %s. Visit http://localhost:%s/ in your browser.",
PORT,
PORT
);
});
});
module.exports = app;
I've been googling for four days....help!
Try adding this to your associations, also why are you defining twice the association function on Tour?
module.exports = function(sequelize, DataTypes) {
var Location = sequelize.define("Location", {
//
});
Location.associate = function(models) {
Location.belongsTo(models.Tour, { as:'Tour', foreignKey:'tourId', onDelete: "cascade"});
};
return Location;
};
module.exports = function(sequelize, DataTypes) {
var Tour = sequelize.define("Tour", {
//
});
Tour.associate = function(models) {
Tour.hasMany(models.Location, { as: 'Locations', foreignKey: 'tourId'});
Tour.belongsTo(models.User, { as: 'User', foreignKey: 'userId' });
};
return Tour;
};
module.exports = function(sequelize, DataTypes) {
var User = sequelize.define("User", {
//
});
User.associate = function(models) {
User.hasMany(models.Tour, {as: 'Tours', foreignKey: 'userId'});
};
return User;
};
And add the same on the query.
db.Tour.findOne({
where: { id: req.params.id },
include: [{
model: db.Location,
as: 'Locations'
}]
}).then(function(tour) {
res.render("tour", {
tour: tour
});
});
I figured it out - the fact that I had defined the association on the tours model twice was breaking everything. Once I combined them as mentioned above, everything worked perfectly!
One other thing to note - sequelize automatically assigns the foreign key and the alias, so I left that part out.