AkAk47
AkAk47

Reputation: 291

Sequelize save Data in belongsToMany created Table and Read from it(Solution at the End)

I am using for my Backend Node.js,Express.js and Sequelize for the connection to the Database. I have a n:m Relationship between Tasks and Keys. schema

Tasks and Keys is created by me and TaskKey through Sequelize with:

Backend

    // Tasks n:m Keys
    db.DictKey.belongsToMany(db.Task, { through: 'TaskKeys' , foreignKey:'key_id'});
    db.Task.belongsToMany(db.DictKey, { through: 'TaskKeys' , foreignKey: 'task_id'});

Now if I create a new Task on the

frontend

$scope.create = () => {
    this.$http.post('/api/tasks', {
        user_id: $scope.selectUser,
        lang_id: $scope.selectLang,
        name: $scope.newTask
    });
}

I want to send with that request an Array of all Keys the User selected.

In the Backend it should add an entry to TaskKeys for the new Task for every DictKey sent. For example

Table Task:

ID | some values
1  | some values | this is the new task created

Send Keys in Array[2,5,6]

TaskKey / in the same moment create in this Table the Dependant Keys

TaskID | KeyID
1      | 2
1      | 5
1      | 6

How can I achieve that ?

After that I would like to show a Task. Taking the Example before. Get task where id = 1 ng-repeat all datas and as well get all Keys thanks to the Table TaskKey.

I could not find a Example which explains that and the only solution I have at the moment is at the front end with $http.post(taskkey) with a foreach for every key in the Table TaskKey. But later in the Live System there would be over 1000 keys, so this isn't an acceptable solution. Is there a good solution for that for backend?

Edit1:

