Skip to content

数据模型

介绍

为了便于模板复用,DataFaker 将数据模板封装为了模型,基于模型我们能够更好的去复用我们定义的数据模板,以此用最低的成本来适应需求变化。

定义模型

使用defineModel函数我们能够定义一个模型,其需要提供两个参数

  • 参数 【1】:模型别名
  • 参数 【2】:模型模板
ts
const userModel = defineModel('user', {});

当使用 defineModel 函数定义一个模型后,模型会被注入模型工厂。
同时,我们还可以基于该模型进行数据生成,也可以对模型进行克隆、添加字段、删除字段等操作。

模块克隆

使用cloneModel函数我们能够克隆一个模型,其需要提供两个参数

  • 参数 【1】:克隆的新的模型别名
  • 参数 【2】:要克隆的模型对象

比如我们以userModel模型为原型,克隆出了一个学生模型,并将其别名命名为studentModel

ts
// 用户模型
const userModel = defineModel('user', {
  id: 'number.int',
  firstName: 'person.firstName',
  secondName: 'person.lastName',
  age: ['number.int', { min: 18, max: 65 }],
  email: (ctx) => {
    return faker.internet.email({ firstName: ctx.firstName, lastName: ctx.secondName });
  },
  children: {
    refModel: 'user',
    // 控制自引用递归深度
    deep: 1,
  },
});
// 克隆学生模型
const studentModel = cloneModel('student', userModel);
const studentDatas = fakeData(studentModel);
console.dir(studentDatas, { depth: Infinity });

生成数据如下:

json
{
  "id": 6584695714108738,
  "firstName": "Jane",
  "secondName": "Wisoky",
  "age": 21,
  "children": {
    "id": 390307445727788,
    "firstName": "Addie",
    "secondName": "Koch",
    "age": 62,
    "children": {
      "id": 6872368248204444,
      "firstName": "Alexandra",
      "secondName": "Powlowski",
      "age": 29,
      "children": null,
      "email": "Alexandra.Powlowski16@hotmail.com"
    },
    "email": "Addie_Koch@hotmail.com"
  },
  "email": "Jane_Wisoky27@gmail.com"
}

深度克隆

模型克隆实际上采用的是深度克隆,对应引用类型不仅仅只克隆地址

添加/覆盖字段

对于模型,我们可以动态的为其添加字段

添加单个字段

使用withProperty函数可以添加/覆盖个字段,它接受两个参数:

  • 参数 【1】:字段名
  • 参数 【2】:字段值

比如在上面克隆的学生模型上再添加爱好字段

ts
// 用户模型
const userModel = defineModel('user', {
  id: 'number.int',
  firstName: 'person.firstName',
  secondName: 'person.lastName',
  age: ['number.int', { min: 18, max: 65 }],
  email: (ctx) => {
    return faker.internet.email({ firstName: ctx.firstName, lastName: ctx.secondName });
  },
  children: {
    refModel: 'user',
    // 控制自引用递归深度
    deep: 1,
  },
});
// 克隆学生模型
const studentModel = cloneModel('student', userModel);
// 添加爱好
studentModel.withProperty('hobby', ['helpers.arrayElements', ['篮球', '足球', '乒乓球', '羽毛球']]);
const studentDatas = fakeData(studentModel);
console.dir(studentDatas, { depth: Infinity });

这样生成的学生数据就有爱好字段了,如下所示

json
{
  "id": 4686280253521772,
  "firstName": "Kelton",
  "secondName": "Veum",
  "age": 50,
  "children": {
    "id": 5161302098209435,
    "firstName": "Jewell",
    "secondName": "Quigley",
    "age": 33,
    "children": {
      "id": 1378172277935689,
      "firstName": "Eula",
      "secondName": "Stehr",
      "age": 31,
      "children": null,
      "email": "Eula.Stehr1@hotmail.com"
    },
    "email": "Jewell.Quigley45@gmail.com"
  },
  "hobby": ["羽毛球", "篮球"],
  "email": "Kelton.Veum@yahoo.com"
}

添加多个字段

使用withProperties函数可以添加多个字段,它接受一个对象作为参数,对象中的每一项都是一个字段。

