web-dev-qa-db-fra.com

Déclencheur de mise à jour automatique Knex.JS

J'utilise Knex.JS des outils de migration. Cependant, lors de la création d'une table, j'aimerais avoir une colonne nommée updated_at qui est automatiquement mis à jour lorsqu'un enregistrement est mis à jour dans la base de données.

Par exemple, voici un tableau:

knex.schema.createTable('table_name', function(table) {
    table.increments();
    table.string('name');
    table.timestamp("created_at").defaultTo(knex.fn.now());
    table.timestamp("updated_at").defaultTo(knex.fn.now());
    table.timestamp("deleted_at");
})

Le created_at et updated_at la colonne par défaut correspond à l'heure de création de l'enregistrement, ce qui est correct. Mais, lorsque cet enregistrement est mis à jour, j'aimerais que le updated_at colonne pour afficher la nouvelle heure à laquelle elle a été mise à jour automatiquement.

Je préfère ne pas écrire en postgres bruts.

Merci!

20
aashah7

La réponse de Benny est tout à fait correcte. Cependant, je remarque que vous mentionnez spécifiquement Postgres, auquel cas sa syntaxe knex.raw Ne fonctionnera pas pour vous. Voici une méthode que j'ai utilisée avec succès.

Ajouter une fonction

Si vous avez plusieurs fichiers de migration dans un ordre défini, vous devrez peut-être modifier artificiellement l'horodatage dans le nom de fichier pour que cela s'exécute en premier (ou simplement l'ajouter à votre premier fichier de migration). Si vous ne pouvez pas revenir en arrière, vous devrez peut-être effectuer cette étape manuellement via psql. Cependant, pour les nouveaux projets:

const ON_UPDATE_TIMESTAMP_FUNCTION = `
  CREATE OR REPLACE FUNCTION on_update_timestamp()
  RETURNS trigger AS $$
  BEGIN
    NEW.updated_at = now();
    RETURN NEW;
  END;
$$ language 'plpgsql';
`

const DROP_ON_UPDATE_TIMESTAMP_FUNCTION = `DROP FUNCTION on_update_timestamp`

exports.up = knex => knex.raw(ON_UPDATE_TIMESTAMP_FUNCTION)
exports.down = knex => knex.raw(DROP_ON_UPDATE_TIMESTAMP_FUNCTION)

Maintenant, la fonction devrait être disponible pour toutes les migrations suivantes.

Définissez un assistant de déclenchement knex.raw

Je trouve plus expressif de ne pas répéter de gros morceaux de SQL dans les fichiers de migration si je peux l'éviter. J'ai utilisé knexfile.js Ici mais si vous n'aimez pas compliquer cela, vous pouvez le définir n'importe où.

module.exports = {
  development: {
    // ...
  },

  production: {
    // ...
  },

  onUpdateTrigger: table => `
    CREATE TRIGGER ${table}_updated_at
    BEFORE UPDATE ON ${table}
    FOR EACH ROW
    EXECUTE PROCEDURE on_update_timestamp();
  `
}

Utilisez l'aide

Enfin, nous pouvons définir assez facilement les déclencheurs de mise à jour automatique:

const { onUpdateTrigger } = require('../knexfile')

exports.up = knex =>
  knex.schema.createTable('posts', t => {
    t.increments()
    t.string('title')
    t.string('body')
    t.timestamps(true, true)
  })
    .then(() => knex.raw(onUpdateTrigger('posts')))

exports.down = knex => knex.schema.dropTable('posts')

Notez que la suppression de la table suffit pour se débarrasser du déclencheur: nous n'avons pas besoin d'un DROP TRIGGER Explicite.

Tout cela peut sembler être beaucoup de travail, mais c'est assez "réglé et oublié" une fois que vous l'avez fait et pratique si vous voulez éviter d'utiliser un ORM.

20
Rich Churcher

Vous pouvez créer une migration knex en utilisant horodatages :

exports.up = (knex, Promise) => {
  return Promise.all([
    knex.schema.createTable('table_name', (table) => {
      table.increments();
      table.string('name');
      table.timestamps(false, true);
      table.timestamp('deleted_at').defaultTo(knex.fn.now());
    })
  ]);
};

exports.down = (knex, Promise) => {
  return Promise.all([
    knex.schema.dropTableIfExists('table_name')
  ]);
};

Avec horodatages un schéma de base de données sera créé qui ajoutera un created_at et updated_at colonne, chacune contenant un horodatage initial.

Pour conserver le updated_at colonne actuelle, vous aurez besoin de knex.raw:

table.timestamp('updated_at').defaultTo(knex.raw('CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP'));

Pour ignorer le knex.raw solution, je suggère d'utiliser un ORM de haut niveau comme Objection.js . Avec Objection.js vous pouvez implémenter votre propre BaseModel qui met alors à jour le updated_at colonne:

Something.js

const BaseModel = require('./BaseModel');

class Something extends BaseModel {
  constructor() {
    super();
  }

  static get tableName() {
    return 'table_name';
  }
}

module.exports = Something;

BaseModel

const knexfile = require('../../knexfile');
const knex = require('knex')(knexfile.development);
const Model = require('objection').Model;

class BaseModel extends Model {
  $beforeUpdate() {
    this.updated_at = knex.fn.now();
  }
}

module.exports = BaseModel;

Source: http://vincit.github.io/objection.js/#timestamps

7
Benny Neugebauer

Ce n'est pas une fonctionnalité de Knex. Knex crée uniquement les colonnes, mais ne les tient pas à jour pour vous.

Si vous utilisez, l'ORM Bookshelf, cependant, vous pouvez spécifier qu'une table a des horodatages, et elle définira et mettra à jour les colonnes comme prévu:

0
Swaraj

C'est ma façon de le faire dans Mysql 5.6+

La raison pour laquelle je n'ai pas utilisé table.timestamps est que j'utilise DATETIME au lieu de timestamp.

table.dateTime('created_on')
        .notNullable()
        .defaultTo(knex.raw('CURRENT_TIMESTAMP'))

table.dateTime('updated_on')
        .notNullable()
        .defaultTo(knex.raw('CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP'))
0
Alex Rizvi