内容通用接口
所有内容对象的通用属性和方法
站点的所有内容,提供一组公共的属性和方法。下面以 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)
其中:
- internal_: 是否是内部调用。如果是转换回掉,应该用内部的地址,使用True
- auth_:是否使用当前用户身份调用API
- 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.1 可以查出所有的关系类型 keys
doc1.relations.keys()
7.2 添加关系 add
doc1引用了doc2:
doc1.relations.add('related', doc2, metadata={})
其中:
- 第一个参数是关系名字
- 第二个参数是目标对象
- metadata: 关系说明信息
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})