Fetch associations on create or save with Sequelize - javascript

Fairly simple problem, just cant find the good/clean way to do this without making a call to another find
I've got my node app rigged up with Angular-Resource, and I'm just making some round-trip like data calls on new or changed data.
So ngResource making the $save() call to my /api/users/:id and such. And Node reacts to this call by creating or finding the user, making the updates, and saving them.
Whether through create() or save(), it returns the created record, and for right now, I use res.json(user) to spill the created/returned record for my Angular to handle populating my view with the updated information
Now, I know with Sequelizes find() and findAll() methods, I can use findAll({ include: [{ all: true }]}) or specify my models individually.
What I want to know is, what is the best way to get my records associations on save/create
and unfortunately, this just doesn't work:
models.User.create(newuser, {include:[{ all: true }]}).then(function(user) {
res.json(user);
});
Do I really have to perform another find() just to get my managed models associations?

To better illustrate the opted solution from RedactedProfile's comment, here's the code.
models.User
.create(newuser, {include:[{ all: true }]})
.then(user => {
user.reload().then(user => { res.json(user); })
});

Related

Mongoose updateOne vs save methods

There are few ways to update the document record in MongoDB database.
With the User model defined I can find the user first, then modify its attribute and save it with the save() method:
let user = await User.findOne({"email": "user#email.com"});
user["email"] = "another#email.com";
await user.save();
Another option would be to use updateOne method instead of save:
await user.updateOne({"email": "another#email.com"});
Why would I choose to use the updateOne method over the save? What are the pros and cons?
I believe in your first example it requires 2 separate queries to your database, so less performant. One to find the document, and then another to save (update it). Though the benefit of doing it this way is if you have validation setup in your schema, then it would check the validation before updating.
The second one you only do one query and it's done. This however won't check your validations. Though you can opt-in by passing runValidators:true:
await user.updateOne({"email": "another#email.com"}, {"email": "another#email.com"}, { runValidators: true });

How do I filter and delete specific objects in the database? | React as Front and Rails as Back/API

