Skip to content

owais/django-niceid

Repository files navigation

django-niceid

IDs look like user_1KGGWGM8FEABRJA533GJKYW0B in Python and APIs, while PostgreSQL stores them as native UUID values (sortable, compact, index-friendly).

Why

  • 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_ or org_.
  • Efficiency: Store IDs as native UUID in 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.

Requirements

  • Python 3.14+ (uuid.uuid7 in the standard library)
  • Django 5.1+
  • PostgreSQL 18+ with native UUID support and a built-in uuidv7() function

Install

pip install django-niceid

Optional 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 bridge

Quickstart

Add 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
        pass

Each 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.

PostgreSQL uuidv7()

On PostgreSQL 18+, uuidv7() is available without extra setup. Ensure your database supports it before running migrations:

SELECT uuidv7();

URL converter

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.

Optional integrations

Django REST Framework

from niceid.drf import NiceIDSerializerField, NiceIDRelatedField

class UserSerializer(serializers.ModelSerializer):
    id = NiceIDSerializerField(read_only=True)
    team_id = NiceIDRelatedField(queryset=Team.objects.all(), source="team")

Django Ninja

Install django-niceid[ninja]. Field registration runs automatically in NiceIDConfig.ready().

Pydantic

Install django-niceid[pydantic]:

from pydantic import BaseModel
from niceid import NiceID

class Payload(BaseModel):
    user_id: NiceID

ID format

Part Rule
Namespace [a-z_]{2,16}
Separator _
Encoded UUID 25 Crockford base32 characters (UUIDv7)

Example: user_1KGGWGM8FEABRJA533GJKYW0B

Limitations (v0.1.0)

  • PostgreSQL only for database fields (native UUID column + uuidv7() default).
  • Python 3.14+ only.
  • Non-primary-key NiceIDField usage requires an explicit namespace= on the field.

Development

pip install -e ".[dev]"
pytest -m "not postgres"
docker compose up -d
POSTGRES_HOST=localhost POSTGRES_PORT=54329 pytest -m postgres

See CONTRIBUTING.md.

License

MIT — see LICENSE.

About

No description, website, or topics provided.

Resources

License

Contributing

Security policy

Stars

Watchers

Forks

Packages

 
 
 

Contributors