为什么这个内部原子块没有在视图流自定义视图代码中回滚

问题描述 投票:0回答:1

我创建了这个自定义创建流程视图,将登录用户的电子邮件添加到保存的对象中,我还添加了一个事务块,以便当下一步失败时(在

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 文档,事务内引发的异常会回滚

enter image description here

但是,当表单显示错误消息时,新的流程实例仍然被保存,所以我怀疑事务没有回滚,我不知道为什么。

更新: 如果我在

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()
django django-viewflow
1个回答
0
投票

没有退出的

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 您可以验证一下吗?更改后是否会产生任何意想不到的后果?

© www.soinside.com 2019 - 2024. All rights reserved.