如何创建 knex 迁移以根据其他列创建列?

how to create a knex migration for creating a column depending on other columns?

我有一个 Node.js 项目,我正在使用 knex.js 作为查询构建器。

我有一个名为长方体的模型:

import { Id, RelationMappings } from 'objection';
import { Bag } from './Bag';
import Base from './Base';

export class Cuboid extends Base {
  id!: Id;
  width!: number;
  height!: number;
  depth!: number;
  bagId?: Id;
  bag!: Bag;
  volume!: number;

  static tableName = 'cuboids';

  static get relationMappings(): RelationMappings {
    return {
      bag: {
        relation: Base.BelongsToOneRelation,
        modelClass: 'Bag',
        join: {
          from: 'cuboids.bagId',
          to: 'bags.id',
        },
      },
    };
  }
}

export default Cuboid;

我还有一个迁移,我在其中为长方体创建了一些列 table:

import { Bag, Cuboid } from '../../src/models';
import { Knex } from 'knex';

export const up = (knex: Knex): Promise<void> =>
  knex.schema.createTable(Cuboid.tableName, (table: Knex.TableBuilder) => {
    table.increments();
    table.timestamps();
    table.integer('width');
    table.integer('height');
    table.integer('depth');
    table.integer('bagId');
    table.foreign('bagId').references('id').inTable(Bag.tableName);
  });

export const down = (knex: Knex): Promise<void> =>
  knex.schema.dropTable(Cuboid.tableName);

我想创建一个新的迁移,我在其中定义了一个名为 'volume' 的列。体积需要是宽度、高度和深度列的乘积(体积 = 宽度 * 高度 * 深度)。

import { Knex } from 'knex';
import { Cuboid } from '../../src/models';

export const up = (knex: Knex): Promise<void> =>
  knex.schema.alterTable(Cuboid.tableName, (table: Knex.TableBuilder) => {
/* 
* how can I define this column, so that it's value is the product of other three(width,height,depth) columns
* plus the value should be updated on insert and update queries for a cuboid record
* e.g if I change the height or width so the volume should also change after being created 
*/
    table.integer('volume');
  });

// ignore the down function
export const down = (knex: Knex): Promise<void> =>
  knex.schema.dropTable(Cuboid.tableName);

注意:基础模型是从 objection.js 创建的模型。是这样的:

import { Model } from 'objection';
import * as path from 'path';

import knex from '../db/knex';

Model.knex(knex);

export default class Base extends Model {
  static get modelPaths(): string[] {
    return [path.resolve('src/models')];
  }
}

创建操作类似于

bag = await Bag.query().insertGraphAndFetch({
    volume: 100,
    title: 'A bag',
    cuboids: [{ width: 2, height: 2, depth: 2 }],
  });
//Note: A cubid belongs to a bag, so a bag is created before cuboids are assigned to it.

   cuboid = await Cuboid.query().insert({
      width,
      height,
      depth,
      bagId: bag.id,
    });

这无法使用迁移实现,而是您可以实施 objection.js' 模型方法,即 afterInsertafterUpdate 在 create/update 操作后更新长方体的体积执行。

您的长方体模型将变为:

import { Id, ModelOptions, QueryContext, RelationMappings } from 'objection';
import { Bag } from './Bag';
import Base from './Base';

export class Cuboid extends Base {
  id!: Id;
  width!: number;
  height!: number;
  depth!: number;
  bagId?: Id;
  bag!: Bag;
  volume!: number;

  static tableName = 'cuboids';

  $afterInsert = async (queryContext: QueryContext) => {
    await super.$afterInsert(queryContext);
    console.log(' after insert working');
    await this.$query().updateAndFetchById(this.id, {
      volume: this.width * this.height * this.depth,
    });
  };
  $afterUpdate = async (opt: ModelOptions, queryContext: QueryContext) => {
    await super.$afterUpdate(opt, queryContext);
    console.log(' after update working');
    await this.$query().updateAndFetchById(this.id, {
      volume: this.width * this.height * this.depth,
    });
  };

  static get relationMappings(): RelationMappings {
    return {
      bag: {
        relation: Base.BelongsToOneRelation,
        modelClass: 'Bag',
        join: {
          from: 'cuboids.bagId',
          to: 'bags.id',
        },
      },
    };
  }
}

export default Cuboid;