Wrapping Django Models

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.

State change view

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

Logging

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
        )