@acore/typeorm-seeding 中文文档教程

发布于 3年前 浏览 12 项目主页 更新于 3年前

logo

TypeORM Seeding

NPM 包 构建状态 依赖 Sematic-Release

一种将测试数据植入数据库的令人愉快的方式。
灵感来自 PHP 中令人敬畏的框架 laravel 和来自 pleerock
Gery Hirschfeld贡献者


❯ Table of contents

❯ Introduction

为您的数据库创建一些示例数据是不是很累,这次就结束了!

它是如何工作的? 只需为您的实体(模型)和种子脚本创建一个实体工厂。

Entity

首先创建您的 TypeORM 实体。

// user.entity.ts
@Entity()
export class User {
  @PrimaryGeneratedColumn('uuid') id: string
  @Column({ nullable: true }) name: string
  @Column({ type: 'varchar', length: 100, nullable: false }) password: string
  @OneToMany((type) => Pet, (pet) => pet.user) pets: Pet[]

  @BeforeInsert()
  async setPassword(password: string) {
    const salt = await bcrypt.genSalt()
    this.password = await bcrypt.hash(password || this.password, salt)
  }
}

// pet.entity.ts
@Entity()
export class Pet {
  @PrimaryGeneratedColumn('uuid') id: string
  @Column() name: string
  @Column() age: number
  @ManyToOne((type) => User, (user) => user.pets)
  @JoinColumn({ name: 'user_id' })
  user: User
}

Factory

然后为每个实体定义一个工厂。 工厂的目的是创建具有生成数据的新实体。

注意:工厂也可用于生成测试数据。

// user.factory.ts
define(User, (faker: typeof Faker) => {
  const gender = faker.datatype.number(1)
  const firstName = faker.name.firstName(gender)
  const lastName = faker.name.lastName(gender)

  const user = new User()
  user.name = `${firstName} ${lastName}`
  user.password = faker.random.word()
  return user
})

// pet.factory.ts
define(Pet, (faker: typeof Faker) => {
  const gender = faker.datatype.number(1)
  const name = faker.name.firstName(gender)

  const pet = new Pet()
  pet.name = name
  pet.age = faker.datatype.number()
  pet.user = factory(User)() as any
  return pet
})

Seeder

最后但同样重要的是,创建一个播种机。 可以通过配置的 cli 命令 seed:run 调用播种器。 在这种情况下,它会生成 10 只宠物和主人(用户)。

注意:必须先配置seed:run。 转到 CLI 配置

// create-pets.seed.ts
export default class CreatePets implements Seeder {
  public async run(factory: Factory, connection: Connection): Promise<any> {
    await factory(Pet)().createMany(10)
  }
}

❯ Installation

在使用此 TypeORM 扩展之前,请阅读 TypeORM 入门 文档。 这解释了如何设置 TypeORM 项目。

之后使用 npmyarn 安装扩展。

npm i typeorm-seeding
# or
yarn add typeorm-seeding

可选,安装 Faker 库的类型定义。

npm install -D @types/faker

Configuration

要配置种子和工厂的路径,请更改 TypeORM 配置文件(ormconfig.js 或 ormconfig.json)。

默认路径是 src/database/{seeds,factories}/**/*{.ts,.js}

ormconfig.js

module.exports = {
  ...
  seeds: ['src/seeds/**/*{.ts,.js}'],
  factories: ['src/factories/**/*{.ts,.js}'],
}

.env

TYPEORM_SEEDING_FACTORIES=src/factories/**/*{.ts,.js}
TYPEORM_SEEDING_SEEDS=src/seeds/**/*{.ts,.js}

CLI Configuration

将以下脚本添加到您的 package.json 文件以配置种子 cli 命令。

"scripts": {
  "seed:config": "ts-node ./node_modules/typeorm-seeding/dist/cli.js config"
  "seed:run": "ts-node ./node_modules/typeorm-seeding/dist/cli.js seed"
  ...
}

在终端中执行种子运行 npm run seed:run

注意:更多 CLI 选项位于此处

将以下 TypeORM cli 命令添加到 package.json 以删除和同步数据库。

"scripts": {
  ...
  "schema:drop": "ts-node ./node_modules/typeorm/cli.js schema:drop",
  "schema:sync": "ts-node ./node_modules/typeorm/cli.js schema:sync",
  ...
}

CLI Options

OptionDefaultDescription
--seed or -snullOption to specify a seeder class to run individually.
--connection or -cnullName of the typeorm connection. Required if there are multiple connections.
--configName or -normconfig.jsName to the typeorm config file.
--root or -rprocess.cwd()Path to the typeorm config file.

❯ Basic Seeder

默认情况下,播种器类仅包含一个方法 run。 在此方法中,您可以将数据插入数据库。 对于手动插入,请使用 Query Builder 或使用 实体工厂

