My Favorite Secret Django Idiom

Aaron Lee Maxwell
3 min readApr 10, 2023

--

Here’s a fun Django trick I “invented”…

And later learned a few others discovered first.

But not enough people know about it yet. So let me explain. And even if you are not into Django, this is also a fun Python trick. So buckle up.

Quick intro paragraph, which you can skip if you speak fluent Django:

Django lets you define functions called “views”, which generate the response back to the client: returning the HTML, or JSON, etc. This function takes an argument called “request”, with info about the HTTP request from that client. And when a webpage has a form, there’s a request.POST attribute containing the submitted form data. Oh, and Django views often use a function called “render()” to generate and return that HTML, JSON, etc. to respond with.

Okay, now that you’re a Django expert, let’s continue:

Imagine you have a Django view named choose_course(), using a form called CourseSelectForm. So the user can pick a course to enroll in. If you follow what’s suggested in the Django docs, you might write it like this:

from .forms import CourseSelectForm

def choose_course(request):
if request.method == "POST":
form = CourseSelectForm(request.POST)
if form.is_valid():
return render("courses/finish.djt", {
"form": form,
})
else:
form = CourseSelectForm()
return render(request, "courses/course-prep.djt", {
"form": form,
})

Everyone who uses Django knows they have some of the best documentation in all of open-source software…

But in this case, they are wrong.

Actually, they’re not wrong. That code works great.

But in almost every instance of a Django view using a form, there is a better way. A way that is simpler, more readable, and less error-prone.

That way is to use “request.POST or None”. Like so:

def choose_course(request):
form = CourseSelectForm(request.POST or None)
if not form.is_valid():
return render("courses/finish.djt", {
"form": form,
})
return render(request, "courses/course-prep.djt", {
"form": form,
})

Only four logical lines of code in the body. Simple and clear, compared to the previous version.

Let’s break it down:

  • When the page first loads in the browser, it will be done as an HTTP GET. So request.POST will be None.
  • That means the expression request.POST or None evaluates as None or None, i.e. (boolean false) or None, reducing to None.
  • So form is set to CourseSelectForm(None). But the first argument of Django Form objects defaults to None, so that’s the same as CourseSelectForm().
  • Because of that, the call to form.is_valid() will return False. So it will skip to the final render() line, presenting the ready-to-fill-out form.

Now, that’s the case when the page first loads. What happens when the form is submitted?

  • Because this is a form submission, it will use an HTTP POST request, and request.POST will be an object containing the form data.
  • Hence, request.POST is “truthy” and request.POST or None will short-circuit to the value of request.POST.
  • This is then fed as a non-default-value first argument to CourseSelectForm().
  • And if the form data is good, then form.is_valid() will return True. Causing the view function to return the first render().
  • If the form data was bad, it falls through to the final render(), so they can try again.

I have implemented many dozens of Django webapps, and this is the way I nearly always implement my form views.

And it works great.

Not only does it work correctly — which of course is critical and non-negotiable. It is also more concise, while staying readable; simpler, and thus less error-prone; and easier to modify and maintain.

All great stuff!

--

--