Search code examples
pythondjangoworkflowdjango-viewflow

Django Viewflow passing variables to function based view


Trying out a very simple test app with viewflow.io using function based views rather than the built-in class based views. The intended idea is that a product is added and then approved (via two different views/ forms). There are two issues I cannot seem to work out:

  1. I want to pass the Product to the approval view (so that the user doing the approval can see the summary of what they are meant to approve. I am not sure how to do this - I tried passing the product_pk via the flow.View in flows.py but this results in an error and if I leave it out then the approval view updates all records rather than the current product.
  2. The flow.If gate in flows.py always seems to be True regardless of whether the approved field in Product has been check or not. Ideally I am hoping that the approval is recorded in the Product model rather than the process model

Probably super basic mistake/ concept I am missing - any help would be appreciated.

In models.py

class Product(models.Model):
    name = models.CharField(max_length=30)
    quantity = models.IntegerField()
    approved = models.BooleanField(default=False)

    def __str__(self):
        return self.name

class ProductProcess(Process):
    product = models.ForeignKey(Product, blank=True, null=True)

    def approved(self):
        return self.product.approved


class ProductTask(Task):
    class Meta:
        proxy = True

In flows.py

class ProductFlow(Flow):
    process_cls = ProductProcess
    task_cls = ProductTask

    start = flow.Start(start_process).Next(this.approve)

    approve = flow.View(approve_product).Next(this.checkapproval)

    checkapproval = flow.If(cond=lambda p: p.approved()) \
        .OnFalse(this.approve) \
        .OnTrue(this.end)

    end = flow.End()

In views.py

@flow_start_view()
def start_process(request, activation):
    activation.prepare(request.POST or None,)
    form = ProductForm(request.POST or None)

    if form.is_valid():
        Product.objects.create(
            name = form.cleaned_data['name'],
            quantity = form.cleaned_data['quantity']
        )
        activation.done()
        return redirect('/test')

    return render(request, 'viewflowtest/product.html', {'activation': activation, 'form': form})

@flow_view()
def approve_product(request, activation):
    activation.prepare(request.POST or None,)
    form = ApproveProductForm(request.POST or None)

    if form.is_valid():
        Product.objects.update(
            approved = form.cleaned_data['approved']
        )
        activation.done()
        return redirect('/test')
    return render(request, 'viewflowtest/product.html', {'activation': activation, 'form': form})

The form that is called is a very basic ModelForm class and the URLs are exactly as is described in the demo applications on the project GitHub pages. The template has the {{ activation.management_form }} tag.


Solution

  • First of all, you need to link the product and process. So in start view, you can do

    if form.is_valid():
        product = Product.objects.create(
            name = form.cleaned_data['name'],
            quantity = form.cleaned_data['quantity']
        )
        activation.process.product = product
        activation.done()
    

    or even better, if the ProductForm is the ModelForm

    if form.is_valid():
        product = form.save()
        activation.process.product = product
        activation.done() # here is new process instance created and saved to db
    

    So the approval view could be rewritten as::

    @flow_view()
    def approve_product(request, activation):
        activation.prepare(request.POST or None,)
        form = ApproveProductForm(request.POST or None, instance=activation.process.product)
    
        if form.is_valid():
            form.save()  # here is the approved field is updated
            activation.done()
            return redirect('/test')
        return render(request, 'viewflowtest/product.html', {'activation': activation, 'form': form})
    

    In addition, you can take a look to the viewflow example with the function-based views - https://github.com/viewflow/cookbook/blob/master/viewflow_customization/customization/parcel/views.py