EZDML脚本快速上手
——HUZ 20191027
请注意:HTML文档较旧,仅适用于Win32版,适用最新EZDML for mac64/linux64/win64版的文档请下载PDF。
脚本能够访问模型中的所有对象及其属性,能够批量进行各种增删改查,能生成你想要的各种格式。
注意:模型目录树上有批量添加删除字段功能,比写脚本简单多了,如果仅仅是想批量增减字段,可能没有必要用脚本。
EZDML同时支持Javascript和Pascal脚本(以下简称JS和PAS),你可以根据需要选择一种。
本文档主要说如何用JS实现一些目的,不讲解javascript的基础知识。
打开示例文件,选中会员表,右键弹出菜单,选择“执行脚本”:
弹出脚本编辑窗口,默认就是JAVASCRIPT的示例了:
简单说下这个脚本窗口:执行NEW命令新建时会自动初始化为示例脚本,默认是JS脚本(再点一下NEW命令就切换为PAS)。JS脚本目前不支持断点和单步调试,按F9运行,F1显示帮助。脚本窗口关闭时会自动保存当前内容到临时文件(每次F9运行脚本时也会)。
在这个脚本窗口执行”Help”菜单或按F1可显示一些简单的帮助:
回到JAVASCRIPT的示例,往下翻,可以看到一共有五个示例:
1. 演示如何编写JavaScript脚本(再次新建可切换为PascalScript)遍历所有模型、表和字段
2. 演示如何编写脚本页面模板
3. 演示如何编写脚本处理文本文件
4. 演示如何编写脚本遍历对象属性
5. 演示如何在JS脚本中混合调用Pascal脚本
其中只有第一个没有被注释掉,我们就拿它来说事。它显然是i j k三个for循环遍历了模型、表和字段。代码很简单,不过我还是再解释一下:
//开始
for(var i=0; i<allModels.count; i++) //遍历所有模型,一个文件里可能有多个模型图
//if(allModels.getItem(i) == allModels.curDataModel) //判断是否为当前模型
{
var md=allModels.getItem(i); //获得第i个模型
curOut.add('Model'+i+': '+md.name); //输出模型名称
for(var j=0; j<md.tables.count; j++) //遍历模型中的表
//if(md.tables.getItem(j).isSelected) //判断表是否选中
//if(curTable && md.tables.getItem(j).name == curTable.name) //判断表名是否与当前表名相同
{
var tb = md.tables.getItem(j); //获取表
curOut.add(' Table'+j+': '+tb.name); //输出表名
for(var k=0; k<tb.metaFields.count; k++) //遍历表中的字段
{
var fd = tb.metaFields.getItem(k); //获取字段
curOut.add(' Field'+k+': '+fd.name); //输出字段名
}
}
}
//结束
我们把其它示例2345的代码删除,只留下示例1,直接按F9运行,可以看到运行结果输出了所有表和字段:
把第12行//if(md.tables.getItem(j).isSelected)这行前面的//注释符删除,再运行,这时就只输出会员表的信息了,因为我们只选中了会员表:
把第12行注掉,增加字段名的判断,并修改输出代码,最终改成下面的样子:
for(var i=0; i<allModels.count; i++)
//if(allModels.getItem(i) == allModels.curDataModel)
{
var md=allModels.getItem(i);
for(var j=0; j<md.tables.count; j++)
//if(md.tables.getItem(j).isSelected)
//if(curTable && md.tables.getItem(j).name == curTable.name)
{
var tb = md.tables.getItem(j);
for(var k=0; k<tb.metaFields.count; k++)
{
var fd = tb.metaFields.getItem(k);
if(fd.name.toLowerCase().indexOf("product")>=0) //判断字段名是否包含product
{
curOut.add('Model_'+i+': '+md.name+' Table'+j+': '+tb.name+' Field'+k+': '+fd.name);
}
}
}
}
运行结果如下,只输出了符合条件的字段:
很多时候我们只要处理当前表curTable,假设我要为当前表的字段生成某个赋值代码,这时可以这样遍历字段(请注意要选一个表,以保证当前表curTable有值):
for(var i=0; i<curTable.metaFields.count-1; i++)
{
var fd = curTable.metaFields.getItem(i);
curOut.add(' if(member1.'+fd.name+'==null)');
curOut.add(' member1.'+fd.name+'=member2.'+fd.name+';');
}
运行效果:
查询遍历基本上就这样了,下面我们要动手修改了。
我要给每一个包含字段id的表,增加一个memo备注字段(如果已存在则跳过),类型为字符串,长度512,普通索引(只是为了演示哈,其实模型目录树上有批量添加字段功能,比写脚本简单多了),代码如下:
for(var i=0; i<allModels.count; i++)
{
var md=allModels.getItem(i);
for(var j=0; j<md.tables.count; j++)
{
var tb = md.tables.getItem(j);
var fd=tb.metaFields.fieldByName("id"); //查找ID字段
if(!fd) //未找到ID字段,跳过
{
curOut.add(' Table'+j+': '+tb.name +' no ID found, skipped');
}
else //找到ID字段了
{
fd=tb.metaFields.fieldByName("memo"); //查找memo字段
if(!fd) //不存在就添加
{
fd=tb.metaFields.newMetaField();
fd.name='memo';
fd.displayName='备注';
fd.memo='这是一个演示添加的字段';
fd.dataType='cfdtString';
fd.dataLength=512;
fd.indexType='cfitNormal';
fd.nullable=true;
curOut.add(' Table'+j+': '+tb.name +' memo field added');
}
else //已存在则提示
curOut.add(' Table'+j+': '+tb.name +' memo field exists');
}
}
}
正常来说,我们改了一个表,需要调_syncTableProps(tb)同步属性到模型中其它所有同名的表。不过,这里我们直接遍历了所有模型中的表全改了一遍,就不需要同步了。
运行结果:
回到模型图刷新一下,大部分表都加上了memo字段:
接下来我觉得memo这名字不好,我要把这些memo字段改名为remark,逻辑名为“评论”,长度改为255,代码如下:
for(var i=0; i<allModels.count; i++)
{
var md=allModels.getItem(i);
for(var j=0; j<md.tables.count; j++)
{
var tb = md.tables.getItem(j);
var fd=tb.metaFields.fieldByName("memo"); //查找memo字段
if(!fd) //未找到memo字段,跳过
{
curOut.add(' Table'+j+': '+tb.name +' no memo field found, skipped');
}
else //找到memo字段了
{
fd.name='remark';
fd.displayName='评论';
fd.dataLength=255;
curOut.add(' Table'+j+': '+tb.name +' memo field modified');
}
}
}
运行结果:
回到模型图,刷新:
接下来我又后悔了,我觉得remark这字段没什么用,决定删除掉,代码如下:
for(var i=0; i<allModels.count; i++)
{
var md=allModels.getItem(i);
for(var j=0; j<md.tables.count; j++)
{
var tb = md.tables.getItem(j);
var fd=tb.metaFields.fieldByName("remark"); //查找remark字段
if(!fd) //未找到remark字段,跳过
{
curOut.add(' Table'+j+': '+tb.name +' no remark field found, skipped');
}
else //找到remark字段了,删除之
{
tb.metaFields.remove(fd);
curOut.add(' Table'+j+': '+tb.name +' remark field removed');
}
}
}
运行结果:
好了,用JS增删改查了一遍,想必你已经有个大概了解了。
会JS的人没必要看这一节。
写了JS的内容后,感觉PASCAL似乎没有必要写了,不过空着也不好,还是写点凑数吧。
还是打开示例文件,选中会员表,右键弹出菜单,选择“执行脚本”:
弹出脚本编辑窗口,默认就是JAVASCRIPT的示例:
再执行一次File/New菜单命令,切换为PASCAL脚本(如果还是JS,就再点一次):
PASCAL脚本支持断点和单步调试,按F9运行,F1显示帮助。脚本窗口关闭时会自动保存当前内容到临时文件(每次F9运行脚本时也会)。
照例解释一下:
var //变量声明,PASCAL比较烦人的一点就是变量声明要在最前面
I, J, K: Integer;
md: TCtDataModelGraph;
tb: TCtMetaTable;
fd: TCtMetaField;
begin //开始
for I:=0 to AllModels.Count-1 do //遍历所有模型
//if AllModels.Items[I] = AllModels.CurDataModel then //判断如果是当前模型
begin
md := AllModels.Items[I]; //获取模型
CurOut.Add('Model'+IntToStr(I)+': '+md.Name); //输出模型名称
for J:=0 to md.Tables.Count-1 do //遍历该模型中的所有表
//if md.Tables.Items[J].IsSelected then //如果表被选中
//if CurTable <> nil then if md.Tables.Items[J].Name = CurTable.Name then //或者表名与当前表一样
begin
tb := md.Tables.Items[J]; //获取表
CurOut.Add(' Table'+IntToStr(J)+': '+tb.Name); //输出表名
for K:=0 to tb.MetaFields.Count -1 do //遍历该表的所有字段
begin
fd := tb.MetaFields.Items[K]; //获取字段
CurOut.Add(' Field'+IntToStr(K)+': '+fd.Name); //输出字段名
end;
end;
end;
end. //PASCAL的最后结束是end加一个点
直接按F9运行,效果跟之前JS的示例是一样的:
增加字段名的判断,并修改输出代码,最终改成下面的样子:
var
I, J, K: Integer;
md: TCtDataModelGraph;
tb: TCtMetaTable;
fd: TCtMetaField;
begin
for I:=0 to AllModels.Count-1 do
//if AllModels.Items[I] = AllModels.CurDataModel then
begin
md := AllModels.Items[I];
for J:=0 to md.Tables.Count-1 do
//if md.Tables.Items[J].IsSelected then
//if CurTable <> nil then if md.Tables.Items[J].Name = CurTable.Name then
begin
tb := md.Tables.Items[J];
for K:=0 to tb.MetaFields.Count -1 do
begin
fd := tb.MetaFields.Items[K];
if Pos('product', LowerCase(fd.Name))>0 then//判断字段名是否包含product
CurOut.Add('Model_'+IntToStr(i)+': '+md.name+' Table'+IntToStr(j)+': '+tb.name+' Field'+IntToStr(k)+': '+fd.name);
end;
end;
end;
end.
运行结果如下,只输出了符合条件的字段:
很多时候我们只要处理当前表CurTable,假设我要为当前表的字段生成某个赋值代码,这时可以这样遍历字段(请注意要选一个表,以保证当前表CurTable有值):
var
I: Integer;
fd: TCtMetaField;
begin
for I:=0 to CurTable.MetaFields.Count -1 do
begin
fd := CurTable.MetaFields.Items[I];
CurOut.Add(' if(member1.'+fd.Name+'==null)');
CurOut.Add(' member1.'+fd.Name+'=member2.'+fd.Name+';');
end;
end.
运行效果:
再简单说下批量加字段,要给每一个包含字段id的表,增加一个memo备注字段(如果已存在则跳过),类型为字符串,长度512,普通索引(只是为了演示,其实模型目录树上有批量添加字段功能,比写脚本简单),代码如下:
var
I, J: Integer;
md: TCtDataModelGraph;
tb: TCtMetaTable;
fd: TCtMetaField;
begin
for I:=0 to AllModels.Count-1 do
begin
md := AllModels.Items[I];
for J:=0 to md.Tables.Count-1 do
begin
tb := md.Tables.Items[J];
fd := tb.metaFields.fieldByName('id');
if fd = nil then //未找到ID字段,跳过
begin
curOut.add(' Table'+IntToStr(j)+': '+tb.name +' no ID found, skipped');
end
else
begin
fd := tb.metaFields.fieldByName('memo'); //查找memo字段
if fd = nil then //不存在就添加
begin
fd:=tb.metaFields.newMetaField();
fd.name:='memo';
fd.displayName:='备注';
fd.memo:='这是一个演示添加的字段';
fd.dataType:=cfdtString;
fd.dataLength:=512;
fd.indexType:=cfitNormal;
fd.nullable:=true;
curOut.add(' Table'+IntToStr(j)+': '+tb.name +' memo field added');
end
else //已存在则提示
curOut.add(' Table'+IntToStr(j)+': '+tb.name +' memo field exists');
end;
end;
end;
end.
正常来说,我们改了一个表,需要调SyncTableProps(tb)同步属性到模型中其它所有同名的表。不过,这里我们直接遍历了所有模型中的表全改了一遍,就不需要同步了。
运行结果:
回到模型图,刷新可见效果:
PAS就先说到这。
如需了解EZDML脚本中的对象及其关系等详细内容,请参考《EZDML脚本配置》。