Node.js - SequelizeDatabaseError: column "user_id" does not exist - javascript

I have a very simple schema but when I try to make a call for a data it returns me the error
SequelizeDatabaseError: column "user_id" does not exist
My database consists of three tables only. Below is the schema file for all of them:
Users
module.exports = (sequelize, DataTypes) => {
const User = sequelize.define('users', {
email: DataTypes.STRING,
password: DataTypes.STRING
}, {});
User.associate = function (models) {
User.hasMany(models.survey_instances)
};
return User;
};
Survey instances
module.exports = (sequelize, DataTypes) => {
const SurveyInstances = sequelize.define('survey_instances', {
userId: {
field: 'user_id',
type: DataTypes.INTEGER
},
templateId: {
field: 'template_id',
type: DataTypes.INTEGER
},
fields: DataTypes.JSONB,
}, {
underscored: true,
});
SurveyInstances.associate = function (models) {
SurveyInstances.belongsTo(models.survey_templates, { foreignKey: 'templateId' })
SurveyInstances.belongsTo(models.users, { foreignKey: 'userId' })
};
return SurveyInstances;
};
Survey Templates
module.exports = (sequelize, DataTypes) => {
const SurveyTemplates = sequelize.define('survey_templates', {
fields: DataTypes.JSONB,
}, {});
SurveyTemplates.associate = function (models) {
SurveyTemplates.hasMany(models.survey_instances)
};
return SurveyTemplates;
};
My database is populated and I make the call by:
http://localhost:3000/templates?id=1
The code handling this endpoint in node is:
app.get('/templates', authenticateToken, async (req, res) => {
const templates = await db.survey_instances.findByPk(1);
res.send(templates);
});
I'm new to node so maybe the error is obvious but I cant see it. I would really appreciate it if someone helped me out. Thank you!

I figured out the issue. There is a bug in the new Sequelize updates that don't make the underscored: true option work. More details can be found here.
So I am now just using camel case instead of snake case. Everthing's working now.

Related

Express doesn't get another user with .map()

I came to a problem, where I can create conversations with multiple people 2 and so on. However, I can't understand why it doesn't store data to seperate User models.
Here is a code that you only need to know:
router.post(
"/",
auth,
[
check("conversators", "There should be at least two conversators").isLength(
{ min: 2 }
),
],
async (req, res) => {
const { conversators } = req.body;
const errors = validationResult(req);
if (!errors.isEmpty()) {
return res.status(400).json({ errors: errors.array() });
}
try {
let conversation = new Conversation({
user: req.user.id,
conversators: conversators,
});
await conversators.map(async (conversator) => {
let user = await User.findById(conversator);
let newData = user;
newData.conversations.push(conversation.id);
console.log('Created data', newData);
let newUser = await User.findOneAndUpdate(
{ user: conversator },
{
$set: {
newData,
},
},
{ new: true }
);
await newUser.save();
console.log(newUser);
});
await conversation.save();
res.status(200).json(conversation);
} catch (error) {
console.error(error.message);
res.status(500).send("Server error.");
}
}
);
module.exports = router;
What I can assure is that this line: console.log('Created data', newData); prints the desired data. However, the next console: console.log(newUser); prints the same User model as the previous one.
const mongoose = require("mongoose");
const Schema = mongoose.Schema;
const UserSchema = new Schema({
name: {
type: String,
required: true,
},
surname: {
type: String,
required: true,
},
email: {
type: String,
required: true,
},
password: {
type: String,
required: true,
},
conversations: [
{
type: mongoose.Schema.Types.ObjectId,
ref: "conversation",
},
],
date: {
type: Date,
default: Date.now,
},
});
module.exports = User = mongoose.model("user", UserSchema);
The reason might be the difference in search methods used to get a record for newData and newUser. You have used User.findById for newData, which will obviously return different objects for different ids. But User.findOneAndUpdate uses filter criteria that may satisfy several results, but only first will be returned. So it boldly depends on what that user field is.
Here is the part that I changed and started to see the data on MongoDB:
await conversators.map(async (conversator) => {
let user = await User.findById(conversator);
let newData = user;
newData.conversations.push(conversation.id);
new Promise(async (resolve, reject) => {
user = await User.findOneAndUpdate(
{ id: conversator },
{
$set: {
newData,
},
},
{ new: true }
);
return resolve;
})
return await user.save();
});
Posted on behalf of the question asker

