prefixed-ulids
Prefixed ULID Primary Keys
This project uses Stripe-style prefixed ULIDs as the primary key for every Django model:
prd_01jq3v8f6a7b2c8d9e0f1g2h3j4k
ord_01jq3v8fgh7x2y5z9a1b2c3d4e5f
A 3-4 character prefix identifies the entity type, followed by an underscore and a lowercase ULID. ULIDs are 128-bit, lexicographically sortable by creation time, URL-safe, and collision-resistant.
Why
- Debuggable.
ord_01jq...in a log line tells you immediately it's an order — no need to cross-reference the column. - Safe to expose. Unlike auto-increment integers, prefixed ULIDs leak no ordering or volume information, and unlike opaque UUIDs they remain human-readable.
- Time-sortable. ULIDs sort chronologically, so
ORDER BY iddoubles asORDER BY created_atwithout a second index. - Type-safe across layers. Every ID is a
strend-to-end — noUUID/strcoercion at the service/API boundary. - No integer collisions. Exporting, importing, and sharding are all easier without monotonic counters.
More from dvf/opinionated-django
services
Structure Django business logic as plain services that receive their dependencies via constructor injection, and wire them through an svcs registry so they can be resolved anywhere — views, Celery tasks, management commands, tests. Use when adding a new service, refactoring fat views or model methods into a service, wiring a service into the registry, or explaining where business logic should live in this project.
11dj-lint
Run linting, formatting, and static type checks on a Django project using ruff and pyrefly, and fix any issues found. Use after making code changes, before committing, or whenever the user asks to lint, format, or type-check the codebase.
11architecture
Implement a Django feature following the opinionated architecture — prefixed ULID IDs, repository pattern, Pydantic DTOs, svcs service locator, project-scoped django-ninja API, Celery reliable signals, and layered tests. Use when the user asks to add a new entity, endpoint, app, or business logic in a Django project that follows these conventions.
11dj-pytest
Set up and write pytest tests for an op-django project — pytest-django configuration, Celery eager mode for reliable-signal tests, freezegun for time-sensitive logic, shared conftest fixtures for DTOs and svcs overrides, and the three-layer test convention (repository against a real DB, service against mocked repos, API through HTTP). Use when adding tests to a new project, writing tests for a new feature, setting up test infrastructure, or explaining how tests should be organized.
11lint
Run linting, formatting, and static type checks on a Django project using ruff and pyrefly, and fix any issues found. Use after making code changes, before committing, or whenever the user asks to lint, format, or type-check the codebase.
10pytest
Set up and write pytest tests for an op-django project — pytest-django configuration, Celery eager mode for reliable-signal tests, freezegun for time-sensitive logic, shared conftest fixtures for DTOs and svcs overrides, and the three-layer test convention (repository against a real DB, service against mocked repos, API through HTTP). Use when adding tests to a new project, writing tests for a new feature, setting up test infrastructure, or explaining how tests should be organized.
10