DO

dvf/opinionated-django

开发工具
109 stars 质量 70 趋势 70

A collection of Agent Skills that give a coding agent architectural skills to build scalable, maintainable Django projects with clean separation of concerns, testability, and a full suite of best...

概览

A collection of Agent Skills that give a coding agent architectural skills to build scalable, maintainable Django projects with clean separation of concerns, testability, and a full suite of best...

README

op-django

A collection of Agent Skills that give a coding agent architectural skills to build scalable, maintainable Django projects with clean separation of concerns, testability, and a full suite of best practices.

I love Django, but some parts of it are genuinely hard to type: querysets, model instances, related managers, F()/ Q() expressions, etc. In my experience, the cleanest way to handle this is to keep all the ORM work behind a repository layer that returns Pydantic DTOs. Your business logic lives in services that never import a model. Your views become one-liners, and each layer becomes very easy to mock and test.

After discussions with my friends Haki Benita and Pete Nilson, this is what I believe to be a reasonable set of patterns for flexible, testable Django projects. They’re opinionated, but hold up well on large scale projects in the wild.

A Layered Approach Using Encapsulation

Each layer encapsulates the one beneath it. The API layer never touches the ORM. Services never import a model. Repositories never leak a queryset or a model instance. Every boundary between layers is crossed as a typed Pydantic DTO, so changes stay local and tests stay fast.

  ┌──────────┐   ┌──────────┐   ┌──────────┐   ┌──────────┐   ┌──────────┐
  │   API    │──▶│ Service  │──▶│   DTO    │──▶│   Repo   │──▶│  Model   │
  └──────────┘   └──────────┘   └──────────┘   └──────────┘   └──────────┘
   thin views    business       typed data      ORM lives       
                 logic + DI     at boundaries   here only       
Layer Role Library
API Routing, input validation, OpenAPI django-ninja
Service Business logic & orchestration with true DI svcs
DTO Typed data at every layer boundary Pydantic v2
Repository All ORM access, transactions, prefetches Django
Model Persistence with prefixed ULID primary keys Django · python-ulid
Async Reliable signals & background tasks Celery

DX

Concern Tool
Packaging uv
Settings python-decouple
Linting & formatting ruff
Type checking pyrefly · django-stubs
Testing pytest · pytest-django · pytest-celery · freezegun

Install

Skills install with a single command:

# The whole collection
npx skills add dvf/opinionated-django

# Or just one
npx skills add dvf/opinionated-django/dj-scaffold|dj-architecture|dj-models|dj-services|dj-prefixed-ulids|dj-signals|dj-settings|dj-lint

Your agent will pick them up automatically on its next run. You can also clone the repo and point your agent at skills/ directly.

The Skills

Each skill is a directory under skills/ with its own SKILL.md. They’re designed to stand alone, but they compose nicely — dj-scaffold lays the foundation, dj-architecture builds features on top, and the rest fill in the details.

🏗️ dj-scaffold

Sets up a new (or existing) Django project into the op-django layout. Creates the src/project/ shell — ids.py, services.py, api.py, a ReliableSignal base, Celery wiring — installs dependencies with uv, and lays down ruff / pyrefly / pytest config. Run this first.

🔑 dj-prefixed-ulids

Stripe-style prefixed ULID primary keys for every model (prd_01jq3v..., ord_01jq3v...). Time-sortable, safe to expose in URLs, debuggable in logs, and str end-to-end so there’s no UUID/str coercion between layers.

🧩 dj-services

Plain service classes with constructor-injected repositories, wired through an svcs registry. Business logic lives here, zero ORM imports allowed. Resolve anywhere — views, Celery tasks, management commands, tests — with a single generic get[T]() call.

📐 dj-models

Structures Django models with a strict internal layout: Meta first (verbose names, indexes, constraints), then fields grouped as identifiers → time → status → domain → relations. Uniqueness lives in Meta.constraints (never unique=True), indexes in Meta.indexes (never db_index=True) — both optimized for the queries the repository actually runs. Multi-word fields get verbose_name, obscure fields get help_text. Every model is registered in the admin with a clean, fast-loading config (list_per_page = 25, raw_id_fields for large FKs, extra = 0 on inlines).

🏛️ dj-architecture

The full feature blueprint. Given a description, the agent scaffolds models, Pydantic DTOs, repositories, services, django-ninja routes, admin registration, and three layers of tests (repo against a real DB, service against mocked repos, API through HTTP). Every convention is spelled out; every layer is non-negotiable.

📡 dj-signals

Reliable signals for async side-effects — notifications, cache invalidation, analytics, cross-service coordination. Receivers are enqueued inside the database transaction via Celery, so rollbacks are respected and delivery is at-least-once. Pattern adapted from Haki Benita’s Reliable Signals in Django.

⚙️ dj-settings

Keeps settings.py organized with banner-style section headers in a predictable order. Use whenever settings are added, removed, or restructured.

🧪 dj-pytest

Three-layer pytest setup — repo against a real DB, service against mocked repos, API through HTTP — with pytest-django, pytest-celery for reliable-signal receivers, freezegun for time-sensitive logic, and a project conftest.py full of DTO factories and svcs-override fixtures.

✅ dj-lint

Runs ruff check, ruff format --check, and pyrefly check, then fixes whatever it finds. Use before committing, or any time you want a clean bill of health.

The Patterns at a Glance

  • Models — Meta first (verbose names, indexes, constraints). Fields ordered: identifiers → time → status → domain → relations. Uniqueness via UniqueConstraint, indexes via Meta.indexes — both optimized for actual query patterns. No business logic, no custom save(), no properties that compute. Multi-word fields get verbose_name, obscure fields get help_text.
  • DTOs — Pydantic v2 with from_attributes=True. All IDs are str. ORM objects never leave the repository.
  • Repositories — The only layer that touches the ORM. Returns DTOs. @transaction.atomic for multi-writes. One repo per aggregate root.
  • Services — Receives dependencies via __init__. Pure business logic. Zero ORM imports. Testable without a database.
  • API — django-ninja routes centralized in project/api.py. Input schemas are ninja.Schema, output schemas reuse DTOs.
  • Reliable Signals — Side-effects enqueued inside the DB transaction via Celery. At-least-once delivery. Idempotent receivers.
  • Settings — Sectioned with banner headers. python-decouple for env vars.

Example Project

See example_project for a working Django project built with these patterns — two apps ( products, orders), full repository + service + API layering, and tests at all three levels.

License

MIT. Use them, fork them, make them yours.

View this README on GitHub

推荐工具

换一个关键词,或者移除筛选条件。

安装

npx skillfish add dvf/opinionated-django