解决Plone词汇表工厂无法获取门户对象及读取注解数据问题
解决Plone词汇表工厂无法访问门户注解的问题
背景
我将JSON格式的术语定义存储在门户对象的注解中,更新定义时只需上传新的JSON文件即可。目前正尝试为内容类型创建一个行为:新增字段让用户从当前定义中选择术语。
问题
填充词汇表的函数无法访问注解,执行api.portal.get()时触发以下错误:
(Pdb) from plone import api (Pdb) api.portal.get() *** plone.api.exc.CannotGetPortalError: Unable to get the portal object.
当前代码
vocabulary.py
def generic_vocabulary(_terms, sort=True): """Returns a zope vocabulary from a dict or a list""" if _terms and isinstance(_terms, dict): _terms = _terms.items() elif _terms and isinstance(_terms[0], str): _terms = [(x, x) for x in _terms] if sort: _terms = sorted(_terms, key=lambda x: x[0]) def factory(context): """Simple Vocabulary factory""" return SimpleVocabulary( [SimpleTerm(n, n.encode("utf-8"), term) for n, term in _terms] ) return factory _terms_list = ( ("key1", "Value 1"), ("key2", "Value 2"), ) def generate_terms_list(): import pdb pdb.set_trace() # TODO Get the list of terms from annotations # from plone import api # api.portal.get() ERROR HERE return _terms_list terms_list = generic_vocabulary(generate_terms_list()) alsoProvides(terms_list, IVocabularyFactory)
configure.zcml
<utility name="my.add.on.terms" component=".vocabulary.terms_list" />
behavior.py
from plone.autoform import directives from plone.autoform.interfaces import IFormFieldProvider from plone.supermodel import model from zope import schema from zope.interface import provider @provider(IFormFieldProvider) class ICustomTermsListBehavior(model.Schema): terms_list = schema.List( title="Terms List", description="Select Terms", required=False, value_type=schema.Choice(vocabulary="my.add.on.terms"), ) directives.write_permission(terms_list="cmf.ManagePortal")
我期望将演示术语替换为注解中的真实内容,预期读取注解的代码如下:
def get_annot(): annot_key = 'TEST_KEY' container = api.portal.get() annotations = IAnnotations(container) return annotations[annot_key]
已尝试的方法(均失败)
(Pdb) from zope.component import getUtility (Pdb) from Products.CMFCore.interfaces import ISiteRoot (Pdb) portal = getUtility(ISiteRoot) *** zope.interface.interfaces.ComponentLookupError: (<InterfaceClass Products.CMFCore.interfaces.ISiteRoot>, '')
(Pdb) site = api.portal.getSite() (Pdb) site is None True
(Pdb) from zope.component.hooks import getSite (Pdb) getSite() is None True
解决方案
问题核心是模块加载阶段就执行了generate_terms_list,此时Plone站点上下文尚未初始化,无法获取门户对象。正确做法是将动态获取术语的逻辑移到接收context参数的工厂函数内部,利用请求时传入的上下文定位门户。
修改后的vocabulary.py代码:
from plone import api from zope.annotation.interfaces import IAnnotations from zope.schema.vocabulary import SimpleVocabulary, SimpleTerm from zope.interface import alsoProvides, IVocabularyFactory def generic_vocabulary(sort=True): """Returns a zope vocabulary factory that fetches terms from portal annotations""" def factory(context): """Vocabulary factory that uses context to access portal""" # 通过传入的上下文获取门户对象 portal = api.portal.get(context=context) annot_key = 'TEST_KEY' annotations = IAnnotations(portal) # 从注解中读取术语数据,兼容字典或列表格式 _terms = annotations.get(annot_key, {}) if _terms and isinstance(_terms, dict): _terms = _terms.items() elif _terms and isinstance(_terms[0], str): _terms = [(x, x) for x in _terms] if sort: _terms = sorted(_terms, key=lambda x: x[0]) return SimpleVocabulary( [SimpleTerm(n, n.encode("utf-8"), term) for n, term in _terms] ) return factory # 直接生成词汇表工厂,避免模块加载时提前执行数据获取逻辑 terms_list = generic_vocabulary() alsoProvides(terms_list, IVocabularyFactory)
关键改动:
- 将术语获取逻辑移到
factory函数内部,利用请求时传入的context定位门户 - 移除模块加载阶段执行的
generate_terms_list,避免访问未初始化的上下文 - 使用
api.portal.get(context=context)确保在任意请求场景下都能正确获取门户对象
修改后,当表单调用词汇表时,会传入当前内容对象作为上下文,通过该上下文即可读取门户注解中的术语数据。
内容的提问来源于stack exchange,提问作者GhitaB
相关产品推荐
相关产品推荐