mongoose populate one to many realtion

I'm trying populate function in mongoose docs
i've searched a lot for this, i couldn't find anything.
and i have one more question. is this good approach to do this since i would need books whenever i need author in my front end code
is there any other ideas to get this behavior?
I've a simple example.
here is my models
const mongoose = require("mongoose");
const Schema = mongoose.Schema;
const Author = new Schema({
name: {
type: String,
required: true,
},
books: [{ type: Schema.Types.ObjectId, ref: "Book" }],
});
const Book = new Schema({
title: {
type: String,
required: true,
},
author: { type: Schema.Types.ObjectId, ref: "Author" },
});
module.exports = {
Author: mongoose.model("author", Author),
Book: mongoose.model("book", Book),
};
then I created some routes
const express = require("express");
const { Book, Author } = require("../models");
const router = express.Router();
router.get("/books", async (req, res) => {
let books = await Book.find();
res.send(books);
});
router.get("/authors", async (req, res) => {
let authors = await Author.find();
res.send(authors);
});
router.post("/book/:author", async (req, res) => {
let { title } = req.body;
let { author } = req.params;
let book = new Book({
title,
author,
});
book.save().then((data) => {
res.send(data);
});
});
router.post("/author", (req, res) => {
let { name } = req.body;
let author = new Author({
name,
});
author.save().then((data) => {
res.send(data);
});
});
module.exports = router;
then I created a new Author and new Book calling these 2 end points I created using postman
here is result from GET /authors
[
{
"books": [],
"_id": "6085a6fb098c0003944b1dcd",
"name": "john",
"__v": 0
}
]
why books are empty array?
I was expecting it to be something like this
[
{
"books": [
{
"_id": "6085a702098c0003944b1dce",
"title": "hello",
}
],
"_id": "6085a6fb098c0003944b1dcd",
"name": "joe",
"__v": 0
}
]
if your other routes have no problems when saving and retrieving data (check the result using mongo shell or Mongo compass or anything else, or better, write tests to automate things for you before coding, anyhow), you should populate books field
router.get("/authors", async (req, res) => {
let authors = await Author.find().populate('books').lean();
res.send(authors);
});
this is your code with out considering your http handler
try it with and without populate to understand the difference
const mongoose = require('mongoose');
mongoose.connect('mongodb://localhost:27017/testX', { useNewUrlParser: true, useUnifiedTopology: true });
const Author = mongoose.model('Author', {
name: {
type: String,
required: true,
},
books: [{ type: mongoose.ObjectId, ref: 'Book' }],
});
const Book = mongoose.model('Book', {
title: {
type: String,
required: true,
},
author: { type: mongoose.ObjectId, ref: 'Author' },
});
(async () => {
try {
const book1 = new Book({ title: 'titleHere' });
await book1.save();
const author1 = new Author({ name: 'nameHere' });
author1.books.push(book1);
await author1.save();
const authors = await Author.find().populate('books').lean();
console.log(authors);
} catch (error) {
console.error(error);
}
})();

I can't insert data into a table in Sequelize (foreign keys specifically)

So I'm using Sequelize for my PostgreSQL database on my JavaScript app. I have 4 models (Users, Posts, Likes, and FollowingsFollowers). My Users model works fine, it doesn't have any foreign keys so I'm able to create and find data with no problems whatsoever. However, my Posts model has 2 foreign keys (user_id and parent_id). user_id references the primary key (id) from the Users table and parent_id references the primary key (id) from itself (the Posts table). When I try to insert data into the Posts table I'm able to insert data on the regular fields, but the foreign keys are simply ignored.
Here is my database connection:
const Sequelize = require('sequelize');
const API = require('../../client/public/config.js');
const db = new Sequelize(API.elephantSqlUrl);
db.authenticate()
.then(() => {
console.log('Connection has been established successfully.');
})
.catch((err) => {
console.error('Unable to connect to the database:', err);
});
module.exports = db;
Here is my Users model:
const Sequelize = require('sequelize');
const db = require('../db.js');
const Users = db.define('users', {
username: {
type: Sequelize.STRING,
unique: true
},
bio: Sequelize.STRING,
profile_picture: Sequelize.STRING
});
module.exports = Users;
Here is my Posts model:
const Sequelize = require('sequelize');
const db = require('../db.js');
const Posts = db.define('posts', {
type: Sequelize.INTEGER,
body: Sequelize.STRING,
likesCount: {
type: Sequelize.INTEGER,
defaultValue: 0
}
});
module.exports = Posts;
Here is where I define the foreign keys and sync my database:
const db = require('./db.js');
const Users = require('./models/users.js');
const Posts = require('./models/posts.js');
const Likes = require('./models/likes.js');
const FollowingsFollowers = require('./models/followingsFollowers.js');
Posts.belongsTo(Users, { foreignKey: 'user_id' });
Users.hasMany(Posts, { foreignKey: 'user_id' });
Posts.belongsTo(Posts, { foreignKey: 'parent_id' });
Posts.hasMany(Posts, { foreignKey: 'parent_id' });
Likes.belongsTo(Posts, { foreignKey: 'post_id' });
Posts.hasMany(Likes, { foreignKey: 'post_id' });
Likes.belongsTo(Users, { foreignKey: 'user_id' });
Users.hasMany(Likes, { foreignKey: 'user_id' });
FollowingsFollowers.belongsTo(Users, { foreignKey: 'following_id' });
Users.hasMany(FollowingsFollowers, { foreignKey: 'following_id' });
FollowingsFollowers.belongsTo(Users, { foreignKey: 'follower_id' });
Users.hasMany(FollowingsFollowers, { foreignKey: 'follower_id' });
db.sync({ force: true })
.then(() => {
console.log('db synced');
})
.catch(() => {
console.log('error syncing db');
});
and this is where I try to add a Post:
const addPost = (username, post) => {
return new Promise((resolve, reject) => {
Users.findOne({ where: { username: username } })
.then((user) => {
post.user_id = user.id;
Posts.create()
.then((created) => {
resolve();
});
})
.catch((err) => {
reject(err);
});
});
};
When I call this function with a username and an object with "type" and "body" keys, a Post is created. However, the Post contains an "id", a "type", a "body", a "likesCount", a "createdAt", and an "updatedAt" fields. The "user_id" field never gets added in there.
I don't know if this is gonna solve your problem, but you don't pass any parameter to create() method. post object is passed to function, enriched with user_id and... ignored.
Shouldn't it be like this?:
(...)
Posts.create(post)
(...)
I was able to solve it. My solution was to add foreign key relations on my model files, like so:
const Sequelize = require('sequelize');
const Users = require('./users.js');
const db = require('../db.js');
const Posts = db.define('posts', {
type: Sequelize.INTEGER,
body: Sequelize.STRING,
likesCount: {
type: Sequelize.INTEGER,
defaultValue: 0
}
});
Posts.belongsTo(Users, { foreignKey: 'user_id' });
Posts.belongsTo(Posts, { foreignKey: 'parent_id' });
module.exports = Posts;
and then for the file where i sync my db, thats all i would do, like so:
const db = require('./db.js');
const Users = require('./models/users.js');
const Posts = require('./models/posts.js');
const Likes = require('./models/likes.js');
const FollowingsFollowers = require('./models/followingsfollowers.js');
db.sync({ force: true })
.then(() => {
console.log('db synced');
})
.catch(() => {
console.log('error syncing db');
});
1. Add user_id as foreign key in posts model and other models where realtion with users required.
2.Define your Posts models as:
const Posts = db.define('posts', {
type: Sequelize.INTEGER,
user_id: Sequelize.INTEGER,
body: Sequelize.STRING,
likesCount: {
type: Sequelize.INTEGER,
defaultValue: 0
}
});
3. Remove Posts.belongsTo(Posts, { foreignKey: 'parent_id' }); not required

Associations in sequelize

I've read through all the documentation and googled it a ton and I still can't figure this out. I'm trying to setup a database that allows users to be able to create an event/activity and assign multiple tags to it (indoors, food, breakfast) to categorize the events. I'm using postgres and sequelize. I'm also unsure of how I would query these events. Would I use app.get?
Currently I have an activities table and a tags table and a 3rd join table.
Tags table:
'use strict';
module.exports = (sequelize, DataTypes) => {
var Tag = sequelize.define('Tag', {
title: DataTypes.STRING
}, {
classMethods: {
associate: function(models) {
Tag.hasMany(models.Activity, { through: models.ActivityTag, foreignKey: 'tag_id' });
}
}
});
return Tag;
};
Activities table:
'use strict';
module.exports = (sequelize, DataTypes) => {
var Activity = sequelize.define('Activity', {
description: DataTypes.STRING,
location: DataTypes.STRING,
title: DataTypes.STRING,
cost: DataTypes.INTEGER
}, {
classMethods: {
associate: function(models) {
console.log(models);
Activity.belongsToMany(models.Tag, {as: 'activity' through: models.ActivityTag, foreignKey: 'activity_id' });
}
}
});
return Activity;
};
Join table:
'use strict';
module.exports = (sequelize, DataTypes) => {
var ActivityTag = sequelize.define('ActivityTag', {
activity_id: DataTypes.INTEGER,
tag_id: DataTypes.INTEGER
}, {
classMethods: {
associate: function(models) {
}
}
});
return ActivityTag;
};

Sequelize v3 — how do I make the default value for a hasMany relationship an empty list?

I am using Sequelize as my ORM. I have a simple User model as follows. A User hasMany comments, and a Comment belongsTo a user.
models/user.js (trimmed to what's relevant)
const model = (sequelize, DataTypes) => {
const User = sequelize.define('User', {
name: {
type: DataTypes.STRING
}
},
{
classMethods: {
associate: function(models) {
User.hasMany(models.Comment, {as: 'comments'})
}
}
}
)
return User
}
module.exports = model
models/comment.js (also trimmed)
const model = (sequelize, DataTypes) => {
const Comment = sequelize.define('Comment', {
text: {
type: DataTypes.TEXT
}
}, {
classMethods: {
associate: function(models) {
Comment.belongsTo(models.User, {as: 'author'})
}
}
}
)
return Comment
}
module.exports = model
I've created a test as follows (trimmed for brevity)
describe('create user given simple valid data', () => {
const userData = {
name: 'test'
}
it('creates a user', (done) => {
User.create(userData, {
include: [{model: models.Comment, as: 'comments'}]
}).then((user) => {
const userJS = user.get({plain: true})
expect(userJS).to.have.property('name')
expect(userJS).to.have.property('comments')
done()
}, done)
})
})
which results in
Unhandled rejection AssertionError: expected { Object (name, ...) } to have a property 'comments'
If I specify my userData like this however
const userData = {
name: 'test',
comments: []
}
the test passes just fine.
How can I tell Sequelize to just make an empty list of comments as the default?
Actually, this feature is not part of the models creation.
It relates to the Query when getting data.
You can use the nested:true option in include statement of findAll.
User.findAll({
include: [{
model: Comment,
as: "comments",
nested: true
}]});
source: http://docs.sequelizejs.com/manual/models-usage.html#nested-eager-loading

Categories