ORM: обновление всех моделей данного экземпляра

#javascript #orm #sequelize.js

#javascript #orm #sequelize.js

Вопрос:

У меня возникла концептуальная проблема с тем, как обновить все экземпляры модели после ее обновления.

Представьте себе следующий метод renameUser (может быть любой ORM):

 async function renameUser(userId: number, newUsername: string) {
  const user = await User.findByPk(userId);
  await user.update({ username: newUsername });
}

  

И следующее использование:

 const user = await User.create({ username: "old" });
// user.username === "old"

this.renameUser(user.id, "new");

// still, user.username === "old"
  

Очевидно, что этой проблемы не было бы, если бы я передавал user объект непосредственно в метод update, но это работает только в этом простом примере — в моем случае фактически невозможно использовать один и тот же экземпляр, поскольку он может быть изменен с помощью перехватов в совершенно другом контексте.

Итак, одним из простых решений было бы вызвать user.reload() после вызова, который будет извлекать последние пользовательские данные из базы данных:

 const user = await User.create({ username: "old" });
// user.username === "old"

this.renameUser(user.id, "new");

user.reload();
// now: user.username === "new"
  

Однако для этого мне нужно знать, что renameUser метод изменит мой пользовательский объект. В этом случае это очевидно, но если вызывается метод, это не всегда возможно.

Есть ли какой-либо шаблон, который я могу использовать для решения этой проблемы?Мне пришло в голову создать a UserFactory , который гарантирует, что у меня есть только один экземпляр пользователя (индексируемый его первичным ключом) в любое время, а затем обновить этот экземпляр. Но мне было интересно, как другие решают это? Это распространенная проблема?

Ответ №1:

Почему вы не используете .save() updateUser функцию in?

 const renameUser = async (userId, newUsername) => {
  const user = await User.findByPk(userId);

  user.username = newUsername;
  await user.save();

  return user;
}
  

и используйте это так

 const user = await User.create({ username: "old" });
// user.username === "old"

user = await this.renameUser(user.id, "new");

// now, user.username === "new"
  

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

1. См. Мой вопрос: «Очевидно, что этой проблемы не было бы, если бы я передавал объект user непосредственно в метод update, но это работает только в этом простом примере — в моем случае фактически невозможно использовать один и тот же экземпляр, поскольку он может быть изменен с помощью перехватов в совершенно другом контексте.»

Ответ №2:

Вы можете выполнять свои запросы более эффективно, просто запустив обновление с помощью Model.update() функции вместо запроса экземпляра и последующего его обновления.

 async function renameUser(userId: number, newUsername: string) {
  
  const [ updatedRowCount, updateRowOnPostgresOnly ] = await User.update({
    username: newUsername,
  }, {
    where: {
      id: userId,
    },
  });
  // you can use the updated row count to see if the user changed.
  const isUpdated = updatedRowCount > 0;
  return isUpdated;
}
  

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

 const user = await User.create({ username: "old" });
// user.username === "old"

const isUpdated = await this.renameUser(user.id, "new");

if (isUpdated) {
  user.reload();
  // now: user.username === "new"
}
  

Обратите внимание, что в вашем примере вы не используете await async runameUser() функцию on.

Это немного отличается от вашего вопроса, но если вы уже работаете с экземплярами модели, то вы можете использовать Instance.changed() для получения измененных полей.

 const user = await User.create({ username: "old" });
user.username = "old";
const changed = user.changed(); // == [ "username" ]
if (changed.length) {
  await user.save();
}
  

* Обратите внимание, что в этом примере он проверяет changed.length , но на самом деле Sequelize ничего не сделает, если вы await instance.save() и ничего instance.changed() измените осведомленность о сохранении.