Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/python/348.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Python Plone:对移除对象作出反应_Python_Plone - Fatal编程技术网

Python Plone:对移除对象作出反应

Python Plone:对移除对象作出反应,python,plone,Python,Plone,我想在删除容器中的项目后重定向到容器的父级。为此,我尝试订阅了zope.lifecycleevent的IObjectRemovedEvent: @grok.subscribe(ISite, IObjectRemovedEvent) def redirect_to_trial_on_delete(obj, event): request = getattr(obj, 'REQUEST', None) if request: trial_url = obj.aq_pa

我想在删除容器中的项目后重定向到容器的父级。为此,我尝试订阅了
zope.lifecycleevent的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)
单击
容器/id/delete\u确认
会触发删除,但这会触发比我预期的更多的事件。我的订阅函数被调用两次:一次是在我点击链接时,另一次是在我确认删除时。更令人困惑的是,如果我取消删除,它也会被调用。我希望只有当一个对象从容器中移除时才会引发该事件

在这三种情况下,事件对象都是相同的,oldName、oldParent等具有相同的属性值

如何区分请求删除项目、取消请求和实际删除项目


更新:因此调用初始事件似乎是因为从容器中移除对象以检查链接完整性,此时会出现回滚。

一位同事想出了一个可行的解决方案:

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)
这将在提交后进行检查,以确保在执行重定向之前确实已删除对象


不过,如果能证实这是否是一种合适的方法,我们将不胜感激。

这里还有另一种可能性,同样来自同一位genius同事:

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 for subscription更轻松地将其绑定到多种内容类型:

<subscriber
    zcml:condition="installed zope.lifecycleevent"
    for=".schema.ISite zope.lifecycleevent.IObjectRemovedEvent"
    handler=".base.redirect_to_trial"
/>


这就是我最终采用的解决方案,因为我发现它比手动检查我捕获的事件是否是我真正想要的事件更清楚地说明发生了什么。

您可以自定义
删除确认操作,而不是使用事件处理程序;这些甚至可以通过web进行更改,并且可以根据类型进行自定义。
delete\u确认
脚本是一个脚本,有几个选项可以改变它的行为

目前,这些行动的定义如下:

[actions]
action.success=redirect_to:python:object.aq_inner.aq_parent.absolute_url()
action.confirm=traverse_to:string:delete_confirmation_page
例如,您可以通过定义
action.success.TypeName
来添加特定于类型的操作

要通过web执行此操作,请访问ZMI并找到
portal\u form\u controller
工具,然后单击
Actions
选项卡:

正如您在这个屏幕截图中看到的,这里还有关于该工具的文档

在“操作”选项卡上,有一个用于添加新操作的表单:

如您所见,上下文类型是包含所有现有类型注册的下拉列表,以便更容易指定特定于类型的操作。我复制了常规操作(a
redirect\u到
python:
表达式指定的
操作,并添加了一个额外的
.aq\u 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
文件中指定此类自定义操作;以下是基于上述操作的示例:



.

我也面临着一个我认为很常见的用例,即本地Plone对象代理远程对象。移除Plone对象后,但仅在实际移除时,我想移除远程对象

对我来说,addAfterCommitHook()并没有避免任何问题,所以我采用了自定义IDataManager方法,它为simlar用例提供了一个很好的通用解决方案

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))

“addAfterCommitHook”--太棒了!我不知道这个:)谢谢分享,效果非常好!FWIW,我需要使用
addBeforeCommitHook
而不是
afterCommitHook
,然后得到“redirect_to_trial()至少需要1个参数(2个给定)”,这是可以解决的,通过另外传递一个空的
args
-dict。整行是:
trans.addBeforeCommitHook(redirect_to_to_trial,args=(),kws=kwargs)
谢谢Martijn。我天生就是一名程序员,所以我总是更喜欢编程方法,但我认为这也很方便。@MatthewTrevor:这里也是一样,但是使用事务管理器进行重定向,我们可以说,是一个解决问题的大锤,当有一个更简单的方法时..+1并且感谢您指出GS的可能性,很高兴知道这一点。