Three more uses for functools.partial() in Django
I remain convinced that Python’s
functools.partial() is underappreciated. Following my previous post, here are three more ways to use
partial() with Django.
(For an explanation of
partial() see PyDanny’s post.)
render() shortcut simplifies generating an HTTP Response from rendering a template. If several views need to pass the same arguments, we can wrap the shortcut with
partial() to avoid repetition.
For example, imagine we have several views that return text, rather than HTML. We could create our own
render_text shortcut that calls
render with the
content_type argument pre-filled:
from functools import partial from django.shortcuts import render render_text = partial(render, content_type="text/plain") def robots_txt(request): ... return render_text(request, "robots.txt") def security_txt(request): ... return render_text(request, "security.txt")
(See my posts on robots.txt and security.txt.)
We can use this approach with other shortcut functions, or even
HttpResponse & its subclasses.
Django’s examples for
transaction.on_commit() use functions, or lambdas:
from django.db import transaction # With a function def send_email(): send_admin_email( subject="Author created", # ... ) transaction.on_commit(send_email) # With a lambda transaction.on_commit( lambda: send_admin_email( subject="Author created", # ... ) )
In the wild, most
on_commit() callbacks only call another function with pre-filled arguments. Using
lambda can make this easy, but lambda functions have their drawbacks. We can instead use
partial() to bind the arguments to the function:
transaction.on_commit( partial( send_admin_email, subject="Author created", # ... ) )
3. Database functions
Django’s database functions represent functions that will run in the database. As such, in Python they are classes, rather than functions, but this doesn’t stop us from using
partial() with them.
For example, to create a specialized
SubStr that extracts the first character from a string, we can do:
from functools import partial from django.db.models.functions import Substr FirstChar = partial(Substr, pos=1, length=1)
In use, this looks just like a normal database function:
In : from example.core.models import Author, FirstChar In : Author.objects.annotate(first_char=FirstChar("name")).filter(first_char="C") Out: <QuerySet [<Author: Author 'Charles Dickens' (1)>]>
For wrappers using more than one database function, we can’t use
partial(). Instead we can combine them with a function. For example, to take the first character and uppercase it:
from functools import partial from django.db.models.functions import Substr, Upper def FirstCharUpper(expression): return Upper(Substr(expression, pos=1, length=1))
Even though this is a function, I like to stick to the CamelCaes naming convention so it matches the database functions.
Learn how to make your tests run quickly in my book Speed Up Your Django Tests.
One summary email a week, no spam, I pinky promise.
- 3 uses for functools.partial in Django
- A Guide to Python Lambda Functions
- How to Combine Two Python Decorators
Tags: django, python