注意。 种子文件将按字母顺序执行。

import { Factory, Seeder } from 'typeorm-seeding'
import { Connection } from 'typeorm'
import { User } from '../entities'

export default class CreateUsers implements Seeder {
  public async run(factory: Factory, connection: Connection): Promise<any> {
    await connection
      .createQueryBuilder()
      .insert()
      .into(User)
      .values([
        { firstName: 'Timber', lastName: 'Saw' },
        { firstName: 'Phantom', lastName: 'Lancer' },
      ])
      .execute()
  }
}

❯ Using Entity Factory

当然,手动为每个实体种子指定属性是很麻烦的。 相反,您可以使用实体工厂来方便地生成大量数据库记录。

对于我们要创建的所有实体,我们需要定义一个工厂。 为此,我们为您提供了很棒的 faker 库作为您工厂的参数。 然后创建您的“假”实体并将其返回。 这些工厂文件应该在 src/database/factories 文件夹中,并以 .factory 为后缀,例如 src/database/factories/user.factory.ts

TypesDescription
EntityTypeORM Entity like the user or the pet in the samples.
ContextArgument to pass some static data into the factory function.
EntityFactoryThis object is used to make new filled entities or create it into the database.

define

define 函数创建一个新的实体工厂。

define: <Entity, Context>(entity: Entity, factoryFn: FactoryFunction<Entity, Context>) => void;
import Faker from 'faker'
import { define } from 'typeorm-seeding'
import { User } from '../entities'

define(User, (faker: typeof Faker, context: { roles: string[] }) => { ... })

factory

Factory 检索定义的工厂函数并返回 EntityFactory 以开始创建新实体。

factory: (entity: Entity) => (context?: Context) => EntityFactory<Entity, Context>
factory(Pet)()
factory(Pet)({ name: 'Balou' })

EntityFactory

map

使用 .map() 函数在生成的值被持久化之前改变它们。

map(mapFunction: (entity: Entity) => Promise<Entity>): EntityFactory<Entity, Context>
await factory(User)()
  .map(async (user: User) => {
    const pets: Pet[] = await factory(Pet)().createMany(2)
    const petIds = pets.map((pet: Pet) => pet.Id)
    await user.pets().attach(petIds)
  })
  .createMany(5)

make & makeMany

Make 和 makeMany 执行工厂函数并返回给定实体的新实例。 该实例由工厂函数生成的值填充,但未保存在数据库中。

overrideParams - 覆盖实体的一些属性。

make(overrideParams: EntityProperty<Entity> = {}): Promise<Entity>
await factory(User)().make()
await factory(User)().makeMany(10)

// override the email
await factory(User)().make({ email: 'other@mail.com' })
await factory(User)().makeMany(10, { email: 'other@mail.com' })

create & createMany

create 和 createMany 方法类似于 make 和 makeMany 方法,但最后创建的实体实例会持久保存在数据库中。

overrideParams - 覆盖实体的一些属性。

create(overrideParams: EntityProperty<Entity> = {}): Promise<Entity>
await factory(User)().create()
await factory(User)().createMany(10)

// override the email
await factory(User)().create({ email: 'other@mail.com' })
await factory(User)().createMany(10, { email: 'other@mail.com' })

❯ Seeding Data in Testing

实体工厂也可以用于测试。 为此,请调用 useSeeding 函数,该函数加载所有已定义的实体工厂。

选择您的测试数据库 wisley。 我们建议在内存数据库中的 sqlite 中运行测试。

{
  "type": "sqlite",
  "name": "memory",
  "database": ":memory:"
}

但是,如果测试数据库不在内存中,则使用 --runInBand 标志来禁用并行化运行。

describe("UserService", () => {
  let connection: Connection

  beforeAll(async (done) => {
    connection = await useRefreshDatabase({ connection: 'memory' })
    await useSeeding()

    const user = await factory(User)().make()
    const createdUser = await factory(User)().create()

    await runSeeder(CreateUserSeed)
    done()
  })

  afterAll(async (done) => {
    await tearDownDatabase()
    done()
  })

  test('Should ...', () => { ... })
})

useSeeding

加载定义的实体工厂。

useSeeding(options: ConfigureOption = {}): Promise<void>

runSeeder

运行给定的播种器类。

runSeeder(seed: SeederConstructor): Promise<void>

useRefreshDatabase

连接到数据库,删除它并重新创建模式。

useRefreshDatabase(options: ConfigureOption = {}): Promise<Connection>

tearDownDatabase

关闭打开的数据库连接。

tearDownDatabase(): Promise<void>

ConfigureOption

interface ConfigureOption {
  root?: string // path to the orm config file. Default = process.cwd()
  configName?: string // name of the config file. eg. ormconfig.js
  connection?: string // name of the database connection.
}

❯ Example Projects

