数据模型
介绍
为了便于模板复用,DataFaker 将数据模板封装为了模型,基于模型我们能够更好的去复用我们定义的数据模板,以此用最低的成本来适应需求变化。
定义模型
使用defineModel函数我们能够定义一个模型,其需要提供两个参数
- 参数 【1】:模型别名
- 参数 【2】:模型模板
const userModel = defineModel('user', {});当使用 defineModel 函数定义一个模型后,模型会被注入模型工厂。
同时,我们还可以基于该模型进行数据生成,也可以对模型进行克隆、添加字段、删除字段等操作。
模块克隆
使用cloneModel函数我们能够克隆一个模型,其需要提供两个参数
- 参数 【1】:克隆的新的模型别名
- 参数 【2】:要克隆的模型对象
比如我们以userModel模型为原型,克隆出了一个学生模型,并将其别名命名为studentModel
// 用户模型
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 });生成数据如下:
{
"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】:字段值
比如在上面克隆的学生模型上再添加爱好字段
// 用户模型
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 });这样生成的学生数据就有爱好字段了,如下所示
{
"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函数可以添加多个字段,它接受一个对象作为参数,对象中的每一项都是一个字段。
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 });生成的数据如下:
{
"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函数删除字段。这个函数仅仅接受一个参数,即字段名。如下所示:
// 用户模型
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字段,如下所示:
{
"id": 4505418375002445,
"firstName": "Linwood",
"secondName": "Jones",
"age": 34,
"email": "Linwood.Jones@gmail.com"
}删除多个字段
如果我们不想要某些字段,可以使用excludeProperties函数删除多个字段。这个函数接受一个数组作为参数,数组中的每一项都是一个字段名。如下所示:
// 用户模型
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 });生成的数据不再包含secondName和age字段,如下所示:
{
"id": 2867275153560177,
"firstName": "Kirstin",
"email": "Kirstin.Spinka@yahoo.com"
}最佳实践
先克隆再添加/覆盖字段
如果我们想在模型上添加/覆盖字段,可以先定义一个原型模型,然后克隆它,再添加/覆盖字段,这样可以避免影响依赖该模型的其他模型或场景。
链式编程
模型对象,本身支持链式编程,所以你完全可以这么做:
const studentModel = cloneModel('student', userModel)
.excludeProperty('children')
.excludeProperties(['secondName', 'age']);这与下面是一致的:
const studentModel = cloneModel('student', userModel);
studentModel.excludeProperty('children');
studentModel.excludeProperties(['secondName', 'age']);
···