By default, Django adds integer primary keys as Autofields. This is annoying for many purposes, but especially makes debugging more difficult (code may accidently refer to the wrong "id", but instead of creating a runtime error, this might work in "some" instances because the IDs are accidently the same).
I want either unique integers across all tables or UUIDs for primary key. I do not want to specify either explicitly, since I want to only use this for debugging (and switch back to integers in production).
This is not a new proposal, but all answers seem to say "this is not performant" (blinding flash of the obvious) or advise to use explicit UUID fields (I don't want to do this because it means changing my model EVERYWHERE, then having to change it back later). Is there a way how to achieve this?
The proposals I've looked at (none of which answer by question) can be found here: Using a UUID as a primary key in Django models (generic relations impact) and Django: Unique ID's across tables with an open ticket for this feature here https://code.djangoproject.com/ticket/32577
A not very well known feature is that you can set the DEFAULT_AUTO_FIELD
setting [Django-doc] to specify the primary key used by all models, except stated differently in the model itself, or the app config. Now one of the problems is that Django currently does not like the idea very much to plug in a UUIDField
, but we can "force" this by specifying our own with:
# app_name/fields.py
import uuid
from django.db.backends.base.operations import BaseDatabaseOperations
from django.db.models import AutoField, UUIDField
BaseDatabaseOperations.integer_field_ranges['UUIDField'] = (0, 0)
class UUIDAutoField(UUIDField, AutoField):
def __init__(self, *args, **kwargs):
kwargs.setdefault('default', uuid.uuid4)
kwargs.setdefault('editable', False)
super().__init__(*args, **kwargs)
and then plug the UUIDAutoField
as default field:
# settings.py
# …
DEFAULT_AUTO_FIELD = 'app_name.fields.UUIDAutoField'
That being said, in my humble opinion, it was a bit of a misfeature to map an AutoField
to an int
and vice versa. One could make a separate type for each model like a UserPk
class, that wraps an int
and thus disables arithmetic on that field (since adding two UserPk
s together makes not much sense), and introduce type chekcs such that if you filter on User.objects.filter(pk=42)
, it errors if 42
is a simple int
, or some primary key of another model. Haskell's esqueleto
[hackage] follows this idea.