...    working fine
$scope.create = () => {
            this.$http.post('/api/tasks', {
              task:{
              user_id: $scope.selectUser,
              lang_id: $scope.selectLang,
              name: $scope.newTask
            },
            keys:[1,2,3,4,5]
          });
    ...
 ... 
// Creates a new Task in the DB
export function create(req, res) {
  return Task.create(req.body.task)
  .then(function(task){
    return task.addTaskKeys(req.body.keys);//throws 500error
    //console.log(req.body.keys);//working fine getting the Keys
  })
    .then(respondWithResult(res, 201))
    .catch(handleError(res));
}
...

Reading the docs belongsToManyDoc doesnt help much because no Examples or Detailed Explained. Ive tried several things like:

In my backend I dont have any Functions for TaskKeys only for Tasks and Keys. But as I understood I dont need any for TaskKeys because with

 // Tasks n:m Keys
    db.DictKey.belongsToMany(db.Task, { through: 'TaskKeys' , foreignKey:'key_id'});
    db.Task.belongsToMany(db.DictKey, { through: 'TaskKeys' , foreignKey: 'task_id'});  

creating Middle Table there is an association between Tasks and Keys. So normally add/create should just work and it should add instances in TaskKeys.

 export function create(req, res) {
      return Task.create(req.body.task)
      .then(function(task){
        return task.addTaskKeys(req.body.keys);//throws 500error

with task.addTaskKeys adding Associations with the just created Task=> task_id

with req.body.keys giving him the Keys =>key_id
If I take the Example from Doc change to minde DB:

... example
    Project.create({ id: 11 }).then(function (project) {
      user.addProjects([project, 12]);
    });
... mine
return Task.create(req.body.task)
  .then(function(task){
    return DictKey.addTasks([task,{key_id : 1}]);//still error
  })//DictKey.addTask(task,1); still error

Reading the BelongsToMany Association and Quoting it:

user.addProject(project, { status: 'started' }) By default the code will add projectId and userId to the UserProjects table

Trying out creating with Associations:

http://sequelize.readthedocs.io/en/latest/docs/associations/#creating-elements-of-a-hasmany-or-belongstomany-association Sorry not allowed to post more as 1 Link.

export function create(req, res) {
  return Task.create({
    name: req.body.task.name,
    user_id: req.body.task.user_id,
    lang_id: req.body.task.lang_id,
    key_id:[req.body.keys]
  },{
    include: [DictKey]
  })

No Error but only Creating Task not TaskKeys.... So what Iam doing wrong whole time?

Edit2: For Testing I inserted Data in the Table TaskKeys by Myself in MsSql. TaskKey / Self Created Datas with SqlQuery

TaskID | KeyID
1      | 1
1      | 2
1      | 3
2      | 4
2      | 5

getting it from my frontend

this.$http.get('/api/tasks/' +2 )
            .then(response => {
              this.Tasks = response.data;
              console.log(this.Tasks);
            });

backend

// Gets a single Task from the DB
export function show(req, res) {
  return Task.findAll({
    where: {
      _id: req.params.id
    },
    include: [{
      model: DictKey
    }]
  })
    .then(handleEntityNotFound(res))
    .then(respondWithResult(res))
    .catch(handleError(res));
}

ConsoleOutput:

[Object]
Object
_id:2 // the searched TaskId= 2 => right
dict_Keys:Array[2] // the Dependant 2 KeyIds => right

So the table which is created is working fine. The Question is now only why adding through Backend with addKey is not working by doing the same as in the Example. Trying the Example to check Associations:

// Creates a new Task in the DB
export function create(req, res) {
  Task.create({
  name: req.body.task.name,
  user_id: req.body.task.user_id,
  lang_id: req.body.task.lang_id
}).then(function(task) {
    return DictKey.create({ name:req.body.task.name,notice:req.body.task.name, user_id:req.body.task.user_id })
    .then(function(key) {
      return task.hasDictKey(key).then(function(result) {
        // result would be false
        return task.addDictKey(key).then(function() {
          return task.hasDictKey(key).then(function(result) {
            // result would be true
          })
        })
      })
    })
  })

Throws task.hasDictKey is not a function

db.Task.create({
  name: 'test',
  user_id: 266,
  lang_id: 9,
  key_id:[1,2]
},{
  include: [db.DictKey]
})

Throws dict_Keys is not associated to Task So it means they are not Connected together? BUt Reading data and Inserting Data in the Database is working?

Just Adding now the Example of Sequelize:

var Usert = db.sequelize.define('usert', {})
var Project = db.sequelize.define('project', {})
var UserProjects = db.sequelize.define('userProjects', {
    status: Sequelize.STRING
})

Usert.belongsToMany(Project, { through: UserProjects })
Project.belongsToMany(Usert, { through: UserProjects })
Usert.addProject(Project, { status: 'started' })//addProject is not a Function // for real ? now not even their own is not working? :/

Answer
By Reading the Docs I thought with

// Tasks n:m Keys
db.DictKey.belongsToMany(db.Task, { through: TaskKeys , foreignKey:'key_id',otherKey:'task_id'});
db.Task.belongsToMany(db.DictKey, { through: TaskKeys , foreignKey: 'task_id',otherKey: 'key_id'});

it would automatically generate add/set/get/create Task for DictKey and add/set/get/create DictKey for Task.

But

Naming strategy

By default sequelize will use the model name (the name passed to sequelize.define) to figure out the name of the model when used in associations.

Means it adds Functions by the name of the Table Model where you go define('dict_Keys'... so add/set/get/ Dict_Key to Task. That was the problem because I was using addDictkey instead of addDict_Key. I hope it helps someone else in the Future

Upvotes: 3

Views: 4160

Answers (2)

felixfbecker
felixfbecker

Reputation: 2363

foreignKey should be the same on both sides of the relation, to set the other key use the otherKey option.

Upvotes: 0

Will
Will

Reputation: 2191

Frontend: To get the data from the front end, just include the array as a property of the data you're posting. You might want to wrap the task specific properties in their own object as well:

this.$http.post('/api/tasks', {
    task: {
        user_id: $scope.selectUser,
        lang_id: $scope.selectLang,
        name: $scope.newTask
    },
    keys: [ 2, 5, 6 ]
});

Backend: Sequelize and other ORMs try to provide you with tools for using the relationships you've defined. Use them. You're already using belongsToMany, so you just need to do the next step of using your created Task object to create the m:n entries in TaskKeys:

...
return Task.create(req.body.task)
    .then(function(task) {
        return task.addProjects(req.body.keys);
    })
    .then(respondWithResult(res, 201))
    .catch(handleError(res));
...

As for getting a task with keys, just use the include option:

...
return Task.findById(req.params.id, {
    include: [ { model: db.DictKey } ]
})
...

Upvotes: 2

Related Questions