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!

KidsTasks: Minor Redesign

I’ve been slowed down in my progress this month due to a family vacation and a change of jobs, so I’ve only made a little progress on the KidsTasks app.

This post gets the app up to the ‘almost functional’ stage where the admin portal can be used to create tasks and schedules and the normal app UI can be used to list the day’s tasks, all of the tasks, and to mark tasks complete (or incomplete).  It’s not a great idea to have the admin pages be the main data entry access, but for this early stage it will be sufficient.

All of this functionality needs some more work, but the basics are there, which is always a good time to stop and consider your design.  Especially when I’m learning a new language or technology, I find that iterative design works best for the first several projects.  Once you get your hands dirty, it’s easier to see what the next step should be.  This doesn’t often (some would say ‘rarely’) ends up with an optimal design, but it gives you more room to experiment and learn as you go.  That’s definitely what’s going to happen here, as I already have several changes in mind.

What Changed This Time

While working on the very simple views and templates, I decided that some simplification of the models was also in order.  The models went from two different types of tasks existing in to different types of schedules.  The different schedule types got removed and a new field got added to the Kid model to hold a list of DateTasks.

The basic issue driving this (which would have been obvious to many designers) was the ability to change “abstract tasks” (i.e. ‘practice piano every Tuesday’ into DateTasks (“It’s Tuesday today, get a list of all tasks that are due today and display them.”).   The quick answer to this was to create the new list of DateTasks in the Kid model.  This will store all of the tasks due today.  This is really just a stepping stone to where I want to end up, having a history of completed (and non-completed) tasks.

Once I got this change to the models in place (complete with several rounds of rebuilding the database), I moved on to getting a most basic of user interfaces in place.  To do this I hacked my way through learning the basics of accessing the Django ORM from views and templates.  While this is fairly well documented, it still took some playing with to get where I was going and get the proper syntax.

I ended up doing most of my hacking in the views.py file and that’s where I put the main function to convert the Tasks to DateTasks.  In the subsequent week since I wrote that, I’ve read (in Two Scoops of Django) that putting logic in your views is not the best practice, so the next revision will likely fix this and put that functionality into the model.

The Templates and Views

There’s really not all that much to the new views once you see the code.  One thing that stood out for me was the simplicity of the Update view function.  Being able to do a redirect “by magic” is pretty cool and produces a nice effect in the UI.

As I’ve mentioned before, I’m not a graphic designer, but I do have intentions of creating something a little nicer than it currently stands: a simple HTML list with links to change the state of each task.

Cleanup

On a final note, I had to rebuild my laptop a few months ago and in the process I managed to forget to reinstall flake8!  I finally noticed this and now my vim setup is marking all the errors it found.  These got cleaned up on this round as well.

The next set of work on KidsTasks is to get a little nicer UI and to add some views for creating tasks instead of relying on the clunky admin portal.  This might or might not entail a dive into class-based-views.

For those of you keeping score at home, the code at this state is in branch: blog/03-Simple-Views-Update in this repo: git@github.com:jima80525/KidTasks.git

Thanks for reading!

A Few Interesting Recommendations

I want to pass on a couple of recent recommendations that have come my way.

Weekly Python Chat

Weekly Python Chat is run by Trey Hunner who does a weekly live chat covering python topics.

Trey has been doing Django-related topics recently which have been great learning opportunities for me. I especially like the annotations to the video which allow you to skip to the questions you’re particularly interested in.

This resource led me to the next one.

Two Scoops of Django

This book was brought up in one of the Weekly Python Chats. The current version is for Django 1.8, but so far much of the material has been fairly non-version specific. I’m about 5 chapters in and am not only enjoying it, but also learning some good “common practice” advice that is tough to get when you’re working on your own projects.

From this book, I’ve found the cookiecutter app to generate various types of applications boilerplate structure (proper layout of project, Readme files, etc) in various languages. This might be applicable well beyond my python/Django tasks.

This book also led me to the next topic.

virtualenvwrapper

virtualenvwrapper is a handy little tool which extends the functionality of virtualenv and makes it easier to use across multiple projects.

In the first of my KidsTasks posts, I walked through the project setup using virtualenv. I hope for my next project to use cookiecutter and virtualenvwrapper to simplify and “normalize” the layout of the directories.

requirements.txt

Finally, a quick tip I pulled from one of Daniel Bader’s code reviews. I did not know that you can use a requirements.txt file to list the dependencies for your project and then use pip to install them. This, married with virtualenvwrapper makes creating virtual envs that match any given project much easier.

The syntax for the requirements.txt file is quite simple:

RtmAPI==0.6.0
Django==1.10.0

Running pip is, similarly, quite simple.

$ sudo pip install -r requirements.txt
 Requirement already satisfied (use --upgrade to upgrade): RtmAPI==0.6.0 in /usr/local/lib/python2.7/dist-packages (from -r requirements.txt (line 1))
 Downloading/unpacking Django==1.10.0 (from -r requirements.txt (line 2))
 Downloading Django-1.10-py2.py3-none-any.whl (6.8MB): 6.8MB downloaded
 Installing collected packages: Django
 Successfully installed Django
 Cleaning up...

That’s it for recommendations. Feel free to add your cool tricks and recommendations below!