大道至简,新一代企业应用无栈开发

平台之上,一种语言,可视化、脚本化、全端一体化开发

内容通用接口

所有内容对象的通用属性和方法

docutils document without title

站点的所有内容,提供一组公共的属性和方法。下面以 obj 变量代指任意的站点内容对象。

1   对象类型 object_types

站点里面存放着各种不同类型的内容对象。

每个对象通过属性 obj.object_types 来检查类型。不同类型的对象,有不同的属性和方法。

1.1   容器类的对象

  • 文件夹: ('Folder', 'Container')
  • 表单库: ('DataContainer', 'Container')
  • 应用容器: ('AppContainer', 'Container')
  • 站点根: ('Root', 'AppContainer', 'Container')
  • 成员空间: ('Home', 'AppContainer', 'Container')
  • 成员空间容器: ('HomeContainer', 'AppContainer', 'Container')

1.2   非容器对象

  • 文件: ('File', 'Item')
  • 表单: ('DataItem', 'Item')
  • 工作项: ('WorkItem', 'Item')
  • 文件快捷方式,: ('FileShortCut', 'Item')
  • 文件夹快捷方式: ('FolderShortCut', 'Item')

2   图标 get_icon

不同类型,使用不同的图标。系统内部为内容提供了矢量字体图标:

obj.get_icon() #return {'name': '', 'color': ''}

name是图标名,color是图标颜色。

3   标识和定位内容

3.1   查找内容路径 object_path

可叠加内容的名字、以及包含该内容的所有容器的名字,形成对象路径,用于定位一个内容:

path = root.object_path(obj)

返回path的形式: '/container2/item_1'

3.2   根据路径反查内容 object_by_path

也可反查:

obj = root.object_by_path('/container2/item_1')

注意:一旦发生移动或者改名,对象的路径就发生变化。这样用路径就不能来永久标识对象。

3.3   绝对地址 absolute_url

可以直接根据当前请求地址,得到对象url地址:

obj.absolute_url(request)

有时候无法得到request(如系统自动任务),这时候可以根据系统的配置来计算url:

obj.absolute_url(None)

如果需要生成调用某个脚本的地址,可以这样:

obj.absolute_url(request, 'xxx.xx:xxx', aa=1, bb=2)

3.4   唯一标识定位 object_uid

系统的所有对象,创建后均会注册一个永久的整数ID,无论以后对象是否移动或者改名,都不会改变:

uid = root.object_uid(obj)

注意:站点根的uid固定为0

3.5   通过uid反查对象 object_by_uid

通过uid找到对象:

obj = root.object_by_uid(uid)

注意:如果对象被删除,就无法找到对象,这时候会抛出 KeyError的错误,需要进行异常处理。

3.6   容错的反查对象 query_objects

由于object_by_uid处理异常比较麻烦,这个方法可以自动剔除不存在的对象:

objs = root.query_objects(uids)

注意:我们推荐通过这个方法来查询对象,而不是 object_by_uid ,以便得到更高质量的代码。

3.7   永久地址 uid_url

web访问地址为:

obj.uid_url(request=None)

如果request为None,将根据系统配置生成url

如果需要生成调用某个脚本的地址,可以这样:

obj.uid_url(request, 'xxx.xx:xxx', aa=1, bb=2)

3.8   对象的扩展API访问地址 api_url

扩展API是通过扩展应用编辑的Python脚本。使用如下接口生成API访问地址:

obj.api_url(request, 'xxx.xxx:xxx', internal_=False, auth_=False, **kw)

其中:

  1. internal_: 是否是内部调用。如果是转换回掉,应该用内部的地址,使用True
  2. auth_:是否使用当前用户身份调用API
  3. kw:这是Python脚本的调用参数

4   内容的属性

所有内容存在一组基础属性和设置信息。也可以通过表单和扩展属性来自定义属性。

4.1   核心属性 obj.md

系统的所有内容,都包括一组标准的属性,有系统自动维护,或者有特殊的含义。属性也称作元数据(metadata).

内容obj,通过属性 md 来访问,md有类似dict的接口:

title = obj.md['title']
title = obj.md.get('title', 'no title')

obj.md['title'] = 'new title'
obj.md.keys()
obj.md.values()
obj.md.set('title', 'new title')
obj.md.update(title='new title')

也就是说,obj.md存储的数据是这样的:

{'title':'aa.doc',
 'description':'',
 'creators': ['users.panjunyong'],
 'created': 2015-12-12,
 'modified': 2015-12-23,
 ....
}

title属性是最重要的属性,搜索结果等都采用这个来标识内容的名字。

内容一旦加入到仓库,可以查看其创建人、修改人,创建时间、修改时间:

obj.md['creators']
obj.md['contributors']
obj.md['created']
obj.md['modified']

其他的基础属性,还包括:

obj.md['identifier'] 这个也就是文件的编号
obj.md['expires'] 内容的失效时间
obj.md['effective'] 内容的生效时间

4.2   自定义属性 obj.md

可自由设置属性,比如表单页保存的数据、表单库的设置信息。自定义属性,也存放在 obj.md 中。

对于需要在日历上显示的内容,通常有如下属性:

obj.md['responsibles'] = ('users.panjy', 'users.lei') # 负责人
obj.md['start'] = datetime.now() # 开始时间
obj.md['end'] = datetime.now(), 结束时间

对于联系人类型的内容,通常可以有如下表单属性:

obj.md['mail'] = 'panjy@foobar.com' #邮件
obj.md['mobile'] = '232121' # 手机

经费相关的属性:

obj.md['amount'] = 211

地理相关的属性:

obj.md['longitude'] = 123123.12312 #经度
obj.md['latitude'] = 12312.12312 # 纬度

4.3   扩展属性 obj.mdset

为了避免命名冲突,更好的分类组织属性,系统使用扩展属性(mdset: metadata set), 可方便为内容扩展存储多组属性.

我们可以这里理解扩展属性,在 obj.mdset 中存放档案信息和网站模板信息2组扩展属性:

{'zopen.archive:archve':{
      'number':'DEEE123',
      'copy':3,
      'location':'上海',
      },
  'zopen.cms:left':{
      'show_nav': True,
      'text': 'QQ: 123123'
      }
}

4.3.1   扩展属性管理 dict接口

创建一个扩展属性:

obj.mdset['zopen.archive:archive'] = {}

更改扩展属性内容:

obj.mdset['zopen.archive:archive'] = {'number':'DE33212', 'copy':33})

删除扩展属性:

del obj.mdset['zopen.archive:archive']

查看内容所有扩展属性:

obj.mdset.keys()  # 返回: ['zopen.archive:archve', 'zopen.cms:left']

4.3.2   调整多个扩展属性的顺序

obj.mdset.set_order(['zopen.cms:left', 'zopen.archive:archve'])

4.3.3   继承上层的扩展属性

也可以找到继承的扩展属性,也就是向所有的父容器找扩展属性设置:

obj.mdset.get(mdset_name, default={}, inherit=True, same_type=False)

其中:

  • mdset_name:扩展属性的名字,比如 'zopen.archive:archive'
  • default:如果找不到,返回默认值,默认返回None
  • inherit:如果找不到,是否网上找,默认为False
  • same_type:如果inherit,向上找的时候,是发只找相同类型的容器(比如只找文件夹),默认为False

4.3.4   扩展属性数据读取 dict接口

活动扩展属性的内的属性值的存取:

number = obj.mdset['zopen.archive:archive']['number']
obj.mdset['zopen.archive:archive']['number'] = 'DD222'

注意:如果字段没有值,会报 KeyError 的错误,可以通过get方法取值:

number = obj.mdset['zopen.archive:archive'].get('number')

如果没有值返回None,也可以返回指定默认值:

number = obj.mdset['zopen.archive:archive'].get('number', '11111')

也可以批量更改属性值:

obj.mdset['zopen.archive:archive'].update(copy=34, number='ES33')

4.4   设置信息 obj.settings

通常对于容器会有一系列的设置信息,如显示方式、添加子项的设置、关联流程等等.

设置信息其实是一种特殊的扩展属性, 由于使用频繁,可以直接通过 obj.settings 来访问。

可理解 obj.settings 存储的数据类似:

{'default_view': 'tabula',
'show_nav': True,
'allow_comment':True
}

通过 dict 接口来访问:

obj.settings['show_nav']
obj.settings.get('allow_comment', True)

obj.settings['show_nav'] = 'blabla'

如果需要继承上级容器的设置,可以:

obj.settings.get(setting_name, default=default_value, inherit=True, same_type=False)

其中:

  • setting_name: 设置项的名字
  • default;如果找不到,默认值,默认为None
  • inherit: 如果找不到,是否向上找,默认False
  • same_type:如果向上找,是否只找同类型的(比如只找文件夹), 默认False

5   内容类型 content_types

对于表单对象,自定义属性是通过表单来定义的,表单的每个一个输入字段就对应一个属性; 对于表单库和应用容器,自定义属性通常就是其设置信息。

不同类型的内容,有不同的自定义属性。可在软件包中定义支持的属性(见本章最后一节)。

可以通过对象的 content_type 来指定自己使用的软件包自定义属性。

比如:应用容器天气查看,可通过 content_types 来进行应用设置天气区域等字段(软件包zopen.weather的表单):

obj.content_types = ('zopen.weather:appobj', )

表单库可能是故障跟踪,有故障跟踪的一些设置项需要定义(软件包zopen.issuetracker的issue_obj表单):

obj.content_types = ('zopen.issuetracker:issue_obj', )

具体的一个故障单表单,则可能是(软件包zopen.isssuetracker的issue表单):

obj.content_types = ('zopen.issuetracker:issue', )

如果这里有多个,表示继承。content_types 的具体定义和使用,参照 《表单处理》 一节

6   状态

系统的内容存在一组状态。状态变化,通常会改变角色拥有的权限。因此状态和权限存在一定关系。

阶段是在软件包中定义的一种状态,目前阶段仅仅在表单流程中使用。

6.1   内容的状态 obj.stati

每一个对象存在一组状态,可以用 obj.stati 查看,比如:

('visible.default', 'modify.default')

其中 visible 前缀表示私密性状态,modify 前缀表示修改发布状态。

不同类型的对象,还存在其他一些状态前缀,比如 stage , folder, profile 等。

6.1.1   状态变化 obj.state

使用 state ,来控制对象状态的变化:

# 不进行权限检查,直接发布某个文档
obj.state.set('modify.archived', do_check=False)
# 设置文件夹为受控,需要检查权限
obj.state.set('folder.control', do_check=True)

也可以得到某个状态:

state = obj.state.get('visible') # 得到可见状态
print state.name, state.title, state.description

7   关系 obj.relations

每一个对象都可以和其他的对象建立各种关系. 常用关系类型包括:

  • children:定义父子关系,比如任务的分解,计划的分解
  • related :引用,比如工作日志和任务之间的关联,文件关联等
  • comment_attachment:评注中的附件,和被评注对象之间的关联
  • reference: 关联关系

7.2   添加关系 add

doc1引用了doc2:

doc1.relations.add('related', doc2, metadata={})

其中:

  • 第一个参数是关系名字
  • 第二个参数是目标对象
  • metadata: 关系说明信息

7.3   删除关系 remove

删除上面设置的那条关系:

doc1.relations.remove('related', doc2)

7.4   查目标关系 list_targets

查看应用的关系:

doc1.relations.list_targets('related')

7.5   查源关系 list_sources

查看被引用的文件:

doc2.relations.list_sources('related')

7.6   调整关系内对象顺序 move_to

同一关系的多个target是排序的,可以调整顺序,移动到指定位置:

doc1.relations.move_to('related', doc2, index)

7.7   调整关系属性 set_metadta

设置关系的元数据(关系不存在不会建立该关系):

doc1.relations.set_metadata('related', doc2, {'number':01, 'size':23})

7.8   得到关系属性 get_metadata

得到关系的元数据(关系不存在返回None):

doc1.relations.get_metadata('related', doc2)

7.9   清除关系 clean

清除某种或所有的关系:

doc1.relations.clean(type='related')