跳转到主要内容

plone.app.relations项的Zope 3模式。

项目描述

此扩展的目的是提供Zope 3模式以供plone关系使用。已在Plone 2.5和Plone 3上进行了测试。

接口定义

新字段

>>> from infrae.plone.relations.schema import PloneRelation

一个包含字段的简单接口

>>> from zope.interface import Interface, implements
>>> class IContent(Interface):
...    """Sample interface."""
...    relation = PloneRelation(relation="my relation")

我可以从接口中获取字段

>>> r_field = IContent.get('relation')

这是一个字段

>>> from zope.interface.verify import verifyObject
>>> from zope.schema.interfaces import IField
>>> verifyObject(IField, r_field)
True

现在,一个查找反向关系的字段

>>> class IBackContent(Interface):
...    """Sample interface."""
...    relation = PloneRelation(relation="my relation",
...                             reverse=True)

为测试目的创建简单内容

一个简单的实现

>>> from OFS.Folder import Folder
>>> class BaseContent(Folder):
...    def __init__(self, id):
...       super(BaseContent, self).__init__()
...       self.id = id
...    def UID(self):
...       return 'uid-%s' % self.id

上下文工厂使用UID。

标准基类

>>> class MyContent(BaseContent):
...    implements(IContent)

并且

>>> class MyBackContent(BaseContent):
...    implements(IBackContent)

现在,创建一些项目

>>> for id in range(1, 5):
...    name = 'it%d' % id
...    item = MyContent(name)
...    self.portal._setObject(name, item)
'it1'
'it2'
'it3'
'it4'
>>> it1 = self.portal.it1
>>> it2 = self.portal.it2
>>> it3 = self.portal.it3
>>> it4 = self.portal.it4

>>> itb1 = MyBackContent('itb1')
>>> self.portal._setObject('itb1', itb1)
'itb1'
>>> itb1 = self.portal.itb1

显示关系的实用程序

显示关系的辅助程序

>>> def display(rels):
...     for rel in rels:
...        print "Objects: %s" % list(rel['objects'])
...        if rel.has_key("context"):
...            print "Context: %s" % rel['context']
...     if not rels:
...        print "Empty"

简单字段使用

直接设置和反向访问

并对数据进行一些验证。数据是代表字段所有关系的字典列表。在字典中

  • objects: 代表关系对象列表;

  • 上下文:可能是作为关系上下文存储的对象。

示例

>>> bad_relation1 = [{'bad': None},]
>>> r_field.validate(bad_relation1)
Traceback (most recent call last):
  ...
ValidationError: Invalid structure
>>> good_relation = [{'objects': [it2, itb1]},]
>>> r_field.validate(good_relation)

并设置字段

>>> r_field.set(it1, good_relation)

并从字段获取数据

>>> relation = r_field.get(it1)
>>> relation
[{'objects': <plone.relations.relationships.IntIdSubObjectWrapper object at ...>}]
>>> display(relation)
Objects: [<MyContent at /plone/it2>, <MyBackContent at /plone/itb1>]

现在,我们可以从itb1内容中提问,它有一个反向字段

>>> rb_field = IBackContent.get('relation')
>>> relation = rb_field.get(itb1)
>>> relation
[{'objects': <plone.relations.relationships.IntIdSubObjectWrapper object at ...>}]
>>> display(relation)
Objects: [<MyContent at /plone/it1>]

我们更新关系

>>> good_relation = [{'objects': [it2, it3]},]
>>> r_field.set(it1, good_relation)

所以变化被反映出来

>>> display(r_field.get(it1))
Objects: [<MyContent at /plone/it2>, <MyContent at /plone/it3>]

并且反向字段中没有更多关系

>>> rb_field = IBackContent.get('relation')
>>> rb_field.get(itb1)
[]

现在,在反向字段上设置

>>> good_relation = [{'objects': [it1, it2]}]
>>> rb_field.set(itb1, good_relation)
>>> display(rb_field.get(itb1))
Objects: [<MyContent at /plone/it1>, <MyContent at /plone/it2>]

并在正常字段上

>>> display(r_field.get(it1))
Objects: [<MyContent at /plone/it2>, <MyContent at /plone/it3>]
Objects: [<MyBackContent at /plone/itb1>]

删除

您可以通过将关系设置为空列表 [] 来删除值

>>> display(r_field.get(it2))
Objects: [<MyBackContent at /plone/itb1>]
>>> r_field.set(it2, [])
>>> display(r_field.get(it2))
Empty
>>> display(rb_field.get(itb1))
Objects: [<MyContent at /plone/it1>]

并且

>>> display(r_field.get(it1))
Objects: [<MyContent at /plone/it2>, <MyContent at /plone/it3>]
Objects: [<MyBackContent at /plone/itb1>]
>>> r_field.set(it1, [])
>>> display(r_field.get(it1))
Empty
>>> display(rb_field.get(itb1))
Empty

字段独立性

另一个关系模式

>>> class IComplexContent(Interface):
...    """A content with two relation."""
...    relation1 = PloneRelation(relation="relation1")
...    relation2 = PloneRelation(relation="relation2")

以及相关内容

>>> class MyComplexContent(BaseContent):
...    implements(IComplexContent)

创建三个这样的对象

>>> itcx1 = MyComplexContent("itcx1")
>>> self.portal._setObject("itcx1", itcx1)
'itcx1'
>>> itcx1 = self.portal.itcx1
>>> itcx2 = MyComplexContent("itcx2")
>>> self.portal._setObject("itcx2", itcx2)
'itcx2'
>>> itcx2 = self.portal.itcx2
>>> itcx3 = MyComplexContent("itcx3")
>>> self.portal._setObject("itcx3", itcx3)
'itcx3'
>>> itcx3 = self.portal.itcx3

现在,添加关系

>>> r1_field = IComplexContent.get("relation1")
>>> r1_field.set(itcx1, [{'objects': [itcx2,]}])
>>> display(r1_field.get(itcx1))
Objects: [<MyComplexContent at /plone/itcx2>]
>>> r2_field = IComplexContent.get("relation2")
>>> r2_field.set(itcx1, [{'objects': [itcx3,]}])
>>> display(r2_field.get(itcx1))
Objects: [<MyComplexContent at /plone/itcx3>]

并删除一个

>>> r2_field.set(itcx1, [])
>>> display(r2_field.get(itcx1))
Empty
>>> display(r1_field.get(itcx1))
Objects: [<MyComplexContent at /plone/itcx2>]

更多约束

现在,您必须至少提供1个值,最多不超过3个

>>> class ILengthContent(Interface):
...    """Sample interface with length control."""
...    relation = PloneRelation(relation="my relation",
...                             min_length=1,
...                             max_length=3)

该字段实现了 IMinMaxLen

>>> from zope.schema.interfaces import IMinMaxLen
>>> rl_field = ILengthContent.get('relation')
>>> verifyObject(IMinMaxLen, rl_field)
True

好的,现在一些尝试

>>> bad_relation = []
>>> rl_field.validate(bad_relation)
Traceback (most recent call last):
  ...
TooSmall: Less than 1 values

>>> bad_relation = [{'objects': [it2,]},
...                 {'objects': [it3,]},
...                 {'objects': [it4,]},
...                 {'objects': [itb1,]},]
>>> rl_field.validate(bad_relation)
Traceback (most recent call last):
  ...
TooBig: More than 3 values

现在,一个正确

>>> good_relation = [{'objects': [it2,]},]
>>> rl_field.validate(good_relation)

但我们还希望关系中有唯一的对象

>>> class IUniqueContent(Interface):
...    """Sample interface only one item per relation."""
...    relation = PloneRelation(relation="my relation",
...                             unique=True)
>>> ru_field = IUniqueContent.get('relation')

现在尝试一些

>>> bad_relation = [{'objects': [it2, it3,]}]
>>> ru_field.validate(bad_relation)
Traceback (most recent call last):
  ...
ValidationError: Not uniques values in relation

>>> good_relation = [{'objects': [it2,]}]
>>> ru_field.validate(good_relation)

我们希望关系中的每个对象都实现一个特定的接口

>>> class IConstraintContent(Interface):
...    """Sample interface with constraint on relation."""
...    relation = PloneRelation(relation="my relation",
...                             relation_schema=IUniqueContent)
>>> rs_field = IConstraintContent.get('relation')

上下文对象的使用

两个接口让您可以处理上下文对象

>>> from infrae.plone.relations.schema import IPloneRelationContext
>>> from infrae.plone.relations.schema import IPloneRelationContextFactory

接下来的两个导入是辅助工具,但您可以使用它们,因为它们是良好的内容开始

>>> from infrae.plone.relations.schema import BasePloneRelationContext
>>> from infrae.plone.relations.schema import BasePloneRelationContextFactory

以下上下文接口

>>> class IContextObject(IPloneRelationContext):
...    """Simple context object."""

以及对应的对象

>>> class MyContextObject(BasePloneRelationContext):
...    implements(IContextObject)

我们将这样声明字段

>>> class IContentWithContext(Interface):
...    """Simple content with a context."""
...    relation = PloneRelation(relation="context relation",
...                             context_schema=IContextObject)

我们想要具有此模式的对象

>>> class MyContentWithContext(BaseContent):
...    implements(IContentWithContext)

创建对象

>>> itc1 = MyContentWithContext('itc1')
>>> self.portal._setObject('itc1', itc1)
'itc1'
>>> itc1 = self.portal.itc1

准备一个上下文对象

>>> ctxt_fac = BasePloneRelationContextFactory(MyContextObject, IContextObject)
>>> verifyObject(IPloneRelationContextFactory, ctxt_fac)
True
>>> ctxt1 = ctxt_fac(itc1, it1, dict())
>>> ctxt1
<MyContextObject at /plone/itc1/uid-it1>
>>> verifyObject(IContextObject, ctxt1)
True

获取字段

>>> rc_field = IContentWithContext.get('relation')

现在我们可以尝试这个关系

>>> bad_relation = [{'objects': [it2, itb1,], 'context': it3,}]
>>> rc_field.validate(bad_relation)
Traceback (most recent call last):
  ...
ValidationError: Invalid context
>>> good_relation = [{'objects': [it2, itb1,], 'context': ctxt1,}]
>>> rc_field.validate(good_relation)
>>> rc_field.set(itc1, good_relation)

如果我们查询关系

>>> display(rc_field.get(itc1))
Objects: [<MyContent at /plone/it2>, <MyBackContent at /plone/itb1>]
Context: <MyContextObject at uid-it1>

多对多关系接口

此接口提供了一个比 plone.app.relations 提供的更通用的关系编辑方式,以使 Zope 3 模式在两种方式(关系正常访问和反向访问)中工作。

创建简单内容

>>> from OFS.SimpleItem import SimpleItem
>>> class BaseContent(SimpleItem):
...    def __init__(self, id):
...       super(BaseContent, self).__init__()
...       self.id = id


>>> for num in range(1, 20):
...    id = 'it%02d' % num
...    it = BaseContent(id)
...    _ = self.portal._setObject(id, it)

>>> self.portal.it01
<BaseContent at /plone/it01>

内容必须是 IPersistent

>>> from persistent import IPersistent
>>> from zope.interface.verify import verifyObject
>>> verifyObject(IPersistent, self.portal.it01)
True

接口的简单测试

我们有一个新的适配器来处理您的关联

>>> from infrae.plone.relations.schema import IManyToManyRelationship
>>> manager = IManyToManyRelationship(self.portal.it01)
>>> verifyObject(IManyToManyRelationship, manager)
True

好的,尝试添加关系

>>> rel = manager.createRelationship((self.portal.it11, self.portal.it12,),
...                                  sources=(self.portal.it02,),
...                                  relation='test')
>>> list(rel.sources)
[<BaseContent at /plone/it01>, <BaseContent at /plone/it02>]
>>> list(rel.targets)
[<BaseContent at /plone/it11>, <BaseContent at /plone/it12>]

现在,我们可以检索关系列表

>>> list(manager.getRelationships())
[<Relationship 'test' from (<BaseContent at /plone/it01>, <BaseContent at /plone/it02>) to (<BaseContent at /plone/it11>, <BaseContent at /plone/it12>)>]

方向

您可以使用 setDirection 方法反转关系的工作方式

>>> rel = manager.createRelationship(self.portal.it05, relation='reverse')
>>> list(rel.targets)
[<BaseContent at /plone/it05>]
>>> manager.setDirection(False)
>>> rel = manager.createRelationship(self.portal.it04, relation='reverse')
>>> list(rel.targets)
[<BaseContent at /plone/it01>]

您还有搜索的可传递性

>>> manager = IManyToManyRelationship(self.portal.it04)
>>> list(manager.getRelationshipChains(relation='reverse',
...                                    target=self.portal.it05,
...                                    maxDepth=2))
[(<Relationship 'reverse' from (<BaseContent at /plone/it04>,) to (<BaseContent at /plone/it01>,)>, <Relationship 'reverse' from (<BaseContent at /plone/it01>,) to (<BaseContent at /plone/it05>,)>)]

但是关系总是从源到目标进行跟踪。因此,如果我们反转搜索,我们将找不到结果

>>> manager.setDirection(False)
>>> list(manager.getRelationshipChains(relation='reverse',
...                                    target=self.portal.it05,
...                                    maxDepth=2))
[]

方向仅更改关系对象上源或目标的意义。它不会改变关系本身。

具有传递性的更大示例

回顾第一个测试,并添加一个套件

>>> manager = IManyToManyRelationship(self.portal.it16)
>>> manager.setDirection(False)
>>> rel = manager.createRelationship((self.portal.it12, self.portal.it14),
...                                  relation='test')
>>> manager.setDirection(True)
>>> rel = manager.createRelationship((self.portal.it17, self.portal.it18),
...                                  sources=(self.portal.it19,),
...                                  relation='test')

新的链式尝试

>>> manager = IManyToManyRelationship(self.portal.it02)
>>> list(manager.getRelationshipChains(relation='test',
...                                    target=self.portal.it18,
...                                    maxDepth=3))
[(<Relationship 'test' from (<BaseContent at /plone/it01>, <BaseContent at /plone/it02>) to (<BaseContent at /plone/it11>, <BaseContent at /plone/it12>)>, <Relationship 'test' from (<BaseContent at /plone/it12>, <BaseContent at /plone/it14>) to (<BaseContent at /plone/it16>,)>, <Relationship 'test' from (<BaseContent at /plone/it16>, <BaseContent at /plone/it19>) to (<BaseContent at /plone/it17>, <BaseContent at /plone/it18>)>)]

访问器

getTargets 返回与给定对象作为源的相关对象的延迟列表,而 getSources 返回与给定对象作为目标的相关对象的延迟列表

>>> manager = IManyToManyRelationship(self.portal.it16)
>>> list(manager.getTargets())
[<BaseContent at /plone/it17>, <BaseContent at /plone/it18>]
>>> list(manager.getSources())
[<BaseContent at /plone/it12>, <BaseContent at /plone/it14>]

如果我们反转方向

>>> manager.setDirection(False)
>>> list(manager.getTargets())
[<BaseContent at /plone/it12>, <BaseContent at /plone/it14>]
>>> list(manager.getSources())
[<BaseContent at /plone/it17>, <BaseContent at /plone/it18>]

删除

删除关系

>>> manager.setDirection(True)
>>> manager.deleteRelationship()
>>> list(manager.getRelationships())
[]

>>> manager = IManyToManyRelationship(self.portal.it19)
>>> list(manager.getRelationships())
[<Relationship 'test' from (<BaseContent at /plone/it19>,) to (<BaseContent at /plone/it17>, <BaseContent at /plone/it18>)>]

>>> manager.deleteRelationship(target=self.portal.it17)
>>> list(manager.getRelationships())
[<Relationship 'test' from (<BaseContent at /plone/it19>,) to (<BaseContent at /plone/it18>,)>]

>>> manager.deleteRelationship()
>>> list(manager.getRelationships())
[]

>>> manager = IManyToManyRelationship(self.portal.it01)
>>> manager.deleteRelationship(remove_all_sources=True, multiple=True)
>>> manager = IManyToManyRelationship(self.portal.it02)
>>> list(manager.getRelationships())
[]

变更

1.0

  • 首次发布。

鸣谢

由比利时弗拉芒政府提供支持,用于应用程序 <http://www.zonderisgezonder.be>.

项目详情


下载文件

下载您平台上的文件。如果您不确定要选择哪个,请了解有关 安装包 的更多信息。

源分发

infrae.plone.relations.schema-1.0.tar.gz (12.5 kB 查看散列)

上传时间

构建分发

infrae.plone.relations.schema-1.0-py2.4.egg (28.5 kB 查看散列)

上传时间

由以下支持

AWS AWS 云计算和安全赞助商 Datadog Datadog 监控 Fastly Fastly CDN Google Google 下载分析 Microsoft Microsoft PSF 赞助商 Pingdom Pingdom 监控 Sentry Sentry 错误日志 StatusPage StatusPage 状态页面