Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Pythonize Me: how to manage caller context variables in Python? (Python/Django)

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 locals() in as a dictionary. This is also kludgey because there are now 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?

like image 231
vaughnkoch Avatar asked Dec 30 '22 06:12

vaughnkoch


1 Answers

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

like image 89
Ian Clelland Avatar answered Dec 31 '22 19:12

Ian Clelland