“Get back to where you once belonged…”

I recently ran into the situation where I wanted to be able to return from a form to the previous view.  There was not a way to add kids to the system (short of using the admin portal) and there were two obvious places that should have the ability to do that.  Of course, after the kid is added, you want to take the user back to where they were.

After a bit of searching I ended up on this SO page which has a good description of how to make this work.  I’m writing this up here as some of the examples are a bit terse and it seemed like a more detailed example might be useful.

The Idea

The general idea is that all links and referrers to the new form view will contains a GET parameter containing a link back to the referring page.  This parameter needs to get passed through the GET (or non-POST) portion of the view to be part of the form.  Then, when the user POSTs the form back to the view, it is returned there and used by the POST portion of the view to redirect to the starting point.  Got it?  Let’s walk through in code.

The Code

First, let’s start with the referring code.  I’m using simple hrefs in the referring page to bring up this form, so it’s pretty easy.

The link in the referring templates change from

<br/> <a href="{% url 'kid_new' %}">Add New Kid</a>

to

<br/> <a href="{% url 'kid_new' %}?from=
       {{ request.path|urlencode }}">Add New Kid</a>

The added “?from” portion is what builds the return info.

Moving on to the view, we’ll skip the POST portion and see what the GET portion looks like:

def new_kid(request):
    """ Create a new kid!  Sounds more exciting than it is. """
    if request.method == "POST":
        # skipped
    else:
        form = KidForm()
    return render(request, 'tasks/kid_edit.html',
                  {'form': form, 
                   'from': request.GET.get('from', None)})

You can see that it still build the same form, but passes in the ‘from’ field in the context to the template.

The template (kid_edit.html in this case) adds that to the form itself (this is the part that confused me at first).  So the form went from looking like:

    <form method="POST" class="kid-form">
        skipped...
    </form>

to:

    <form method="POST" class="kid-form"
       action="{% if from %}?next={{ from }}{% endif %}" >
         same stuff skipped ...
    </form>

The action field is what is going to pass the from field back to the view.

Finally, let’s go back and look at the rest of the view.  Here’s the entire function:

def new_kid(request):
    """ Create a new kid!  Sounds more exciting than it is. """
    if request.method == "POST":
        form = KidForm(request.POST)
        if form.is_valid():
            form.save()
            next = request.GET.get('next', None)
            if next:
                return redirect(next)
            # if for some reason it was not set, default to today
            return redirect(reverse('today'))
    else:
        form = KidForm()
    return render(request, 'tasks/kid_edit.html',
                  {'form': form, 'from': request.GET.get('from', None)})

You can see in the POST section that we do the normal form validation, then get the next field out of the POSTed form.  If it exists, we go to the referring page.  In this case, just for safety, if it does not exist we still go back to one of our two main pages.  You could also raise an error here if that was more appropriate.

The form portion of this code is exactly like any other form you’ve used.  Nothing special needs to be added there.

That’s all it takes!

Custom Filters and Ordered Dicts

This week I ran into a minor problem that took a surprising amount of time to resolve. Getting a Django template to produce a dict in sorted order.  While there were answers out there, none seemed to match the environment that I am using (python 3, Django 1.10).  After some experimentation, I finally came up with what I think is a good solution.  Feel free to tell me a better one if you know it.

The Problem

The basic problem was that I have a template which walks through a dict and displays it in a particular format.  As we all know, python dicts are not ordered, so the display would flip around between invocations of the server.  Not a huge deal, but annoying.  The template code looked like:

        {% for kid, days in kids.items %}
            <h1>{{ kid }}</h1>
             ... deleted stuff here ...
        {% endfor %}

StackOverflow to the rescue!

There was a quite good answer on SO which pointed me in the right direction, but it unfortunately did not work for my situation (again, I suspect python 2 vs 3 differences).

After doing some digging, I figured out that invoking the filter with dict.items()

{% for kid, days in kids.items|sort %}

was not resulting in the parameter as a dict, but rather as a ItemsView which is a ‘view’ into a dict.

I played some with using

{% for kid, days in kids|sort %}

which did result in a dict getting passed into the filter, but returning the OrderedDict resulted in some strange iterable being returned (the number of items to unpack changed on each iteration, so I suspect that it was flattening my structure somehow, but I did not pursue that).

I decided to embrace the original form and move forward with the ItemsView object that was getting passed in.  This turned out to be much shorter and easier code than the original, and so I went with that.

My Solution

Here’s where I ended up.  The filter looks like this:

from django import template
from collections import ItemsView

register = template.Library()


@register.filter
def sort(value):
    if isinstance(value, ItemsView) or isinstance(value, list):
        return sorted(value)
    else:
        return value

Note that since ItemsView is iterable and I want an iterable result, I can
just used sorted() on it as we did with lists.

To invoke this we call it with:

{% for kid, days in kids.items|sort %}

in the template.

For completeness, here’s the answer I posted on that SO question.

Why kwargs?

I’ve noticed that several of the Django functions, especially filter, take a kwargs-style set of arguments.  I casually wondered about this but didn’t give it much thought until now.  I ran into a very practical use for it, that should have been obvious, but wasn’t to me.

The Setup

In my recent redesign of the KidTasks project, I opted to take the easy route for tasks which repeat on particular weekdays and simply put a BooleanField for each day of the week into the model.  This makes forms a bit easier but does cause some complications when trying to use a filter function to get a list of weekday-task pairs.  I started with the naive (and un-good) method of filtering each weekday independently:

