Как связать объекты в Sequelize (узел js)?

#javascript #node.js #postgresql #sequelize.js

#javascript #node.js #postgresql #sequelize.js

Вопрос:

У меня есть пользовательские и файловые модели в Sequlize. У пользователя может быть несколько файлов. У меня есть ассоциация db.User.hasMany(db.File, {as: ‘Files’, ForeignKey: ‘userId’, ограничения: false});

Я хочу инициализировать пользовательский объект с несколькими файлами и сохранить его в базе данных.

Я написал следующий код:

  var files = [];

        var file1 = models.File.build();
        file1.name = "JPEG";

        file1.save().then(function () {

        });

        files.push(file1);

        var file2 = models.File.build();
        file2.name = "PNG";

        file2.save().then(function () {

        });

        files.push(file2);


 var newUser = models.User.build();
       newUser.email = email;
 newUser.save().then(function (usr) {

 files.forEach(function (item) {
     newUser.addFile(item);
});

});
  

Но я обнаружил ошибку, иногда несколько файлов не были связаны с пользователем.

Я нашел (в журналах nodejs) команды (update) для установки внешних ключей для этих файлов. Но команды не были выполнены. Внешние ключи (userId) нескольких файлов были пустыми.

Я думаю, что проблема в асинхронных запросах. Как организовать структуру кода, чтобы избежать этой ошибки?

Комментарии:

1. почему вы не создаете файлы после пользователя и не добавляете идентификатор пользователя в определение file?

Ответ №1:

Проблема именно в том, что вы думаете, в асинхронном коде.

Вам нужно переместить функции внутри обратных вызовов, иначе код будет запущен до создания файла.

JavaScript не ждет, прежде чем перейти к следующей строке, поэтому он запустит следующую строку, независимо от того, сделано это или нет. Нет смысла ждать перед перемещением.

Таким образом, вы в основном добавляете что-то, что еще не существует, потому что оно не ожидает сохранения файла, прежде чем двигаться дальше.

Это сработало бы, просто переместив код внутри обратных then вызовов:

 var files = [];

var file1 = models.File.build();
file1.name = "JPEG";

file1.save().then(function () {
    files.push(file1);

    var file2 = models.File.build();
    file2.name = "PNG";

    file2.save().then(function () {
        files.push(file2);

        var newUser = models.User.build();
        newUser.email = email;
        newUser.save().then(function (usr) {
            files.forEach(function (item) {
                newUser.addFile(item);
            });
        });
    });
});
  

Но это грязно. Вместо этого вы можете связать обещания следующим образом:

 var files = [];

var file1 = models.File.build();
file1.name = "JPEG";

file1.save()
  .then(function(file1) {
      files.push(file1);

      var file2 = models.File.build();
      file2.name = "PNG";

      return file2.save();
  })
  .then(function(file2) {
      files.push(file2);

      var newUser = models.User.build();
      newUser.email = email;

      return newUser.save();
  })
  .then(function(newUser) {
       files.forEach(function(item) {
           newUser.addFile(item);
       });
  });
  

Теперь это немного чище, но все еще немного запутанно и немного сбивает с толку. Таким образом, вы можете использовать функции генератора вместо этого следующим образом:

 var co = require('co');

co(function*() {
    var files = [];

    var file1 = models.File.build();
    file1.name = "JPEG";

    yield file1.save();
    files.push(file1);

    var file2 = models.File.build();
    file2.name = "PNG";

    yield file2.save();
    files.push(file2);

    var newUser = models.User.build();
    newUser.email = email;
    newUser.save();

    files.forEach(function(item) {
        newUser.addFile(item);
    });
});
  

Теперь это намного лучше.

Посмотрите внимательно, и вы увидите, что происходит. co принимает функцию генератора, которая в основном является обычной функцией со звездочками * . Это специальная функция, которая добавляет yield поддержку выражений.

yield выражения в основном ожидают then() вызова обратного вызова, прежде чем двигаться дальше, и если у then обратного вызова есть аргумент, он также вернет его.

Итак, вы можете сделать что-то вроде:

 var gif = yield models.File.create({
  name: 'gif'
});
  

вместо:

 models.File.create({
  name: 'gif'
}).then(function(gif) {

});
  

Вы должны использовать небольшой модуль узла, вызываемый co для этого, хотя просто npm install --save co