请在此处填写以添加您的开源项目。 这有助于其他人更好地了解播种技术。

ProjectDescription
copa-ch/copa-backendNest application written in TypeScript for the COPA project. This app manages your tournaments and generates the schedules.

❯ Changelog

变更日志

❯ License

麻省理工学院

logo

TypeORM Seeding

NPM package Build Status Dependency Sematic-Release

A delightful way to seed test data into your database.
Inspired by the awesome framework laravel in PHP and of the repositories from pleerock
Made with ❤️ by Gery Hirschfeld and contributors


❯ Table of contents

❯ Introduction

Isn't it exhausting to create some sample data for your database, well this time is over!

How does it work? Just create a entity factory for your entities (models) and a seed script.

Entity

First create your TypeORM entities.

// user.entity.ts
@Entity()
export class User {
  @PrimaryGeneratedColumn('uuid') id: string
  @Column({ nullable: true }) name: string
  @Column({ type: 'varchar', length: 100, nullable: false }) password: string
  @OneToMany((type) => Pet, (pet) => pet.user) pets: Pet[]

  @BeforeInsert()
  async setPassword(password: string) {
    const salt = await bcrypt.genSalt()
    this.password = await bcrypt.hash(password || this.password, salt)
  }
}

// pet.entity.ts
@Entity()
export class Pet {
  @PrimaryGeneratedColumn('uuid') id: string
  @Column() name: string
  @Column() age: number
  @ManyToOne((type) => User, (user) => user.pets)
  @JoinColumn({ name: 'user_id' })
  user: User
}

Factory

Then for each entity define a factory. The purpose of a factory is to create new entites with generate data.

Note: Factories can also be used to generate data for testing.

// user.factory.ts
define(User, (faker: typeof Faker) => {
  const gender = faker.datatype.number(1)
  const firstName = faker.name.firstName(gender)
  const lastName = faker.name.lastName(gender)

  const user = new User()
  user.name = `${firstName} ${lastName}`
  user.password = faker.random.word()
  return user
})

// pet.factory.ts
define(Pet, (faker: typeof Faker) => {
  const gender = faker.datatype.number(1)
  const name = faker.name.firstName(gender)

  const pet = new Pet()
  pet.name = name
  pet.age = faker.datatype.number()
  pet.user = factory(User)() as any
  return pet
})

Seeder

And last but not least, create a seeder. The seeder can be called by the configured cli command seed:run. In this case it generates 10 pets with a owner (User).

Note: seed:run must be configured first. Go to CLI Configuration.

// create-pets.seed.ts
export default class CreatePets implements Seeder {
  public async run(factory: Factory, connection: Connection): Promise<any> {
    await factory(Pet)().createMany(10)
  }
}

❯ Installation

Before using this TypeORM extension please read the TypeORM Getting Started documentation. This explains how to setup a TypeORM project.

After that install the extension with npm or yarn.

npm i typeorm-seeding
# or
yarn add typeorm-seeding

Optional, install the type definitions of the Faker library.

npm install -D @types/faker

Configuration

To configure the path to your seeds and factories change the TypeORM config file (ormconfig.js or ormconfig.json).

The default paths are src/database/{seeds,factories}/**/*{.ts,.js}

ormconfig.js

module.exports = {
  ...
  seeds: ['src/seeds/**/*{.ts,.js}'],
  factories: ['src/factories/**/*{.ts,.js}'],
}

.env

TYPEORM_SEEDING_FACTORIES=src/factories/**/*{.ts,.js}
TYPEORM_SEEDING_SEEDS=src/seeds/**/*{.ts,.js}

CLI Configuration

Add the following scripts to your package.json file to configure the seed cli commands.

"scripts": {
  "seed:config": "ts-node ./node_modules/typeorm-seeding/dist/cli.js config"
  "seed:run": "ts-node ./node_modules/typeorm-seeding/dist/cli.js seed"
  ...
}

To execute the seed run npm run seed:run in the terminal.

Note: More CLI options are here

Add the following TypeORM cli commands to the package.json to drop and sync the database.

"scripts": {
  ...
  "schema:drop": "ts-node ./node_modules/typeorm/cli.js schema:drop",
  "schema:sync": "ts-node ./node_modules/typeorm/cli.js schema:sync",
  ...
}

CLI Options

OptionDefaultDescription
--seed or -snullOption to specify a seeder class to run individually.
--connection or -cnullName of the typeorm connection. Required if there are multiple connections.
--configName or -normconfig.jsName to the typeorm config file.
--root or -rprocess.cwd()Path to the typeorm config file.

❯ Basic Seeder

A seeder class only contains one method by default run. Within this method, you may insert data into your database. For manually insertion use the Query Builder or use the Entity Factory

Note. The seeder files will be executed alphabetically.

