パラノイド
Sequelizeは、パラノイドテーブルの概念をサポートしています。パラノイドテーブルとは、レコードの削除を指示されたときに、実際には削除しないテーブルのことです。代わりに、deletedAtという特別な列に、削除リクエストのタイムスタンプが設定されます。
これは、パラノイドテーブルが、ハード削除ではなく、レコードのソフト削除を実行することを意味します。
モデルをパラノイドとして定義する
モデルをパラノイドにするには、モデル定義にparanoid: trueオプションを渡す必要があります。パラノイドはタイムスタンプが動作するために必要です(つまり、timestamps: falseも渡すと動作しません)。
また、デフォルトの列名(deletedAt)を別のものに変更することもできます。
class Post extends Model {}
Post.init(
  {
    /* attributes here */
  },
  {
    sequelize,
    paranoid: true,
    // If you want to give a custom name to the deletedAt column
    deletedAt: 'destroyTime',
  },
);
削除
destroyメソッドを呼び出すと、ソフト削除が行われます。
await Post.destroy({
  where: {
    id: 1,
  },
});
// UPDATE "posts" SET "deletedAt"=[timestamp] WHERE "deletedAt" IS NULL AND "id" = 1
モデルがパラノイドの場合、本当にハード削除したい場合は、force: trueオプションを使用して強制できます。
await Post.destroy({
  where: {
    id: 1,
  },
  force: true,
});
// DELETE FROM "posts" WHERE "id" = 1
上記の例では、静的destroyメソッド(Post.destroy)を例として使用しましたが、インスタンスメソッドでもすべて同じように動作します。
const post = await Post.create({ title: 'test' });
console.log(post instanceof Post); // true
await post.destroy(); // Would just set the `deletedAt` flag
await post.destroy({ force: true }); // Would really delete the record
復元
ソフト削除されたレコードを復元するには、静的バージョンとインスタンスバージョンの両方にあるrestoreメソッドを使用できます。
// Example showing the instance `restore` method
// We create a post, soft-delete it and then restore it back
const post = await Post.create({ title: 'test' });
console.log(post instanceof Post); // true
await post.destroy();
console.log('soft-deleted!');
await post.restore();
console.log('restored!');
// Example showing the static `restore` method.
// Restoring every soft-deleted post with more than 100 likes
await Post.restore({
  where: {
    likes: {
      [Op.gt]: 100,
    },
  },
});
他のクエリでの動作
Sequelizeによって実行されるすべてのクエリは、ソフト削除されたレコードを自動的に無視します(もちろん、rawクエリは除きます)。
これは、たとえば、findAllメソッドがソフト削除されたレコードを認識せず、削除されていないレコードのみをフェッチすることを意味します。
ソフト削除されたレコードのプライマリキーを指定してfindByPkを呼び出すだけでも、そのレコードが存在しないかのように、結果はnullになります。
本当にクエリでソフト削除されたレコードを表示させたい場合は、クエリメソッドにparanoid: falseオプションを渡すことができます。例えば
await Post.findByPk(123); // This will return `null` if the record of id 123 is soft-deleted
await Post.findByPk(123, { paranoid: false }); // This will retrieve the record
await Post.findAll({
  where: { foo: 'bar' },
}); // This will not retrieve soft-deleted records
await Post.findAll({
  where: { foo: 'bar' },
  paranoid: false,
}); // This will also retrieve soft-deleted records