To store state in a database, a class with viewflow.fsm.State
slot
could be composed with a django model.
from django.db.models import TextChoices
from django.utils.translation import gettext_lazy as _
class ReportState(TextChoices):
NEW = 'NEW', _('New')
APPROVED = 'APPROVED', _('Approved')
REJECTED = 'REJECTED', _('Rejected')
PUBLISHED = 'PUBLISHED', _('Published')
class Report(models.Model):
text = models.TextField()
stage = models.CharField(max_length=150, choises=ReportState.choices)
To specify state storage use State.setter and State.getter decorators.
class ReportFlow(object):
stage = fsm.State(ReportState, default=ReportState.NEW)
def __init__(self, report):
self.report = report
@stage.setter()
def _set_report_stage(self, value):
self.report.stage = value
@stage.getter()
def _get_report_stage(self):
return self.report.stage
@stage.on_success()
def _on_transition_success(self, descriptor, source, target):
self.report.save()
State.on_success decorator allows to specify action performent at the end of transition.
TODO permission
def approve(request, report_pk):
report = get_object_or_404(Report, pk=review_pk)
flow = Reportflow(report)
if not flow.approve.has_permission(request.user):
raise AccessDenied
form = ApproveForm(request.POST or None, instance=report)
if form.is_valid():
form.save(commit=False)
flow.approve()
return redirect('../')
return render(request, 'approve.html', {
'report': report,
'flow': flow,
'form': form
})
class ReprotChangeLog(models.Model):
review = models.ForeignKey(Review, on_delete=models.CASCADE)
changed = models.DateTimeField(default=timezone.now)
source = models.CharField()
target = models.CharField()
Modify
@stage.on_success()
def _get_report_stage(self, descriptor, source, target, **kwargs):
with transaction.atomic():
self.review.save()
ReviewChangeLog.objects.create(
review=self.review,
source=source.value,
target=target.value
)