Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Updating attributes in associated models using Sequelize

Is it possible to update attributes on both the parent model and the associated models all in one go? I am having trouble getting it to work and haven't been able to find any full examples. I'm not sure if it's something wrong with my code or if it wasn't intended to work the way I would expect. I tried adding the onUpdate : 'cascade' to my hasMany definition, but that didn't seem to do anything.

Models:

module.exports = function( sequelize, DataTypes ) {
var Filter = sequelize.define( 'Filter', {
    id : {
            type : DataTypes.INTEGER,
            autoIncrement : true,
            primaryKey : true
         },
        userId : DataTypes.INTEGER,
        filterRetweets : DataTypes.BOOLEAN,
        filterContent : DataTypes.BOOLEAN
    },
    {
        tableName : 'filter',
        timestamps : false
    }
);

var FilteredContent = sequelize.define( 'FilteredContent', {
        id : {
                type : DataTypes.INTEGER,
                autoIncrement : true,
                primaryKey : true
        },
        filterId : {
                        type : DataTypes.INTEGER,
                        references : "Filter",
                        referenceKey : "id"
        },
        content : DataTypes.STRING
    },
    {
        tableName : "filteredContent",
        timestamps : false
    }
);

Filter.hasMany( FilteredContent, { onUpdate : 'cascade', as : 'filteredContent', foreignKey : 'filterId' } );
sequelize.sync();

    return {
        "Filter" : Filter,
        "FilteredContent" : FilteredContent
    };
}

Retrieving the filter and trying to update an attribute on the associated FilteredContent object:

Filter.find({   where: { id: 3 }, 
            include: [ { model : FilteredContent, as : 'filteredContent' } ] 
}).success ( function( filter ) {
    var filteredContent = FilteredContent.build( {
        filterId : filter.id,
        id : 2,
        content : 'crap'
    });
    filter.save();
});

This results in only attributes in the Filter object being updated. How do I get it to also update the attributes in FilteredContent?

Also, is the sequelize.sync() necessary after defining my models? I'm not clear on what exactly it is supposed to do. I am able to retrieve my object with associations without it. I added it to my code in desperation to get the updates working, but I'm not sure if it's actually necessary.

Thanks

like image 955
jsparks Avatar asked Mar 23 '14 03:03

jsparks


People also ask

How do I update associations in Sequelize?

Creating associations in sequelize is done by calling one of the belongsTo / hasOne / hasMany / belongsToMany functions on a model (the source), and providing another model as the first argument to the function (the target). hasOne - adds a foreign key to the target and singular association mixins to the source.

How do I sync models in Sequelize?

A model can be synchronized with the database by calling model.sync(options) , an asynchronous function (that returns a Promise). With this call, Sequelize will automatically perform an SQL query to the database. Note that this changes only the table in the database, not the model in the JavaScript side.

How do you update multiple records in Sequelize?

When you need to update the rows using the id , you can change the where option as follows: await User. update( { status: "active" }, { where: { id: [1, 2, 3], }, } ); The update() method is able to update multiple rows with the same update values.


1 Answers

To your question:

When you eagerly load FilteredContent (using include), a model instance is already built, so there is no reason to call build. Something along the lines of this should do what you want:

Filter.find({
  where: { id: 3 }, 
  include: [ { model : FilteredContent, as : 'filteredContent' } ] 
}).then ( function( filter ) {
  return filter.filteredContent[0].updateAttributes({
    content: 'crap'
  })
}).then(function () {
  // DONE! :)
});

A couple of pointers about the code you posted as a whole:

  • sequelize.sync creates database tables for your models, if they don't already exist. It is not required for what you want to do if your tables already exist
  • sequelize.sync is an async operation, so doing sequelize.sync without attaching a callback is not advisable. Furthermore it looks like you are doing sync in a model definition - you should only do it once, preferrably in the place where you define your models.
  • It looks like you defining several models in one file - you should only define one in each file. The associations could be set up by doing sequelize.import([path to filter model) in your FilterContent file, or by doing all associations in the place you import your models into your app.

edit to answer your comment:

You cannot do a single function call that will update both filter and filteredcontent, but you don't have to do the updates in sequence either. You can issue all the update commands without waiting for them to complete.

Filter.find({
  where: { id: 3 }, 
  include: [ { model : FilteredContent, as : 'filteredContent' } ] 
}).then ( function( filter ) {
  return Promise.all([
    filter.updateAttributes({}),
    filter.filteredContent.map(fc => fc.updateAttributes({}))
  ]);
}).spread(function (filter, filteredContents) {

})

In this way all queries will run in parallel, and your then function will be called when all of them have completed. Notice that I've used spread here to turn the array returned from Promise.all into separate arguments.

like image 148
Jan Aagaard Meier Avatar answered Oct 22 '22 04:10

Jan Aagaard Meier