Skip to content

Instantly share code, notes, and snippets.

@blopker
Last active August 29, 2015 14:02
Show Gist options
  • Save blopker/341aad62241f3637bd8d to your computer and use it in GitHub Desktop.
Save blopker/341aad62241f3637bd8d to your computer and use it in GitHub Desktop.
Django Creek API
from django.views.generic import View
from django.http import HttpResponse
from django_creek import FlowState, FlowController, flowState
class ViewOne(FlowState, View): # Supports CBVs, just mix in a standard Django View or View subclass
flow_name = 'one'
def get(self, request):
return self.render({})
def post(self, request):
if request.POST.get('answer') == 'correct':
return self.flow.next() # Redirect to next flow
# OR go to a specific state in a complex flow
return self.flow.next('three')
return self.render({'error': 'incorrect'})
def render(self, ctx):
return HttpResponse('one ' + str(ctx))
class ViewTwo(FlowState, View):
flow_name = 'two'
def get(self, request):
return self.render({})
def post(self, request):
if request.POST.get('answer') == 'correct':
return self.flow.next() # Redirect to next flow
return self.render({'error': 'incorrect'})
def render(self, ctx):
return HttpResponse('two ' + str(ctx))
@flowState('three')
def viewThree(request, **kwargs): # Also supports FBVs
kwargs['flow'].end() # Erase flow history on the last view if you'd like
return HttpResponse('three ' + str({'message': 'end'}))
class NumberFlowController(FlowController):
# Simple flow, common case.
flow = [ViewOne,
ViewTwo,
viewThree]
# OR define a complex flow
# Complex flow with valid transitions
flow = {
ViewOne: (ViewTwo, viewThree),
ViewTwo: (viewThree,)
}
index = ViewOne # Must define initial flow state if flow is complex
# Add the flow's URLs to your urlpatterns
# Add ?debug=true as a query parameter to any flow state URL to disable redirects. (Only if DEBUG=True)
# ?debug=false resets back to normal.
urlpatterns = NumberFlowController.urlpatterns()
# patterns('', # All URLs go to last completed flow state if the flow state before it is incomplete.
# url(r'^$', NumberFlowController.dispatch, name='index'), # Goes to last completed flow state
# url(r'one/$', NumberFlowController.dispatch, {'flow': 'one'}, name='one'),
# url(r'two/$', NumberFlowController.dispatch, {'flow': 'two'}, name='two'),
# url(r'three/$', NumberFlowController.dispatch, {'flow': 'three'}, name='three'),
# )
@cblakkan
Copy link

@flowState('three')
def viewThree(request, **kwargs): # Also supports FBVs
    kwargs['flow'].end() # Erase flow history on the last view if you'd like
    return HttpResponse('three ' + str({'message': 'end'}))

I don't think you should be allowed to define flow states this way. It's much easier to understand if all flow states are defined in the same exact manner. IMO the boilerplate is minimal so we're adding complexity in understanding flows just to be slightly more terse.

@steveherrin
Copy link

If I set up a complex flow like

    flow = {
        ViewOne: (ViewTwo, viewThree),
        ViewTwo: (viewThree, ViewFour)
    }

What happens if in ViewOne I call (assuming I've created ViewFour with view_name = 'four'):

    return self.flow.next('four')

Does it make more sense to have the controller only know about a single default transitions, not all valid transitions? Otherwise, you can create inconsistencies pretty easily. I'm imagining the "simple" case where the flow is specified with a list works the same, but for the complex flows, you just specify one transition:

    flow = {
        ViewOne: ViewTwo,
        ViewTwo: viewThree,
    }

But ViewOne would still be free to go to viewThree if it needed to.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment