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!
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.
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.
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();
`
}
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.
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;
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:
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'))