ts
const addressModel = defineModel('address', {
  country: 'location.country',
  city: 'location.city',
});
// 用户模型
const userModel = defineModel('user', {
  id: 'number.int',
  firstName: 'person.firstName',
  secondName: 'person.lastName',
  age: ['number.int', { min: 18, max: 65 }],
  email: (ctx) => {
    return faker.internet.email({ firstName: ctx.firstName, lastName: ctx.secondName });
  },
  children: {
    refModel: 'user',
    // 控制自引用递归深度
    deep: 1,
  },
});
// 克隆学生模型
const studentModel = cloneModel('student', userModel);
// 添加爱好
studentModel.withProperty('hobby', ['helpers.arrayElements', ['篮球', '足球', '乒乓球', '羽毛球']]);
// 添加多个字段
studentModel.withProperties({
  firstName: () => {
    return `__${faker.person.firstName()}`;
  },
  address: addressModel,
});
const studentDatas = fakeData(studentModel);
console.dir(studentDatas, { depth: Infinity });

生成的数据如下:

json
{
  "id": 1024531893968573,
  "secondName": "Hamill",
  "age": 30,
  "children": {
    "id": 4928359344459900,
    "firstName": "Myrtle",
    "secondName": "Schmidt",
    "age": 23,
    "children": {
      "id": 3849344663190874,
      "firstName": "Syble",
      "secondName": "Gerlach",
      "age": 23,
      "children": null,
      "email": "Syble.Gerlach81@gmail.com"
    },
    "email": "Myrtle.Schmidt@hotmail.com"
  },
  "hobby": ["足球"],
  "address": { "country": "San Marino", "city": "West Haven" },
  "firstName": "__Dell",
  "email": "__Dell.Hamill43@yahoo.com"
}

字段覆盖

可以看到原先的firstName字段被覆盖为__firstName,也就是在字段前面会加上__前缀
但是你也要注意,children中的数据并没有被覆盖,因为它是一个引用类型,引用的还是原先的userModel,而不是克隆后的studentModel
如果需要覆盖children字段,需要使用withProperty函数,将children字段重新定义为新的模型。

删除字段

删除单个字段

如果我们不想要某些字段,可以使用deleteProperty函数删除字段。这个函数仅仅接受一个参数,即字段名。如下所示:

ts
// 用户模型
const userModel = defineModel('user', {
  id: 'number.int',
  firstName: 'person.firstName',
  secondName: 'person.lastName',
  age: ['number.int', { min: 18, max: 65 }],
  email: (ctx) => {
    return faker.internet.email({ firstName: ctx.firstName, lastName: ctx.secondName });
  },
  children: {
    refModel: 'user',
    // 控制自引用递归深度
    deep: 1,
  },
});
// 克隆学生模型
const studentModel = cloneModel('student', userModel);
// 排除单个字段
studentModel.excludeProperty('children');
const studentDatas = fakeData(studentModel);
console.dir(studentDatas, { depth: Infinity });

生成的数据不再包含childern字段,如下所示:

json
{
  "id": 4505418375002445,
  "firstName": "Linwood",
  "secondName": "Jones",
  "age": 34,
  "email": "Linwood.Jones@gmail.com"
}

删除多个字段

如果我们不想要某些字段,可以使用excludeProperties函数删除多个字段。这个函数接受一个数组作为参数,数组中的每一项都是一个字段名。如下所示:

ts
// 用户模型
const userModel = defineModel('user', {
  id: 'number.int',
  firstName: 'person.firstName',
  secondName: 'person.lastName',
  age: ['number.int', { min: 18, max: 65 }],
  email: (ctx) => {
    return faker.internet.email({ firstName: ctx.firstName, lastName: ctx.secondName });
  },
  children: {
    refModel: 'user',
    // 控制自引用递归深度
    deep: 1,
  },
});
// 克隆学生模型
const studentModel = cloneModel('student', userModel);
studentModel.excludeProperty('children');
// 排除多个字段
studentModel.excludeProperties(['secondName', 'age']);
const studentDatas = fakeData(studentModel);
console.dir(studentDatas, { depth: Infinity });

生成的数据不再包含secondNameage字段,如下所示:

json
{
  "id": 2867275153560177,
  "firstName": "Kirstin",
  "email": "Kirstin.Spinka@yahoo.com"
}

最佳实践

先克隆再添加/覆盖字段

如果我们想在模型上添加/覆盖字段,可以先定义一个原型模型,然后克隆它,再添加/覆盖字段,这样可以避免影响依赖该模型的其他模型或场景。

链式编程

模型对象,本身支持链式编程,所以你完全可以这么做:

ts
const studentModel = cloneModel('student', userModel)
  .excludeProperty('children')
  .excludeProperties(['secondName', 'age']);

这与下面是一致的:

ts
const studentModel = cloneModel('student', userModel);
studentModel.excludeProperty('children');
studentModel.excludeProperties(['secondName', 'age']);
···