IDs look like user_1KGGWGM8FEABRJA533GJKYW0B in Python and APIs, while PostgreSQL stores them as native UUID values (sortable, compact, index-friendly).
- Ordering: Natural ordering behavior just like integer auto-increment IDs.
- Global Uniqueness: Unlike auto-incredemt IDs.
- Clarity: Keep globally unique IDs while still having human-recognizable prefixes like
user_ororg_. - Efficiency: Store IDs as native
UUIDin PostgreSQL instead of strings. - Performance: Better index locality than random UUIDv4 by using time-ordered UUIDv7.
- Consistency: Standardize ID parsing/validation across Django models, routes, forms, and serializers.
- Friendly: Crockford base32 keeps IDs compact and human-friendly in URLs and APIs. No 0 v O or I v L, only upper-case. Easy to dictate and read from logs, screenshots, errors.
- Python 3.14+ (
uuid.uuid7in the standard library) - Django 5.1+
- PostgreSQL 18+ with native UUID support and a built-in
uuidv7()function
pip install django-niceidOptional integrations:
pip install django-niceid[drf] # Django REST Framework
pip install django-niceid[ninja] # Django Ninja
pip install django-niceid[pydantic] # Pydantic v2 schemas
pip install django-niceid[tortoise] # django-tortoise ORM bridgeAdd the app to INSTALLED_APPS:
INSTALLED_APPS = [
# ...
"niceid.apps.NiceIDConfig",
]Define a model with a class-level namespace (2–16 lowercase letters or underscores):
from niceid.models import NiceIDModel
class User(NiceIDModel):
namespace = "user"
class Meta:
# your options
passEach row gets a primary key like user_1KGGWGM8FEABRJA533GJKYW0B. The value is generated by PostgreSQL via db_default=uuidv7() and exposed in Python as a NiceID instance.
On PostgreSQL 18+, uuidv7() is available without extra setup. Ensure your database supports it before running migrations:
SELECT uuidv7();The app config registers a path converter named niceid:
from django.urls import path
urlpatterns = [
path("users/<niceid:user_id>/", user_detail),
]user_id in the view is a NiceID instance.
from niceid.drf import NiceIDSerializerField, NiceIDRelatedField
class UserSerializer(serializers.ModelSerializer):
id = NiceIDSerializerField(read_only=True)
team_id = NiceIDRelatedField(queryset=Team.objects.all(), source="team")Install django-niceid[ninja]. Field registration runs automatically in NiceIDConfig.ready().
Install django-niceid[pydantic]:
from pydantic import BaseModel
from niceid import NiceID
class Payload(BaseModel):
user_id: NiceID| Part | Rule |
|---|---|
| Namespace | [a-z_]{2,16} |
| Separator | _ |
| Encoded UUID | 25 Crockford base32 characters (UUIDv7) |
Example: user_1KGGWGM8FEABRJA533GJKYW0B
- PostgreSQL only for database fields (native UUID column +
uuidv7()default). - Python 3.14+ only.
- Non-primary-key
NiceIDFieldusage requires an explicitnamespace=on the field.
pip install -e ".[dev]"
pytest -m "not postgres"
docker compose up -d
POSTGRES_HOST=localhost POSTGRES_PORT=54329 pytest -m postgresSee CONTRIBUTING.md.
MIT — see LICENSE.