import { Factory, Seeder } from 'typeorm-seeding'
import { Connection } from 'typeorm'
import { User } from '../entities'

export default class CreateUsers implements Seeder {
  public async run(factory: Factory, connection: Connection): Promise<any> {
    await connection
      .createQueryBuilder()
      .insert()
      .into(User)
      .values([
        { firstName: 'Timber', lastName: 'Saw' },
        { firstName: 'Phantom', lastName: 'Lancer' },
      ])
      .execute()
  }
}

❯ Using Entity Factory

Of course, manually specifying the attributes for each entity seed is cumbersome. Instead, you can use entity factories to conveniently generate large amounts of database records.

For all entities we want to create, we need to define a factory. To do so we give you the awesome faker library as a parameter into your factory. Then create your "fake" entity and return it. Those factory files should be in the src/database/factories folder and suffixed with .factory like src/database/factories/user.factory.ts.

TypesDescription
EntityTypeORM Entity like the user or the pet in the samples.
ContextArgument to pass some static data into the factory function.
EntityFactoryThis object is used to make new filled entities or create it into the database.

define

The define function creates a new entity factory.

define: <Entity, Context>(entity: Entity, factoryFn: FactoryFunction<Entity, Context>) => void;
import Faker from 'faker'
import { define } from 'typeorm-seeding'
import { User } from '../entities'

define(User, (faker: typeof Faker, context: { roles: string[] }) => { ... })

factory

Factory retrieves the defined factory function and returns the EntityFactory to start creating new enities.

factory: (entity: Entity) => (context?: Context) => EntityFactory<Entity, Context>
factory(Pet)()
factory(Pet)({ name: 'Balou' })

EntityFactory

map

Use the .map() function to alter the generated value before they get persisted.

map(mapFunction: (entity: Entity) => Promise<Entity>): EntityFactory<Entity, Context>
await factory(User)()
  .map(async (user: User) => {
    const pets: Pet[] = await factory(Pet)().createMany(2)
    const petIds = pets.map((pet: Pet) => pet.Id)
    await user.pets().attach(petIds)
  })
  .createMany(5)

make & makeMany

Make and makeMany executes the factory functions and return a new instance of the given entity. The instance is filled with the generated values from the factory function, but not saved in the database.

overrideParams - Override some of the attributes of the entity.

make(overrideParams: EntityProperty<Entity> = {}): Promise<Entity>
await factory(User)().make()
await factory(User)().makeMany(10)

// override the email
await factory(User)().make({ email: 'other@mail.com' })
await factory(User)().makeMany(10, { email: 'other@mail.com' })

create & createMany

the create and createMany method is similar to the make and makeMany method, but at the end the created entity instance gets persisted in the database.

overrideParams - Override some of the attributes of the entity.

create(overrideParams: EntityProperty<Entity> = {}): Promise<Entity>
await factory(User)().create()
await factory(User)().createMany(10)

// override the email
await factory(User)().create({ email: 'other@mail.com' })
await factory(User)().createMany(10, { email: 'other@mail.com' })

❯ Seeding Data in Testing

The entity factories can also be used in testing. To do so call the useSeeding function, which loads all the defined entity factories.

Choose your test database wisley. We suggest to run your test in a sqlite in memory database.

{
  "type": "sqlite",
  "name": "memory",
  "database": ":memory:"
}

However, if the test database is not in memory, than use the --runInBand flag to disable parallelizes runs.

describe("UserService", () => {
  let connection: Connection

  beforeAll(async (done) => {
    connection = await useRefreshDatabase({ connection: 'memory' })
    await useSeeding()

    const user = await factory(User)().make()
    const createdUser = await factory(User)().create()

    await runSeeder(CreateUserSeed)
    done()
  })

  afterAll(async (done) => {
    await tearDownDatabase()
    done()
  })

  test('Should ...', () => { ... })
})

useSeeding

Loads the defined entity factories.

useSeeding(options: ConfigureOption = {}): Promise<void>

runSeeder

Runs the given seeder class.

runSeeder(seed: SeederConstructor): Promise<void>

useRefreshDatabase

Connects to the database, drops it and recreates the schema.

useRefreshDatabase(options: ConfigureOption = {}): Promise<Connection>

tearDownDatabase

Closes the open database connection.

tearDownDatabase(): Promise<void>

ConfigureOption

interface ConfigureOption {
  root?: string // path to the orm config file. Default = process.cwd()
  configName?: string // name of the config file. eg. ormconfig.js
  connection?: string // name of the database connection.
}

❯ Example Projects

Please fill free to add your open-source project here. This helps others to better understand the seeding technology.

ProjectDescription
copa-ch/copa-backend???? Nest application written in TypeScript for the COPA project. This app manages your tournaments and generates the schedules.

❯ Changelog

Changelog

❯ License

MIT

    我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
    原文