How to build a simple event-driven architecture

I recently read this interesting Reliable Django Signals article which left me inspired to have a play with building something similar. I wanted build a simple event-driven architecture where: Publishers can publish an event with a given payload One or many subscribers can subscribe to an event Events are asynchronous (as opposed to the synchronous observer pattern) I took the opportunity to try out django-tasks and blinker for this. Here’s what I came up with. ...

February 4, 2026 · James Beith

How to create a Django GeneratedField for extracting a timestamp from a UUIDv7

I recently read How to use UUIDv7 in Python, Django and PostgreSQL from Paolo Melchiorre. I particularly liked the section demonstrating how to extend the model with a generated column that stores the timestamp extracted from the UUID. from django.db import models class UUIDv7(models.Func): function = "uuidv7" output_field = models.UUIDField() class UUIDExtractTimestamp(models.Func): function = "uuid_extract_timestamp" output_field = models.DateTimeField() class Record(models.Model): uuid = models.UUIDField(db_default=UUIDv7(), primary_key=True) creation_time = models.GeneratedField( expression=UUIDExtractTimestamp("uuid"), output_field=models.DateTimeField(), db_persist=True, ) As Paolo says: ...

December 4, 2025 · James Beith

How to use reserved domains in tests to prevent DNS queries

When writing tests I use the example.com domain for any URLs, email addresses, and similar. The reason: if my test unintentionally made a real request, or sent a real email, then it would resolve to a special-use domain. The Internet Assigned Numbers Authority (IANA) reserves the example.com domain for documentation purposes. Better that the request resolves there than to one that’s privately owned, such as test.com. While reviewing some work recently I came across a comment from Ben about his suggestion to use the .invalid top-level domain. I learned that DNS prohibits installing certain TLDs like .example, .invalid, .localhost, and .test. Using test data such as [email protected] is safer than [email protected] because requests should never even reach DNS servers. While all four TLDs receive special handling, .invalid is the most non-resolving. For the technical differences between them, see sections 6.2-6.5 of RFC 6761.

July 1, 2025 · James Beith

How to optimise the order of HTML head elements using Capo

I recently came across Capo, a tool that helps identify HTML head elements that are out of order. Using the Chrome extension, it offered a few suggestions for my project. Move the es-module-shims asynchronous script and importmap synchronous script to be a lot higher All the synchronous styles <link rel=stylesheet> to come before deferred scripts <script defer src> Remove some preload elements which were having little to no effect (as the files were already discoverable by other link elements) Put everything else such as meta, non-stylesheet link, and JSON script elements last You can see how Capo categorises all the elements into 11 groups on the docs rules page.

June 23, 2025 · James Beith

How to write a custom alias for the Python debugger

Recently shared at work was this TIL from Samuel, Custom alias for pretty printing in Python debugger with .pdbrc (including Django models!). Here’s my ever so slightly adapted version which also includes printing a reminder to me that these exist. alias rp import rich; rich.print(%*) alias rpo import rich; from django import forms; rich.print(forms.model_to_dict(%*)) print("Aliases: Use `rp obj` and `rpo instance` to print objects and model instances.\n") I already had something similar in my IPython startup script using pprint but I’ve now adapted it to use Rich too. ...

June 19, 2025 · James Beith

How to catch missing referenced files in Django static files manifest

I recently had a deployment fail with the following WhiteNoise error. -----> $ python src/manage.py collectstatic --noinput Post-processing 'polls/style.css' failed! whitenoise.storage.MissingFileError: The file 'polls/style.css.map' could not be found with <whitenoise.storage.CompressedManifestStaticFilesStorage object at 0x7f5c4b1970e0>. The CSS file 'polls/style.css' references a file which could not be found: polls/style.css.map Please check the URL references in this CSS file, particularly any relative paths which might be pointing to the wrong location. It was easy enough to fix, by adding the missing style.css.map file, but I wanted to write a test that would help prevent this from happening again in the future. Here’s what I started with. ...

February 27, 2025 · James Beith

How to use Django database function expressions directly

Django has a many built-in database functions and a documented Func API for writing your own. Whilst writing a custom Func subclass may sometimes be necessary, I learnt that there’s many cases when you can instantiate Func with the necessary arguments to get what you need. For example, take the following model. from django.contrib.postgres.fields import ArrayField from django.db import models class Post(models.Model): tags = ArrayField(models.CharField(max_length=200)) Making use of the array_shuffle Postgres Array Function and an annotation, each of the post’s tags are in a randomised order. ...

October 2, 2024 · James Beith

How to enqueue Celery tasks using RabbitMQ message priorities

I have some slow running, low priority Celery tasks that I don’t want holding up more important tasks. I learnt how to configure Celery tasks, and queues, to support message priorities. Note, the project in question uses Django, Celery, and RabbitMQ. Firstly, I needed to decide on my priority values. Whilst RabbitMQ supports priorities between 1 and 255, their docs highly recommend using values between 1 and 5. It is important to know that higher priority values require more CPU and memory resources, since RabbitMQ needs to internally maintain a sub-queue for each priority from 1, up to the maximum value configured for a given queue. — https://www.rabbitmq.com/docs/priority ...

August 28, 2024 · James Beith

How to encode Unicode characters to store in AWS S3 object metadata

I recently ran into this error uploading images to AWS S3 using the boto3 package. Parameter validation failed: Non ascii characters found in S3 metadata for key “filename”, value: “ACME™ Anvil.jpg”. S3 metadata can only contain ASCII characters. The character in question was ™. Here’s a simplified example of the code. filename = "ACME™ Anvil.jpg" metadata: dict[str, str] = { "filename": filename, } client.upload_fileobj( Fileobj=..., Bucket=..., Key=..., ExtraArgs={"Metadata": metadata} ) I wanted to preserve the original filename, so I learnt to use backslashreplace when encoding the filename to ASCII. Note, I have to subsequently use .decode() as both metadata keys and values must be str, not bytes. ...

August 5, 2024 · James Beith

How to create a Django model index with an upper case empty string condition

Let’s say I have the following Django model. from django.db import models class User(models.Model): name = models.CharField(blank=True) And I want to filter those like so. users = User.objects.filter(name__istartswith="John") That would perform the following query. SELECT "data_user"."id", "data_user"."name" FROM "data_user" WHERE UPPER("data_user"."name"::text) LIKE UPPER('John%') I can improve the performance of that query by adding the following index to the model. To complement the use of UPPER() in the query I can use the Upper() function for the index’s expression. ...

May 2, 2024 · James Beith