跳转到主要内容

为Zope 3的关系字段框架。

项目描述

z3c.relationfield

简介

此包实现了一个新的模式字段 Relation,以及用于存储实际关系的 RelationValue 对象。它可以使用 zc.relation 基础设施对这些关系进行索引,并通过使用这些索引可以有效地回答关于关系的各种问题。

此外,包 z3c.relationfieldui 还提供了一个小部件来编辑和显示 Relation 字段。

设置

z3c.relationfield.Relation 是一个用于表达关系的模式字段。让我们定义一个使用关系字段的模式 IItem。

>>> from z3c.relationfield import Relation
>>> from zope.interface import Interface
>>> class IItem(Interface):
...   rel = Relation(title=u"Relation")

我们还定义了一个类 Item,它实现了 IItem 和特殊接口 z3c.relationfield.interfaces.IHasRelations

>>> from z3c.relationfield.interfaces import IHasRelations
>>> from persistent import Persistent
>>> from zope.interface import implementer
>>> @implementer(IItem, IHasRelations)
... class Item(Persistent):
...
...   def __init__(self):
...     self.rel = None

IHasRelations 标记接口是必要的,以便对 Item 上的关系进行编目(例如,当它们被放入容器中或从容器中移除时)。实际上,它是 IHasIncomingRelationsIHasOutgoingRelations 的组合,这对于我们想要支持双向关系来说是合适的。

最后,我们需要一个测试应用程序。

>>> from zope.site.site import SiteManagerContainer
>>> from zope.container.btree import BTreeContainer
>>> class TestApp(SiteManagerContainer, BTreeContainer):
...   pass

我们设置了测试应用程序。

>>> from ZODB.MappingStorage import DB
>>> db = DB()
>>> conn = db.open()
>>> root = conn.root()['root'] = TestApp()
>>> conn.add(root)

我们确保这是当前站点,这样我们就可以在其中查找本地实用程序等。通常这由 Zope 的遍历机制自动完成。

>>> from zope.site.site import LocalSiteManager
>>> root.setSiteManager(LocalSiteManager(root))
>>> from zope.component.hooks import setSite
>>> setSite(root)

为了使此站点与 z3c.relationship 一起工作,我们需要设置两个实用程序。首先是一个 IIntIds,它跟踪 ZODB 中对象的唯一标识符。

>>> from zope.intid import IntIds
>>> from zope.intid.interfaces import IIntIds
>>> root['intids'] = intids = IntIds()
>>> sm = root.getSiteManager()
>>> sm.registerUtility(intids, provided=IIntIds)

其次是一个关系目录,它实际上索引关系。

>>> from z3c.relationfield import RelationCatalog
>>> from zc.relation.interfaces import ICatalog
>>> root['catalog'] = catalog = RelationCatalog()
>>> sm.registerUtility(catalog, provided=ICatalog)

使用关系字段

我们将向我们的应用程序添加一个项目 a

>>> root['a'] = Item()

所有项目,包括我们刚刚创建的项目,都应该具有唯一的 int id,因为这需要将它们链接起来。

>>> from zope import component
>>> from zope.intid.interfaces import IIntIds
>>> intids = component.getUtility(IIntIds)
>>> a_id = intids.getId(root['a'])
>>> a_id >= 0
True

当前的关系是 None

>>> root['a'].rel is None
True

现在我们可以创建一个项目 b,它通过其 int id 链接到项目 a

>>> from z3c.relationfield import RelationValue
>>> b = Item()
>>> b.rel = RelationValue(a_id)

现在我们将 b 对象存储在容器中,这将设置其关系(因为将触发 IObjectAddedEvent)。

>>> root['b'] = b

让我们检查这个关系。首先,我们将检查指向对象(‘b’)的哪个属性是这个关系所指向的。

>>> root['b'].rel.from_attribute
'rel'

我们可以请求它所指向的对象。

>>> to_object = root['b'].rel.to_object
>>> to_object.__name__
'a'

我们还可以获取正在指向的对象;由于我们提供了 IHasRelations 接口,事件系统负责设置这一点。

>>> from_object = root['b'].rel.from_object
>>> from_object.__name__
'b'

此对象也称为 __parent__;同样,事件系统负责设置这一点。

>>> parent_object = root['b'].rel.__parent__
>>> parent_object is from_object
True

