我想在删除其中的项目后重定向到容器的父级。为此,我尝试订阅zope.lifecycleevent's IObjectRemovedEvent
:
@grok.subscribe(ISite, IObjectRemovedEvent)
def redirect_to_trial_on_delete(obj, event):
request = getattr(obj, 'REQUEST', None)
if request:
trial_url = obj.aq_parent.aq_parent.absolute_url()
request.response.redirect(trial_url)
点击container/id/delete_confirmation
会触发删除,但这会引发比我预期更多的事件。我的订阅功能被调用两次:一次点击链接,然后再次确认删除。更令人困惑的是,如果我取消删除,它也会被 调用。我期待事件只被提出如果一个对象,你知道,从容器中删除。
在所有三种情况中,事件对象是相同的,具有oldName,oldParent等相同的属性值。
如何区分要求删除项目,取消该请求以及实际删除项目?
更新:所以似乎调用了初始事件,因为要从容器中删除对象以检查链接完整性,此时会有回滚。
答案 0 :(得分:3)
一位同事提出了一个有效的解决方案:
import transaction
def redirect_to_trial(trans, obj=None, parent=None):
if obj.id not in parent:
request = getattr(obj, 'REQUEST', None)
if request:
trial_url = obj.__parent__.__parent__.absolute_url()
request.response.redirect(trial_url)
@grok.subscribe(ISite, IObjectRemovedEvent)
def on_site_delete(obj, event):
kwargs = dict(
obj = obj,
parent = event.oldParent,
)
transaction.get().addAfterCommitHook(redirect_to_trial, kws=kwargs)
这在提交之后进行检查,以确保在执行重定向之前已经删除了对象。
虽然可以理解是否这是一种合适的方法。
答案 1 :(得分:1)
这是另一种可能性,同样来自同一天才的同事:
from zope.interface import implements
from transaction.interfaces import ISavepointDataManager
from transaction._transaction import AbortSavepoint
import transaction
class RedirectDataManager(object):
implements(ISavepointDataManager)
def __init__(self, request, url):
self.request = request
self.url = url
# Use the default thread transaction manager.
self.transaction_manager = transaction.manager
def tpc_begin(self, transaction):
pass
def tpc_finish(self, transaction):
self.request.response.redirect(self.url)
def tpc_abort(self, transaction):
self.request.response.redirect(self.url)
def commit(self, transaction):
pass
def abort(self, transaction):
pass
def tpc_vote(self, transaction):
pass
def sortKey(self):
return id(self)
def savepoint(self):
"""
This is just here to make it possible to enter a savepoint with this manager active.
"""
return AbortSavepoint(self, transaction.get())
def redirect_to_trial(obj, event):
request = getattr(obj, 'REQUEST', None)
if request:
trial_url = obj.__parent__.__parent__.absolute_url()
transaction.get().join(RedirectDataManager(request, trial_url))
我现在使用zcml进行订阅,以便更轻松地将其绑定到多种内容类型:
<subscriber
zcml:condition="installed zope.lifecycleevent"
for=".schema.ISite zope.lifecycleevent.IObjectRemovedEvent"
handler=".base.redirect_to_trial"
/>
这是我最终会采用的解决方案,因为我发现它比正在进行的操作更明确,如果我抓到的事件是我真正想要的事件,可以解决问题。
答案 2 :(得分:1)
您可以 自定义delete_confirmation
操作,而不是使用事件处理程序;这些可以通过网络进行更改,并且可以按类型进行自定义。 delete_confirmation
脚本是CMF Form Controller script,有几个选项可以改变它的行为。
目前,行动的定义如下:
[actions]
action.success=redirect_to:python:object.aq_inner.aq_parent.absolute_url()
action.confirm=traverse_to:string:delete_confirmation_page
例如,您可以通过定义action.success.TypeName
来添加特定于类型的操作。
要通过网络进行操作,请访问ZMI并找到portal_form_controller
工具,然后点击Actions
标签:
正如您在此屏幕截图中所看到的,此处还提供了有关此工具的文档。
在操作选项卡上,有一个表单可添加新操作:
如您所见,上下文类型是一个包含所有现有类型注册的下拉列表,可以更轻松地指定特定于类型的操作。我已经复制了常规操作(由redirect_to
表达式指定的python:
操作,并添加了额外的.aq_parent
来选择容器父级。
您还可以使用.addFormAction
方法在工具上添加此类操作:
fctool = getToolByName(context, 'portal_form_controller')
fctool.addFormAction('delete_confirmation', 'success', 'Event', None,
'redirect_to',
'python:object.aq_inner.aq_parent.aq_parent.absolute_url()')
最后但并非最不重要的是,您可以在GenericSetup配置文件的cmfformcontroller.xml
文件中指定此类自定义操作;以下是基于上述操作的示例:
<?xml version="1.0" ?>
<cmfformcontroller>
<action
object_id="delete_confirmation"
status="success"
context_type="Event"
action_type="redirect_to"
action_arg="python:object.aq_inner.aq_parent.aq_parent.absolute_url()"
/>
</cmfformcontroller>
这种格式是Plone中未记录的内容之一;我从CMFFormController sourcecode for the GS import and export code得到了这个。
答案 3 :(得分:0)
我面对的是我认为必须是常见的用例,其中本地Plone对象代理远程对象。删除Plone对象后,但仅在实际删除时,我想删除远程对象。
对我来说,addAfterCommitHook()没有避免任何问题,所以我采用了自定义IDataManager方法,为类似用例提供了一个很好的通用解决方案......
from transaction.interfaces import IDataManager
from uuid import uuid4
class FinishOnlyDataManager(object):
implements(IDataManager)
def __init__(self, callback, args=None, kwargs=None):
self.cb = callback
self.args = [] if args is None else args
self.kwargs = {} if kwargs is None else kwargs
self.transaction_manager = transaction.manager
self.key = str(uuid4())
def sortKey(self): return self.key
abort = commit = tpc_begin = tpc_vote = tpc_abort = lambda x,y: None
def tpc_finish(self, tx):
# transaction.interfaces implies that exceptions are
# a bad thing. assuming non-dire repercussions, and that
# we're not dealing with remote (non-zodb) objects,
# swallow exceptions.
try:
self.cb(*self.args, **self.kwargs)
except Exception, e:
pass
以及相关的处理程序......
@grok.subscribe(IRemoteManaged, IObjectRemovedEvent)
def remove_plan(item, event): IRemoteManager(item).handle_remove()
class RemoteManager(object): ...
def handle_remove(self):
obj = self._retrieve_remote_object()
def _do_remove():
if obj:
obj.delete()
transaction.get().join(FinishOnlyDataManager(_do_remove))