KidTasks: Simple Class Based Views

For this post I’ve taken some advice from a python talk I watched a few weeks ago and converted some of my function-based-views (FBV) to class-based-views (CBV).  I found this simple and very cool for my simplest view and a bit more complex for another view.  I’ll look at each of these in turn.

Index View

The first view I changed was the simplest.  It merely took a single data type and sent it to a template.

The FBV version of this looked like:

def index(request):
    """ Shows all of the tasks in the system, broken down by kid """
    kids = get_list_or_404(Kid)
    return render(request, 'tasks/index.html', {'kids': kids})

It’s not much of a surprise to see that a simple FBV turns into an even simpler CBV.  Here’s how that ended up:

class FullList(ListView):
    """ Shows all of the tasks in the system, broken down by kid """
    model = Kid

Note that this change required that I move the template “index.html” to kid_list.html.  I also could have specified a template_name in the class, but this seemed like a good time to clean it up.

Today view

The transformation for this view was less pretty.  After a bunch of messing around and reading different examples, I came up with the solution below.  In general it seems the ListView is fantastic at representing a specific model as it exists in the database.  My issue was the need to update the “today” list if needed to match the tasks for the current day and then return only that.

I’ll show what the change looked like and hold off my concerns and ideas until afterward.

Here’s where we started:

def today(request):
    """ Generate the 'today' page showing which tasks are due today """
    day_name = datetime.datetime.now().strftime("%A")
    kid_list = get_list_or_404(Kid)

    kids = dict()
    for kid in kid_list:
        tasks = build_today(kid)
        if tasks:
            kids[kid.name] = tasks
    return render(request, 'tasks/today.html', {'kids': kids, 'day': day_name})

From which we ended up here:

class TodayList(ListView):
    """ Generate the 'today' page showing which tasks are due today """
    model = Kid
    template_name = 'tasks/today.html'

    def get_context_data(self, **kwargs):
        # get_context_data creates the context
        context = ListView.get_context_data(self, **kwargs)

        day_name = datetime.datetime.now().strftime("%A")
        kid_list = get_list_or_404(Kid.objects.order_by('name'))

        kids = {kid.name: kid.build_today() for kid in kid_list}
        context.update({'kids': kids, 'day': day_name})

        return context

It’s about the same amount of code, but somehow feels less ‘obvious’.  It relies on hooking the CBV pretty late in the processing and substituting the newly created kids dict into the context.  The template for this ONLY uses that part of the context which means that technically I could forego the call to the superclass (I think) and just return the kids dict.

I have two thoughts on how this could be done better.  The first is to split the work here.  I could hook the processing earlier to ensure that the today task is updated and stored in the kid and then modify the template to take the full list of kids, running through them to display the today list for each.  If I were to stay with this overall design, I think I’d go this direction.

However, my other thought is to re-design the models (again) to more closely match what’s happening here.  As I write this, I am also working on adding FormViews to allow easier addition of tasks for a given day.  That will likely result in a model redesign, so I suspect this part will change anyway.

The code for this point in the project is stored in branch blog/04-SimpleCBV.

Thanks for following along!

Author: Jim Anderson

I'm a gray-haired coder and an avid snowboarder. I've had a long career doing all levels of software development from assembly language on 8 bit micros to Java Servlets for enterprise servers. Mainly my work consists of C/C++ on embedded systems, however. I enjoy Python coding and Django as a hobby. Almost as much fun as a powder day!

2 thoughts on “KidTasks: Simple Class Based Views”

  1. You didn’t include the motivation for switching to CBVs. From my (limited) perspective, my opinion of the result, however, is that it is either worse code, or parity, in both cases.

    For example, `FullList` vs `index`. For a maintenance programmer, `index` is much, much easier to handle. Suppose a maintenance programmer, who doesn’t know everything about how Django works, sees `FullList`, and needs to change the template, how would they find it? They could try exploring a complex hierarchy of base classes to work out what on earth is going on, but they would have a hard time compared to `index` which tells them right there what the template is.

    Django’s CBVs here are basically using convention over configuration, which IMO is really unhelpful and unpythonic (“explicit is better than implicit”), especially when specifying the template name takes so little code, and massively increases maintainability. We shouldn’t be aiming for minimum keystrokes, but maximum readability (which includes avoiding verbosity, but never at the expense of comprehension). For this reason I always include `template_name` if I do use a CBV.

    As you mentioned, in all cases the code becomes much less obvious and direct. Of course, sometimes in doing so you can leverage inheritance to gain a lot of functionality you really need. But often you don’t need it. Very often I find myself rewriting very verbose CBVs to much shorter FBVs e.g. https://gist.github.com/spookylukey/54f248e7cc1dc5f998b1456b4b601a0f

    My current thoughts on CBVs are here, for reference – http://lukeplant.me.uk/blog/posts/my-approach-to-class-based-views/

  2. Luke –
    Great comments.
    My motivation to switch to CBV was largely “let’s see how this shiny toy works”. I totally agree that in at least this case, it doesn’t do much for code clarity or maintainability. I frankly found it frustrating to figure out which method to overload to get the proper result.

    I think I can see where CBV would be a win in larger, more complex projects, but I’ll definitely agree with you that it doesn’t make much sense from this side.

    On the readability/maintainability front, I found many examples (maybe most?) that did a bunch of the configurations for the CBV in the urls.py file. I found that another one of those “trying to minimize keystrokes” tricks that makes things less obvious.

    Thanks again for the comment!
    jima

Leave a Reply

Your email address will not be published. Required fields are marked *