关系还知道指向对象和被指向对象的接口。

>>> from pprint import pprint
>>> pprint(sorted(root['b'].rel.from_interfaces))
[<InterfaceClass zope.location.interfaces.IContained>,
 <InterfaceClass z3c.relationfield.interfaces.IHasRelations>,
 <InterfaceClass builtins.IItem>,
 <InterfaceClass persistent.interfaces.IPersistent>]

>>> pprint(sorted(root['b'].rel.to_interfaces))
[<InterfaceClass zope.location.interfaces.IContained>,
 <InterfaceClass z3c.relationfield.interfaces.IHasRelations>,
 <InterfaceClass builtins.IItem>,
 <InterfaceClass persistent.interfaces.IPersistent>]

我们还可以以扁平形式获取接口。

>>> pprint(sorted(root['b'].rel.from_interfaces_flattened))
[<InterfaceClass zope.location.interfaces.IContained>,
 <InterfaceClass z3c.relationfield.interfaces.IHasIncomingRelations>,
 <InterfaceClass z3c.relationfield.interfaces.IHasOutgoingRelations>,
 <InterfaceClass z3c.relationfield.interfaces.IHasRelations>,
 <InterfaceClass builtins.IItem>,
 <InterfaceClass zope.location.interfaces.ILocation>,
 <InterfaceClass persistent.interfaces.IPersistent>,
 <InterfaceClass zope.interface.Interface>]

>>> pprint(sorted(root['b'].rel.to_interfaces_flattened))
[<InterfaceClass zope.location.interfaces.IContained>,
 <InterfaceClass z3c.relationfield.interfaces.IHasIncomingRelations>,
 <InterfaceClass z3c.relationfield.interfaces.IHasOutgoingRelations>,
 <InterfaceClass z3c.relationfield.interfaces.IHasRelations>,
 <InterfaceClass builtins.IItem>,
 <InterfaceClass zope.location.interfaces.ILocation>,
 <InterfaceClass persistent.interfaces.IPersistent>,
 <InterfaceClass zope.interface.Interface>]

路径

我们还可以获取关系的路径(从它所指向的地方到它所指向的地方)。该路径应该是人类可读的参考,适用于序列化。为了使用路径,我们首先需要设置一个 IObjectPath 实用程序。

由于在这个示例中我们只将对象放置在一个单个扁平根容器中,此演示中的路径可以非常简单:只是我们指向的对象的名称。在更复杂的应用程序中,路径通常是斜杠分隔的路径,例如 /foo/bar

>>> from zope.interface import Interface
>>> from zope.interface import implementer
>>> from z3c.objpath.interfaces import IObjectPath


>>> @implementer(IObjectPath)
... class ObjectPath(object):
...
...     def path(self, obj):
...         return obj.__name__
...     def resolve(self, path):
...         try:
...             return root[path]
...         except KeyError:
...             raise ValueError("Cannot resolve path %s" % path)

>>> from zope.component import getGlobalSiteManager
>>> gsm = getGlobalSiteManager()

>>> op = ObjectPath()
>>> gsm.registerUtility(op)

在此之后,我们可以获取关系指向的对象的路径。

>>> root['b'].rel.to_path
'a'

我们还可以获取正在指向的对象的路径。

>>> root['b'].rel.from_path
'b'

比较和排序关系

让我们创建一些 RelationValue 对象并比较它们。

>>> rel_to_a = RelationValue(a_id)
>>> b_id = intids.getId(root['b'])
>>> rel_to_b = RelationValue(b_id)
>>> rel_to_a == rel_to_b
False

当然,关系等于自身。

>>> rel_to_a == rel_to_a
True

存储的关系等于尚未存储的关系。

>>> root['b'].rel == rel_to_a
True

我们还可以对关系进行排序。

>>> expected = [('', 'a'), ('', 'b'), ('b', 'a')]
>>> observed = [(rel.from_path, rel.to_path) for rel in
...  sorted([root['b'].rel, rel_to_a, rel_to_b])]
>>> expected == observed
True

关系查询

现在我们已经设置并索引了 ab 之间的关系,我们可以使用关系目录发出查询。让我们首先获取目录。

>>> from zc.relation.interfaces import ICatalog
>>> catalog = component.getUtility(ICatalog)

让我们查询目录,了解从 ba 的关系。