# note: syntax on the filter here is from memory - might be incorrect!
qs = RepeatingTask.objects.filter(kid=self).filter('monday'=True)
tasks.append((day, [task for task in qs]))
qs = RepeatingTask.objects.filter(kid=self).filter('tuesday'=True)
tasks.append((day, [task for task in qs]))
    [etc]

While that will work, I’m sure you’ll agree that it’s ugly.

Kwargs to the rescue!

It turns out that the solution to this problem lies with those mysterious kwargs function signatures.  It turns out that filter takes one. This allows you to create a dict which you can create on the fly.  Which, in turn, allows you to use a variable to define the field on which you’re filtering!

days = [
    'monday',
    'tuesday',
    'wednesday',
    'thursday',
    'friday',
    'saturday',
    'sunday'
]

...

for day in self.days:
    qs = RepeatingTask.objects.filter(kid=self).filter(**{ day : True })
    tasks.append((day, [task for task in qs]))

Much nicer.  While this is likely obvious to most seasoned Django users, I thought it tied things together nicely.

KidsTasks: Another redesign!

My work on the KidsTasks app has hit a bit of a roadblock.  I’ve been looking at adding forms for the users (i.e. mom) to assign new tasks to kids. As I’ve worked, I’ve realized that I made two important mistakes when starting this project.  The first was unavoidable; not taking the structure of how Django works into account when creating the design.

The second mistake was more avoidable.  True to my education and experience, I focused the original design on the internal data structures of the problem rather than the user interface.

The two mistakes together brought me to the point where, while it’s possible to create the UI I want on the models I currently have, it’s much more cumbersome than I feel it should be.  Therefore it’s time to re-evaluate the design.

User Scenarios

Most of my experience comes from embedded systems where the user interface is trivial if not completely absent.   Focusing on the UI earlier is not something in my normal skill set.  That said, let’s dive in.

I envision two primary user scenarios:

  • After school, the parents sit down with the kids and evaluate what homework and other tasks need to be accomplished.  These get added to the list for the day.  There should be recurring tasks that occur on given days every week and ad-hoc tasks for that day only.
  • The kids, when they have completed a task, can go to the app and indicate that it is completed, having its status changed. (changing color from red to green, strike-through, etc)

User Interface

To satisfy the first user scenario, I envision a form that looks similar to the the following:

Task Name: My New Task

Which Kid:
x kid1     _ kid2

Repeat Every
x Mon     _ Tue    _ Wed   x Thu   x Fri  _ Sat    _ Sun

<save and add another>  <save> <exit>

For the second user scenario, I’m envisioning a horizontal row of colored boxes with the name of each task inside.  Each row would correspond to a particular kid.  Clicking on a box would change its state (from red to green to indicate it was completed).

           ___________________________________
    Kid1:  | piano | sleep | chew gum | walk |
           ___________________________________
    
           ___________________________________
    Kid2:  | piano | sleep | spelling | talk |
           ___________________________________

New Models

Considering all this, I threw the old design out and started with a new set of models.  This actually made things much simpler and fits into the Django world view a bit better.

The Kid model changed to just a name.  I was mistaken in my earlier thinking that the Kid needed to “own” the set of tasks.  Once I saw the database modeling underneath the hood, it made sense that I merely needed to create a table of tasks which had a foreign key to the Kid, allowing me to do a simple query to get all the tasks for a given Kid.

I still ended up with two different types of tasks; the first is a RepeatingTask which is in reality a template for a Task.  It stores the idea of a task that is repeated on various days of the week.  Note that I did some searches on the best way to store days of week in a Django model.  There are clearly more efficient ways to store this, but using separate boolean fields makes creating forms for that info much easier.  I chose to stick with “easy” over “efficient” for this project.

The second task is an actual task assigned to a Kid on a particular date.  This is, thankfully, fairly straightforward.

Here’s the code:

#tasks/models.py
""" Define the data models for the KidsTasks app """
import datetime
from django.db import models


class Kid(models.Model):
    """ Defines the kids which have to do the tasks. """
    name = models.CharField(max_length=256)

    def __str__(self):
        return self.name

    class Meta:
        ordering = ['name', ]


class Task(models.Model):
    """ A Task is associated with a kid and a date.  This is the actual thing
    the kid has to do! """
    name = models.CharField(max_length=256)
    completed = models.BooleanField()
    date = models.DateField(default=datetime.datetime.now)
    kid = models.ForeignKey(Kid)

    def __str__(self):
        return self.name

    class Meta:
        ordering = ['name', ]


class RepeatingTask(models.Model):
    """ Defines a repeating task """
    name = models.CharField(max_length=256)
    kid = models.ForeignKey(Kid)  # NOTE: RepeatingTasks are kid specific
    monday = models.BooleanField(default=False)
    tuesday = models.BooleanField(default=False)
    wednesday = models.BooleanField(default=False)
    thursday = models.BooleanField(default=False)
    friday = models.BooleanField(default=False)
    saturday = models.BooleanField(default=False)
    sunday = models.BooleanField(default=False)

    def __str__(self):
        return "{0}:{1}".format(self.kid.name, self.name)

    class Meta:
        ordering = ['kid', 'name', ]

That’s where I ended this time.  Hopefully I’ll get back on track next time in creating views and forms for this app.

If you’re interested, this code will be stored in the blog/04-Redesign branch.

thanks for reading!

jima