Django: a pattern for settings-configured API clients

Step by step!

Here’s an example of a common pattern in Django projects:

from acme.api import APIClient
from django.conf import settings

acme_client = APIClient(api_key=settings.ACME_API_KEY)


def order_anvil() -> None:
    acme_client.anvils.order(...)

An API client is instantiated as a module-level variable based on some settings. This approach has some drawbacks:

Here’s an alternative pattern that avoids these problems:

from functools import cache

from acme.api import APIClient
from django.conf import settings
from django.core.signals import setting_changed
from django.dispatch import receiver


@cache
def get_acme_client() -> APIClient:
    return APIClient(api_key=settings.ACME_API_KEY)


@receiver(setting_changed)
def reset_acme_client(*, setting, **kwargs):
    if setting == "ACME_API_KEY":
        get_acme_client.cache_clear()


def order_anvil() -> None:
    get_acme_client().anvils.order(...)

Notes:

This pattern requires a bit more code, but it is the easiest way to avoid the previous problems. It’s used for several settings-controlled objects inside Django. For example, the loading of password hashers:

@functools.lru_cache
def get_hashers(): ...


@functools.lru_cache
def get_hashers_by_algorithm(): ...


@receiver(setting_changed)
def reset_hashers(*, setting, **kwargs):
    if setting == "PASSWORD_HASHERS":
        get_hashers.cache_clear()
        get_hashers_by_algorithm.cache_clear()

Fin

May your instantiations be lazy and automatically reload,

—Adam


😸😸😸 Check out my new book on using GitHub effectively, Boost Your GitHub DX! 😸😸😸


Subscribe via RSS, Twitter, Mastodon, or email:

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

Related posts:

Tags: