我创建了这个自定义创建流程视图,将登录用户的电子邮件添加到保存的对象中,我还添加了一个事务块,以便当下一步失败时(在
self.activation_done()
语句内),它将回滚form_valid()
内的数据库更改
然后在启动步骤中显示错误,因此没有保存新的流程实例。
from django.core.exceptions import ValidationError
from django.db import transaction
from django.http import HttpResponseRedirect
from viewflow.flow.views import CreateProcessView, StartFlowMixin
class StarterEmailCreateProcessView(CreateProcessView):
"""
Sets the user email to the process model
"""
def form_valid(self, form, *args, **kwargs):
"""If the form is valid, save the associated model and finish the task."""
try:
with transaction.atomic():
super(StartFlowMixin, self).form_valid(form, *args, **kwargs)
# begin of setting requester email
# https://github.com/viewflow/viewflow/issues/314
self.object.requester_email = self.request.user.email
self.object.save()
# end of setting requester email
self.activation_done(form, *args, **kwargs)
except Exception as ex:
form.add_error(None, ValidationError(str(ex)))
return self.form_invalid(form, *args, **kwargs)
return HttpResponseRedirect(self.get_success_url())
根据 Django 文档,事务内引发的异常会回滚
但是,当表单显示错误消息时,新的流程实例仍然被保存,所以我怀疑事务没有回滚,我不知道为什么。
更新: 如果我在
self.activation_done
之前引发异常,它会很好地回滚,我不知道 self.activation_done
里面有什么特别之处阻止了回滚?
class StarterEmailCreateProcessView(CreateProcessView):
"""
Sets the user email to the process model
"""
def form_valid(self, form, *args, **kwargs):
"""If the form is valid, save the associated model and finish the task."""
try:
with transaction.atomic():
super(StartFlowMixin, self).form_valid(form, *args, **kwargs)
# begin of setting requester email
# https://github.com/viewflow/viewflow/issues/314
self.object.requester_email = self.request.user.email
self.object.save()
# end of setting requester email
raise Exception('11111111111')
self.activation_done(form, *args, **kwargs)
except Exception as ex:
form.add_error(None, ValidationError(str(ex)))
return self.form_invalid(form, *args, **kwargs)
return HttpResponseRedirect(self.get_success_url())
我什至尝试了以下方法,它捕获了异常但没有回滚。
def form_valid(self, form, *args, **kwargs):
"""If the form is valid, save the associated model and finish the task."""
try:
with transaction.atomic():
super(StartFlowMixin, self).form_valid(form, *args, **kwargs)
# begin of setting requester email
# https://github.com/viewflow/viewflow/issues/314
self.object.requester_email = self.request.user.email
self.object.save()
# end of setting requester email
try:
self.activation_done(form, *args, **kwargs)
except:
raise Exception(1111111)
except Exception as ex:
form.add_error(None, ValidationError(str(ex)))
return self.form_invalid(form, *args, **kwargs)
return HttpResponseRedirect(self.get_success_url())
更新2 我进入了
activation.done
的源代码并注释掉了锁定逻辑,它回滚得很好
@Activation.status.transition(source=STATUS.PREPARED, target=STATUS.DONE)
def done(self):
"""
Create and start new process instance.
.. seealso::
:data:`viewflow.signals.task_started`
.. seealso::
:data:`viewflow.signals.task_finished`
.. seealso::
:data:`viewflow.signals.flow_started`
"""
with transaction.atomic(savepoint=True):
signals.task_started.send(sender=self.flow_class, process=self.process, task=self.task)
self.process.save()
# lock_impl = self.flow_class.lock_impl(self.flow_class.instance)
# self.lock = lock_impl(self.flow_class, self.process.pk)
# self.lock.__enter__()
self.task.process = self.process
self.task.finished = now()
self.task.save()
signals.task_finished.send(sender=self.flow_class, process=self.process, task=self.task)
signals.flow_started.send(sender=self.flow_class, process=self.process, task=self.task)
self.activate_next()
锁定实现,我还没有弄清楚这如何防止回滚。
class NoLock(object):
"""
No pessimistic locking, just execute flow task in transaction.
Not suitable when you have Join nodes in your flow.
"""
def __call__(self, flow):
@contextmanager
def lock(flow_class, process_pk):
with transaction.atomic():
yield
return lock
no_lock = NoLock()
没有退出的
self.lock.__enter__()
看起来很可疑,所以我将其更改为以下内容并按预期回滚了
with transaction.atomic(savepoint=True):
signals.task_started.send(sender=self.flow_class, process=self.process, task=self.task)
self.process.save()
lock_impl = self.flow_class.lock_impl(self.flow_class.instance)
self.lock = lock_impl(self.flow_class, self.process.pk)
# self.lock.__enter__()
with self.lock:
self.task.process = self.process
self.task.finished = now()
self.task.save()
signals.task_finished.send(sender=self.flow_class, process=self.process, task=self.task)
signals.flow_started.send(sender=self.flow_class, process=self.process, task=self.task)
self.activate_next()
@kmmbvnr 您可以验证一下吗?更改后是否会产生任何意想不到的后果?