So, here is the thing, I'm learning React right now, and I'm building a small taskmanager webapp with React as frontend and Rails(API) as Backend to pratice using React with Rails(which I'm already used to build other webapps with).
The tasks are objects in the database which have a "title" as a string, and "done" as a boolean to indicated if they're done or not.
Using async and await fetch('') I managed to create functions to be able to pass methods directly to the database to check and uncheck the tasks as done, and also delete the tasks. Like that, for example:
async deleteTask(task) {
if (window.confirm(`Are you sure you want to delete: "${task.title}"`)) {
await fetch(`http://localhost:3001/tasks/${task.id}`, {method: 'DELETE'});
this.props.loadTasks();
}
}
Now I want to create a function to delete ALL of the done tasks at once. How would I do that? How do I take all of the tasks and filter them by "done == true" or something like that and pass a DELETE method to the database?
Thanks in advance, any help is appreciated! I tried many alternatives, even tried something like that, no sucess:
async deleteAllDoneTasks() {
if (window.confirm(`Are you sure you want to delete all done tasks?`)) {
await fetch(`http://localhost:3001/tasks/)`, {method: 'DELETE FROM Tasks WHERE done = 1'});
this.props.loadTasks();
}
}
EDIT¹: So, according to JMadelaine this can't be made by the frontend. How do I do that in Rails then? And how do I call that in React, make it happen when I press a button?
I commented below what I would do first. I'd appreciate any insight, help, or solutions. Thanks :)
First of all you need a custom route for this action as it is not default CRUD action.
So in the routes.rb under the tasks route add custom route like:
delete 'tasks', to: 'tasks#multiple_delete'
Then in TasksController define multiple_delete method with code inside which will delete desired tasks and return answer to your client, something like:
def multiple_delete
::Task.where(done: true).destroy_all
head :ok
end
and then in your React, according to answer from server you can do some things.
Then go to your React code, on desired button add onClick event (I do not know which framework you use, I will share example of Shopify code which I am familiar with.
outside the render() do:
deleteAllDoneTasks = () => {
if (window.confirm(`Are you sure you want to delete all done tasks?`)) {
await fetch(`http://localhost:3001/tasks)`, {method: 'DELETE'})
}
}
inside the render() do:
<Button onClick={this.deleteAllDoneTasks}>

Deleting data from associated tables using knex.js

I want to delete from an articles table using knex by article_id. This already exists in comments table as a foreign key.
How can I test that data has been deleted and how can I send that to the user.
I decided to approach this by writing a function to delete from both functions with a .then. Does this look like I am on the right lines?
exports.deleteArticleById = function (req, res, next) {
const { article_id } = req.params;
return connection('comments')
.where('comments.article_id', article_id)
.del()
.returning('*')
.then((deleted) => {
console.log(deleted);
return connection('articles')
.where('articles.article_id', article_id)
.del()
.returning('*');
})
.then((article) => {
console.log(article);
return res.status(204).send('article deleted');
})
.catch(err => next(err));
};
At the moment I am getting the correct data with the logs but I am getting a status 500 but I think I need to be trying to get a 204?
Any help would be much appreciated.
What you're trying to do is called a cascading deletion.
These are better (and almost always) handled at the database level instead of the application level.
It's the job of the DBMS to enforce this kind of referential integrity assuming you define your schema correctly so that entities are correctly linked together, via foreign keys.
In short, you should define your database schema as such that when you delete an Article, it's associated Comments also get deleted for you.
Here's how I would do it using knex.js migrations:
// Define Article.
db.schema.createTableIfNotExists('article', t => {
t.increments('article_id').primary()
t.text('content')
})
// Define Comment.
// Each Comment is associated with an Article (1 - many).
db.schema.createTableIfNotExists('comment', t => {
t.increments('comment_id').primary() // Add an autoincrement primary key (PK).
t.integer('article_id').unsigned() // Add a foreign key (FK)...
.references('article.article_id') // ...which references Article PK.
.onUpdate('CASCADE') // If Article PK is changed, update FK as well.
.onDelete('CASCADE') // If Article is deleted, delete Comment as well.
t.text('content')
})
So when you run this to delete an Article:
await db('article').where({ article_id: 1 }).del()
All Comments associated with that Article also get deleted, automatically.
Don't try to perform cascading deletions yourself by writing application code. The DBMS is specifically designed with intricate mechanisms to ensure that deletions always happen in a consistent manner; It's purpose is to handle these operations for you. it would be wasteful, complicated and quite error-prone to attempt to replicate this functionality yourself.

Pushing new item to a mongoDB document array

I've looked through a bunch of other SO posts and have found different ways to do this, so I'm wondering which is most preferred. I'm teaching this to students, so I want to give them best practices.
If I have the following BlogPost object (Simplified):
var BlogPostSchema = new mongoose.Schema({
body: String,
comments: [String]
});
and I want to add a new comment to the array of comments for this blog, I can think of at least 3 main ways to accomplish this:
1) Push the comment to the blog object in Angular and submit a PUT request to the /blogs/:blogID endpoint, updating the whole blog object with the new comment included.
2) Submit a POST request to a /blogs/:blogID/comments endpoint where the request body is just the new comment, find the blog, push the comment to the array in vanilla js, and save it:
BlogPost.findById(req.params.blogID, function(err, blogPost) {
blogPost.comments.push(req.body);
blogPost.save(function(err) {
if (err) return res.status(500).send(err);
res.send(blogPost);
});
});
OR
3) Submit the POST to a /blogs/:blogID/comments endpoint with the request body of the new comment, then use MongoDB's $push or $addToSet to add the commend to the array of comments:
BlogPost.findByIdAndUpdate(
req.params.blogID,
{$push: {comments: req.body}},
{safe: true, new: true},
function(err, blogPost) {
if (err) return res.status(500).send(err);
res.send(blogPost);
});
});
I did find this stackoverflow post where the answerer talks about option 2 vs. option 3 and basically says to use option 2 whenever you can, which does seem simpler to me. (And I usually try to avoid methods that stop me from being able to use hooks and other mongoose goodies.)
What do you think? Any advice?
From application point of view, point 3 is better. The reason I think are.
The query itself specifies what we are trying to achieve. it's
easily readable.
save function is a wild card, so we don't know what it's going to change.
if you fetch the document and manipulate it and then call save it, there is outside but real chance that you might mess up some
other field of the document in process of manipulation
unintentionally, not the case with point 3.
In case of addToSet,basically the previous point is more visible.
Think about the concurrency, if multiple calls comes with different comment for same blog and you are trying option 2, there
is a chance that you might override the changes which were done in
between you fetched the document and when you are saving it. Option
3 is better in that sense.
Performance wise they both do the same thing, so there might not be much or any visible difference. But option 3 is bit safer and cleaner.

How to update the whole Backbone.js collection that in database?

I have a collection (an object list) in database. I can fetch it like: collectionModel.fetch()
But then user changes something on that collection. When user clickes on save button, the whole collection list must be update in database. I thought maybe i can delete() the old one first and then create() it with new one but i could'n achive it. I can't use the update() method because in this case i should find which collection elements has changed but i want to update whole list. How can i do that? Thanks for help.
Do you have a REST api in front of that database? That's how Backbone is made to work with. When your JavaScript code runs model.save(); a PUT request is made to your api for that model.
You question is about saving the whole collection, for that if you want to remain within the default implementation of Backbone you will have to go over all the models in the collection and call save for each of them.
If you want to make one single request to your server you will have to implement a custom method inside your collection. Something like:
MyCollection = Backbone.Collection.extend({
saveAll: function() {
var data = this.toJSON();
return Backbone.$.ajax({
data: { objects: data },
url: '/url/in/your/server/to/update/db'
});
}
});
That's going to send the array of all models in your collection converted to JSON to your server.
Again, you want to have a RESTful API on the server side if you want to make your life with Backbone easy.
If you want to reset collection you have to specify "reset" attribute.
collectionList.fetch({
reset: true,
...
});
But I think it's better to just update it:
collectionList.fetch({
remove: false,
update: true,
merge: true,
...
});
This is a very old question, but I had another approach so I thought I'd post it.
Sometimes my collections have a lot of data and the server doesn't get it all. I solved this by using one of the underscore methods that backbone collections have, invoke (also relies on jquery):
MyCollection = Backbone.Collection.extend({
update: function(callback) {
// Invoke the update method on all models
$.when.apply($, this.invoke('update')).then(() => {
// After complete call the callback method (if passsed)
if(callback) {
callback();
}
});
}
});
You can use it by calling collection.update() when the collection has models in it. A similar method can be used for creating or deleting collections, and this should be modifiable to catch errors but I didn't account for that.

Categories