Search code examples
pythondjangodjango-viewflow

Inline formsets with django viewflow frontend


I can get an inline formset to work fine with django, but when trying to use viewflow and the built-in workflow frontend, I can't seem to get the inline form to work during the workflow.

What is the proper way to reference the formset?

Since the workflow models inherit from the viewflow.models.Process object, I can't seem to understand how to create the inline related model.

Use case: I have a workflow with about 50 various fields and I can run the workflow just fine using the Flow and breaking up the process in logical parts in about 10 steps. However, After the process is started and the first 3 or 4 fields are entered, I want step 2 to be an inline form where various roles (unknown number) are assigned to users for this particular workflow event.

I already have a separate table where they are manually assigned to roles and I can have the Flow assign the next task to whomever is assigned to that "steps" role and it works fine, but I want the action of assigning the roles to be part of the actual workflow itself.

I suspect I should add the inlineformset as a field of the parent model(Process), but couldn't see how...

I get close (at least in appearance) with the code below but

  1. I don't get the repeating inline form
  2. when I submit it errors with ManagementForm data is missing or has been tampered with
# models.py

from django.db import models
from django.utils import timezone
from filer.fields.file import FilerFileField
from viewflow.models import Process

# Create your models here.

class RoutingSheetProcess(Process):
    project     = models.CharField(verbose_name=u"Project#",max_length=20,blank=False,null=False)
    internaltask     = models.CharField(verbose_name=u"Task #",max_length=20,blank=False,null=False)
    pono        = models.CharField(verbose_name=u"P.O. Number",max_length=20,blank=True,null=True)
    sampletype  = models.CharField(verbose_name=u"Sample Type",max_length=10)
    sampleother = models.CharField(verbose_name=u"Other Sample Type",max_length=20,blank=True,null=True)
    sampledate  = models.DateField(verbose_name=u"Date Sample Taken",blank=True,null=True)

class InlineModel(models.Model):
    routingsheet = models.ForeignKey(RoutingSheetProcess,on_delete=models.CASCADE,blank=False,null=False)
    field1 = models.CharField(max_length=20,blank=True,null=True)
    field2 = models.CharField(max_length=20,blank=True,null=True)
    field3 = models.CharField(max_length=20,blank=True,null=True)
# flows.py

from viewflow import flow, frontend
from viewflow.base import this, Flow
from viewflow.flow.views import CreateProcessView, UpdateProcessView

from .models import RoutingSheetProcess
from . import views



@frontend.register
class RoutingSheetFlow(Flow):
    process_class = RoutingSheetProcess

    start = (
        flow.Start(
            CreateProcessView,
            fields=["project","internaltask","pono"]
        ).Permission(
            auto_create=True
        ).Next(this.sample)
    )

    sample = (
        flow.View(
            UpdateProcessView,
            fields=["sampletype","sampleother","sampledate"]
        ).Permission(
            auto_create=True
        ).Next(this.inline_sample)
    )

    inline_sample = (
        flow.View(
            views.InlineSample
        ).Permission(
            auto_create=True
        ).Next(this.end)
    )

    end = flow.End()
# views.py

from django.shortcuts import render

from viewflow.flow.views import CreateProcessView, UpdateProcessView

# Create your views here.

from .forms import *

from material import *

class InlineSample(UpdateProcessView):
    form_class=InlineForm

    def get_context_data(self, **kwargs):
        data = super(InlineSample, self).get_context_data(**kwargs)
        if self.request.POST:
            data['fields'] = InlineFormset(self.request.POST)
        else:
            data['fields'] = InlineFormset()
        #print(data)
        #for f in data['fields']:
        #    print(f)
        return data


    layout = Layout(
        Fieldset('Inline Fields',
            Row('field1','field2','field3'),
        )
    )

    def get_object(self):
        #print(self.activation.process)
        return self.activation.process 
# forms.py

from django import forms
from django.forms import ModelForm
from django.forms.models import inlineformset_factory, formset_factory

from .models import *

InlineFormset=inlineformset_factory(
        RoutingSheetProcess, #parent
        InlineModel, #child
        fields=('field1','field2','field3'), #fieldlist from child
        extra=2
        )  

class InlineForm(ModelForm):

    class Meta:
        model=InlineModel

        exclude=[]
        widgets = {
            #'description':forms.Textarea,
        }

Produces this on step 2 of flow... output of step 2


Solution

  • To handle formsets django-material provides InlineFormSet field, but it's available only in PRO version

    http://docs.viewflow.io/forms_formsets.html https://github.com/viewflow/viewflow/blob/master/demo/shipment/forms.py#L7