>>> l = sorted(catalog.findRelations({'to_id': intids.getId(root['a'])}))
>>> l
[<...RelationValue object at ...>]

再次观察这个关系对象。我们确实找到了正确的关系。

>>> rel = l[0]
>>> rel.from_object.__name__
'b'
>>> rel.to_object.__name__
'a'
>>> rel.from_path
'b'
>>> rel.to_path
'a'

查询到 b 的关系将会得到一个空列表,因为没有设置这样的关系。

>>> sorted(catalog.findRelations({'to_id': intids.getId(root['b'])}))
[]

我们还可以发出更具体的查询,限制在用于关系字段的属性和由相关对象提供的接口上。在这里,我们寻找存储在对象属性 rel 中,并且从一个具有接口 IItem 的对象指向另一个具有接口 IItem 的对象的全部关系。

>>> sorted(catalog.findRelations({
...   'to_id': intids.getId(root['a']),
...   'from_attribute': 'rel',
...   'from_interfaces_flattened': IItem,
...   'to_interfaces_flattened': IItem}))
[<...RelationValue object at ...>]

没有存储其他属性的任何关系。

>>> sorted(catalog.findRelations({
...   'to_id': intids.getId(root['a']),
...   'from_attribute': 'foo'}))
[]

也没有存储我们将在这里介绍的新接口的关系。

>>> class IFoo(IItem):
...   pass

>>> sorted(catalog.findRelations({
...   'to_id': intids.getId(root['a']),
...   'from_interfaces_flattened': IItem,
...   'to_interfaces_flattened': IFoo}))
[]

更改关系

让我们创建一个新的对象 c

>>> root['c'] = Item()
>>> c_id = intids.getId(root['c'])

目前没有任何东西指向 c

>>> sorted(catalog.findRelations({'to_id': c_id}))
[]

我们目前有一个从 ba 的关系。

>>> sorted(catalog.findRelations({'to_id': intids.getId(root['a'])}))
[<...RelationValue object at ...>]

我们可以更改这个关系,使其指向新对象 c

>>> root['b'].rel = RelationValue(c_id)

我们需要发送一个 IObjectModifiedEvent,让目录知道我们已经更改了关系。

>>> from zope.event import notify
>>> from zope.lifecycleevent import ObjectModifiedEvent
>>> notify(ObjectModifiedEvent(root['b']))

现在我们应该找到一个从 bc 的单一关系。

>>> sorted(catalog.findRelations({'to_id': c_id}))
[<...RelationValue object at ...>]

a 的关系现在应该消失了。

>>> sorted(catalog.findRelations({'to_id': intids.getId(root['a'])}))
[]

如果我们在一个非模式字段中存储关系,它应该持久化 ObjectModifiedEvent。

>>> from z3c.relationfield.event import _setRelation
>>> _setRelation(root['b'], 'my-fancy-relation', rel_to_a)
>>> sorted(catalog.findRelations({'to_id': intids.getId(root['a'])}))
[<...RelationValue object at ...>]

>>> notify(ObjectModifiedEvent(root['b']))
>>> rel = sorted(catalog.findRelations({'to_id': intids.getId(root['a'])}))
>>> rel
[<...RelationValue object at ...>]

>>> catalog.unindex(rel[0])

删除关系

我们目前有一个从 bc 的关系。

>>> sorted(catalog.findRelations({'to_id': c_id}))
[<...RelationValue object at ...>]

我们可以通过将其设置为 None 来清理从 bc 的现有关系。

>>> root['b'].rel = None

我们需要发送一个 IObjectModifiedEvent,让目录知道我们已经更改了关系。

>>> notify(ObjectModifiedEvent(root['b']))

b 上的关系设置为 None 应该从关系目录中删除该关系,因此我们不应该再找到它。

>>> sorted(catalog.findRelations({'to_id': intids.getId(root['c'])}))
[]

让我们重新建立已删除的关系。

>>> root['b'].rel = RelationValue(c_id)
>>> notify(ObjectModifiedEvent(root['b']))

>>> sorted(catalog.findRelations({'to_id': c_id}))
[<...RelationValue object at ...>]

复制带有关系的对象

让我们复制一个带有关系的对象。

>>> from zope.copypastemove.interfaces import IObjectCopier
>>> IObjectCopier(root['b']).copyTo(root)
'b-2'
>>> 'b-2' in root
True

