Adam Johnson

Home | Blog | Training | Projects | Colophon

Use Pathlib in Your Django Settings File

2020-03-16

Django’s default settings file has always included a BASE_DIR pseudo-setting. I call it a “pseudo-setting” since it’s not read by Django itself. But it’s useful for configuring path-based settings, it is mentioned in the documentation, and some third party packages use it.

(One that I maintain, the Scout APM Python integration, uses it.)

Django has, up until now, defined it as:

import os

# Build paths inside the project like this: os.path.join(BASE_DIR, ...)
BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))

This changes in version 3.1, which as I write is still months in the future. Thanks to a contribution by Jon Dufresne and Curtis Maloney, it’s instead defined using pathlib:

from pathlib import Path

# Build paths inside the project like this: BASE_DIR / 'subdir'.
BASE_DIR = Path(__file__).resolve(strict=True).parent.parent

Note this is in the new project template only. If you upgrade an older project to Django 3.1, your settings file won’t be changed.

pathlib was added to Python’s standard library in Python 3.4, thanks to PEP 428. All file-path using functions across Python were then enhanced to support pathlib.Path objects (or anything with a __fspath__ method) in Python 3.6, thanks to PEP 519.

pathlib is great! It has an easier API than os.path.join(), allows method chaining, and handles path normalization automatically. See how you can define a subdirectory using BASE_DIR / 'subdir'. If you want to read more, see Trey Hunner’s articles Why you should be using pathlib and No really, pathlib is great.

Even though this is in the future (or maybe not, if you are in the future), you can convert your projects to use it today. (Or, if you are using Django 3.1+, but your project was started before, you can convert.)

If you haven’t used pathlib before, I think this is a great place to try it out.

To migrate, you’d pretty much want to copy the commit from Django itself. First copy the new definition of BASE_DIR, then any other settings using it. For example, the documentation changed around defining STATICFILES_DIRS, from:

STATICFILES_DIRS = [
    os.path.join(BASE_DIR, "static"),
    '/var/www/static/',
]

to:

STATICFILES_DIRS = [
    BASE_DIR / "static",
    '/var/www/static/',
]

If you’re using Python 3.6+, you don’t need to worry about converting all uses at once. PEP 519 meant that os.path.join() was enhanced to accept Path() objects as well.

Fin

Thanks to Carlton Gibson, Curtis Maloney, Jon Dufresne, and Nick Pope for changing this in Django 3.1. Hope this helps your use of paths,

—Adam


Interested in Django or Python training? I'm taking bookings for workshops.


Subscribe via RSS, Twitter, or email:

One summary email a week, no spam, I pinky promise.

Related posts:

Tags: django, python