views:

225

answers:

2

I'm trying to refactor a fairly hefty view function in Django. There are too many variables floating around and it's a huge function.

Ideally, I want to modularize the view into logical functions. However, I have to pass the function context around to get easy access to the variables.

For example,

def complex_view(request, slug):
    some complex logic which creates variable abc ...
    ...
    some more complex logic which uses variable abc ...
    ...
    etc.

should become something like

def complex_view(request, slug):
    process_X()
    ...somehow pass variables and context to next function...
    process_Y()
    ... 
    etc.

def process_X():
    ...

def process Y():
    ...

I can think of a few ways to do this, some of which were pointed out in this page: http://mail.python.org/pipermail/tutor/2009-February/067506.html

a) Subfunctions defined in the master view. This seems kludgey since it's hard to tell which variables are shared and which aren't.

b) Passing the locals() in as a dictionary. This is also kludgey because you now have two separate ways of accessing a variables, xyz and contextDict['xyz'], and you have to use one in call N, and the next in call N+1 on the stack.

c) Brute force pass in all variables to each function call and return relevant vars. This gets very tedious when there's a lot of vars involved.

d) In C++/C#, I would simply create a class MyComplexViewContext, define all the shared variables, and create member functions to carry out the work. Then you can use self.xyz for everything inside that class. I suppose I could use this method in Python as well. Not sure if this is the best way though.

What's your take on the preferred way to do this in Python/Django?

+7  A: 

I like (d) - Create a class for it, and use member functions to do the work.

In Django, a view is just a 'callable' that accepts an HTTPRequest object, and whatever other paramaters your URL routing passes to it.

Python classes can be callable just like functions, if you define a __call__ method on them, like this:

class MyView(object):
    def __call__(self, request, slug)
        # do stuff here

    def helper_method(self):
        # etc.

Then you can name the class in your urls.py file, and it will be called like any other python function.

Doing this also lets you turn similar views into object instances:

class MyView(object):
    def __init__(self, parameters):
        # initialize instance

    def __call__(self, request, slug):
        # main view code goes here

first_view = MyView("some parameter")
second_view = MyView("some other parameter") # creates second object instance

and in urls.py, reference the objects (rather than the class) -- the objects can be called like functions as well.

Other tricks are to use inheritance to define similar views, or to provide some general functionality for a number of similar views in a base class, which specialized view classes then inherit from.

You can see this slideshow by Simon Willison for more details, or this snippet for a concrete example

Ian Clelland
+10000 This is just so wonderful for more complex tasks. Thinking functionally is wonderful for the view paradigm, especially to get off the ground quickly, but the truth is you could write your entire views.py file OO style. The fact that more tutorials don't mention this is criminal.
David Berger
A: 

May I suggest to use something similar to d), like so:

class ComplexView_SharedVariables:
  # Shared variables (to be used as instance variables, not class variables!)
  a = "somevalue"
  b = ...
  c = ...

class ComplexView_Methods1:
  ... some functionality ...

class ComplexView_Methods2:
  ... some more functionality ...

class ComplexView_All(ComplexView_SharedVariables, ComplexView_Methods1, ComplexView_Methods2):
  # This class puts together all functionality and shared variables
  pass

With this procedure, you can even split the classes into different files (if that's possible with Django, I don't have experience with it).

AndiDog