现在可以找到两个指向 c 的关系,一个来自原始对象,另一个来自副本。

>>> l = sorted(catalog.findRelations({'to_id': c_id}))
>>> len(l)
2
>>> l[0].from_path
'b'
>>> l[1].from_path
'b-2'

关系是可排序的

默认情况下,关系根据关系名称、关系所在对象的路径以及关系所指向对象的路径的组合进行排序。

让我们查询所有当前可用的关系并对其进行排序。

>>> l = sorted(catalog.findRelations())
>>> len(l)
2
>>> l[0].from_attribute
'rel'
>>> l[1].from_attribute
'rel'
>>> l[0].from_path
'b'
>>> l[1].from_path
'b-2'

删除带有关系的对象

我们将再次删除 b-2。其关系应自动从目录中删除。

>>> del root['b-2']
>>> l = sorted(catalog.findRelations({'to_id': c_id}))
>>> len(l)
1
>>> l[0].from_path
'b'

中断关系

我们目前有一个从 bc 的关系。

>>> sorted(catalog.findRelations({'to_id': c_id}))
[<...RelationValue object at ...>]

我们没有断开的关系。

>>> sorted(catalog.findRelations({'to_id': None}))
[]

这个关系没有断开。

>>> b.rel.isBroken()
False

我们现在将通过删除 c 来断开这个关系。

>>> del root['c']

这个关系现在已断开。

>>> b.rel.isBroken()
True

原始关系仍然有一个 to_path

>>> b.rel.to_path
'c'

然而,它是断开的,因为没有 to_object

>>> b.rel.to_object is None
True

to_id 也消失了。

>>> b.rel.to_id is None
True

我们不能以这种方式在目录中找到断开的关系,因为它不再指向 c_id

>>> sorted(catalog.findRelations({'to_id': c_id}))
[]

然而,我们可以通过搜索具有 to_idNone 的关系来找到它。

>>> sorted(catalog.findRelations({'to_id': None}))
[<...RelationValue object at ...>]

断开的关系不等于 None(这是一个错误)。

>>> b.rel == None
False

RelationChoice

一个 RelationChoice 字段与一个普通的 Relation 字段非常相似,但可以用来渲染一个允许选择的项目特殊小部件。

我们将首先演示一个 RelationChoice 字段与一个 Relation 字段本身具有相同的效果

>>> from z3c.relationfield import RelationChoice
>>> class IChoiceItem(Interface):
...   rel = RelationChoice(title=u"Relation", values=[])
>>> @implementer(IChoiceItem, IHasRelations)
... class ChoiceItem(Persistent):
...
...   def __init__(self):
...     self.rel = None

让我们创建一个对象来指向这个关系

>>> root['some_object'] = Item()
>>> some_object_id = intids.getId(root['some_object'])

然后,我们建立这个关系

.. 代码块:

python

>>> choice_item = ChoiceItem()
>>> choice_item.rel = RelationValue(some_object_id)
>>> root['choice_item'] = choice_item

现在我们可以查询这个关系了

>>> l = sorted(catalog.findRelations({'to_id': some_object_id}))
>>> l
[<...RelationValue object at ...>]

关系列表

现在让我们尝试使用 RelationList 字段,它可以用来维护关系列表

>>> from z3c.relationfield import RelationList
>>> class IMultiItem(Interface):
...   rel = RelationList(title=u"Relation")

我们还定义了一个 MultiItem 类,它实现了 IMultiItem 以及特殊的 z3c.relationfield.interfaces.IHasRelations 接口

>>> @implementer(IMultiItem, IHasRelations)
... class MultiItem(Persistent):
...
...   def __init__(self):
...     self.rel = None

我们设置了一些对象,然后在这些对象之间建立关系

>>> root['multi1'] = MultiItem()
>>> root['multi2'] = MultiItem()
>>> root['multi3'] = MultiItem()

让我们从 multi1 创建到 multi2multi3 的关系

>>> multi1_id = intids.getId(root['multi1'])
>>> multi2_id = intids.getId(root['multi2'])
>>> multi3_id = intids.getId(root['multi3'])

>>> root['multi1'].rel = [RelationValue(multi2_id),
...                       RelationValue(multi3_id)]

我们需要通知已经修改了 ObjectModifiedEvent

>>> notify(ObjectModifiedEvent(root['multi1']))

现在这个已经设置好了,让我们验证是否可以在目录中找到合适的关系

>>> len(list(catalog.findRelations({'to_id': multi2_id})))
1
>>> len(list(catalog.findRelations({'to_id': multi3_id})))
1
>>> len(list(catalog.findRelations({'from_id': multi1_id})))
2

临时关系

如果我们有一个导入程序,从外部源(如XML文件)导入关系,那么可能我们读取的关系指向的对象还不存在,因为它尚未被导入。我们为此情况提供了一个特殊的 TemporaryRelationValue。一个 TemporaryRelationValue 只包含它指向的路径,但尚未解析。让我们在一个新对象中使用 TemporaryRelationValue,创建一个指向 a 的关系

>>> from z3c.relationfield import TemporaryRelationValue
>>> root['d'] = Item()
>>> root['d'].rel = TemporaryRelationValue('a')

修改事件实际上并没有将这个关系编入目录

>>> before = sorted(catalog.findRelations({'to_id': a_id}))
>>> notify(ObjectModifiedEvent(root['d']))
>>> after = sorted(catalog.findRelations({'to_id': a_id}))
>>> len(before) == len(after)
True

现在我们将 d 上的所有临时关系转换为真实的关系

>>> from z3c.relationfield import realize_relations
>>> realize_relations(root['d'])
>>> notify(ObjectModifiedEvent(root['d']))

现在我们可以看到真实的关系对象

>>> root['d'].rel
<...RelationValue object at ...>

关系现在也会出现在目录中

>>> after2 = sorted(catalog.findRelations({'to_id': a_id}))
>>> len(after2) > len(before)
True

临时关系值也与 RelationList 对象一起工作

>>> root['multi_temp'] = MultiItem()
>>> root['multi_temp'].rel = [TemporaryRelationValue('a')]

让我们将其转换为真实的关系

>>> realize_relations(root['multi_temp'])
>>> notify(ObjectModifiedEvent(root['multi_temp']))

再次,当我们查看它时,我们可以看到真实的关系对象

>>> root['multi_temp'].rel
[<...RelationValue object at ...>]

现在我们将在目录中看到这个新关系

>>> after3 = sorted(catalog.findRelations({'to_id': a_id}))
>>> len(after3) > len(after2)
True

损坏的临时关系

让我们创建另一个临时关系,这次是一个无法解析的损坏的关系

>>> root['e'] = Item()
>>> root['e'].rel = TemporaryRelationValue('nonexistent')

让我们尝试实现这个关系

>>> realize_relations(root['e'])

我们最终得到了一个损坏的关系

>>> root['e'].rel.isBroken()
True

它指向一个不存在的路径

>>> root['e'].rel.to_path
'nonexistent'

建立关系目录

此包提供了一个初始化了用于查询 RelationValue 对象的常用索引集的 RelationCatalog。默认索引包括 from_idto_idfrom_attributefrom_interfaces_flattenedto_interfaces_flattened

有时需要定义自定义索引或使用少于默认的索引。可以使用包含键 elementkwargs 的字典列表初始化 zc.relationfield.index.RelationCatalog 类,这些键将被传递给 RelationCatalogaddValueIndex 方法。通常,elementIRelationValue 上的属性,如 IRelationValue['from_id']。然而,如果使用了具有额外字段的 IRelationValue 子类,则可以将这些字段添加到这里作为索引。

变更

1.1 (2023-08-17)

  • 考虑没有来源的 RelationValue 为损坏。 [ksuess]

1.0 (2023-02-22)

破坏性变更

  • 停止支持 Python 2.7、3.5、3.6。

新功能

  • 添加对 Python 3.7、3.8、3.9、3.10、3.11 的支持。

0.9.0 (2019-09-15)

新功能

  • 提供 IRelationBrokenEvent,以便在订阅 IObjectModifiedEvent 时能够区分事件 [vangheem]

0.8.0 (2019-02-13)

新功能

错误修复

  • 修复 tests.py 中的 DeprecationWarnings。[jensens]

0.7.1 (2018-11-08)

  • Python 3 兼容性:使用实现者装饰器并修复排序。[ale-rt]

  • Python 3 兼容性:使 RelationValue 可哈希。[sallner]

  • README.txt 重命名为 README.rst,将 CHANGES.txt 重命名为 CHANGES.rst。[thet]

  • 更新 buildout / travis 配置。[tomgross]

  • 修复当关系不是作为类属性存储时修改关系会被清除的问题。用例请参阅 https://github.com/plone/Products.CMFPlone/issues/2384。[tomgross]

0.7 (2015-03-13)

  • 移除对 zope.app.* 的依赖。[davisagli]

0.6.3 (2014-04-15)

  • 移除对 grok 的依赖。[pbauer, jensens]

0.6.2 (2012-12-06)

  • 更新测试设置和测试,使其与依赖包的当前版本一起运行,因此也可以在 Python 2.6 下运行。

  • 添加缺失的(测试)依赖。

  • 将 __neq__ 方法重命名为 __ne__,因为 __neq__ 不是 != 处理器的正确内置名称。

0.6.1 (2009-10-11)

  • 修复损坏的发布版本。

0.6 (2009-10-11)

  • 确保在构建字段时,关系列表的 value_type 不会被覆盖为 'None'。

0.5 (2009-06-30)

  • 将 lxml 和 schema2xml 依赖项移动到 [xml] 额外依赖项中,这样人们就可以使用此包而无需安装 lxml,它仍在一些平台上引起问题。如果 z3c.schema2xml 和 lxml 无法导入,则不会定义相关适配器,但其他一切仍然正常工作。

  • 订阅到 IIntIdAddedEvent 而不是 IObjectAddedEvent,以防止由于订阅者排序错误而导致的错误。

0.4.3 (2009-06-04)

  • 添加缺少的 lxml 依赖。

0.4.2 (2009-04-22)

  • 防止在缺少工具或对象未实现 IContained 时事件失败。

0.4.1 (2009-02-12)

  • 不要处理尚未具有父对象的对象的 IObjectModified 事件。实际上也没有必要这样做,因为这些对象无法有出向关系索引。

0.4 (2009-02-10)

  • 引入一个 RelationChoice 字段,它类似于 schema.Choice,但跟踪关系。与源(如由 z3c.relationfieldui 提供的 RelationSourceFactory 创建的源)结合使用,这可以用于创建关系的下拉选择。

  • 阐明比较和排序 RelationValue 对象的方式,以更好地支持选择支持。

0.3.2 (2009-01-21)

  • 当关系损坏时,正确地重新编目事物。

0.3.1 (2009-01-20)

  • 基于 (from_attribute, from_path, to_path) 元组引入合理的排序顺序。

  • 关系现在永远不会与 None 进行比较。

0.3 (2009-01-19)

  • 引入两个新的接口:IHasOutgoingRelationsIHasIncomingRelationsIHasOutgoingRelations 应由实际上设置了关系的对象提供,以便它们可以正确编目。 IHasIncomingRelations 应设置在可以关联的对象上,以便可以正确跟踪损坏的关系。IHasRelations 现在扩展了这两个接口,因此如果您在对象上提供这些接口,则该对象可以具有出向关系和入向关系。

  • 改进损坏关系支持。现在,当您断开关系(通过删除关系目标)时,to_idto_object 变为 None。然而,to_path 将保持关系最后指向的路径。也可以创建 TemporaryRelation 对象,这些对象在实现时是损坏的关系。

    您还可以通过在关系上调用 isBroken 来检查损坏状态。

  • 顶层函数 create_relation 的签名已更改。它原本接受要创建关系的对象,但现在应该接收路径(以 IObjectPath 术语表示)。如果路径无法解析,create_relation 现在将创建一个损坏的关系对象。

0.2 (2009-01-08)

  • 添加了对 RelationList 字段的支持。这允许用户维护一个 RelationValue 对象的列表,这些对象将像常规 Relation 字段一样被编目。

  • 取消了对 IRelationInfo 适配器的需求。只需定义一个执行相同工作的 create_relation 函数。

  • 在查找对象上的关系时,如果找不到这些关系,则应更加宽容(只需跳过它们) - 这可能发生在模式更改时。

0.1 (2008-12-05)

  • 首次公开发布。

下载

项目详情


下载文件

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

源分发

z3c.relationfield-1.1.tar.gz (32.7 kB 查看哈希值)

上传时间

构建分发

z3c.relationfield-1.1-py3-none-any.whl (26.1 kB 查看哈希值)

上传时间 Python 3

由以下支持

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