Django Celery Expert
vintasoftware/django-ai-plugins
Expert Django Celery guidance for asynchronous task processing.
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…
$ npx skills add dvf/opinionated-django --skill dj-services -a claude-codeProject install by default; add -g for ~/.claude/skills/.
$ gh skill install dvf/opinionated-django dj-services --agent claude-codeProject scope by default; add --scope user for a personal install. Needs GitHub CLI 2.90.0 or later (public preview).
$ git clone --depth 1 https://github.com/dvf/opinionated-django.git skills-src && mkdir -p .claude/skills && cp -r skills-src/skills/dj-services .claude/skills/dj-services && rm -rf skills-srcUse ~/.claude/skills/ instead of .claude/skills for a personal install. The folder must contain SKILL.md.
Claude Code skills documentation · loads skills from .claude/skills/
Install the "dj-services" agent skill from https://github.com/dvf/opinionated-django/tree/master/skills/dj-services into .claude/skills/dj-services/ in this project. Copy the whole folder (SKILL.md and every file beside it), keep the folder name "dj-services", then confirm the skill loads.Claude Code copies the folder itself, the same result as the manual copy. Check what it changed before you commit it.
$skill-installer install https://github.com/dvf/opinionated-django/tree/master/skills/dj-servicesType this inside Codex. $skill-installer <name> installs a curated skill from openai/skills. The installer writes to $CODEX_HOME/skills (default ~/.codex/skills). Restart Codex if the skill does not show up.
$ npx skills add dvf/opinionated-django --skill dj-services -a codexProject install goes to .agents/skills/; add -g for ~/.codex/skills/.
$ gh skill install dvf/opinionated-django dj-services --agent codexProject scope by default (.agents/skills/); add --scope user for a personal install.
$ git clone --depth 1 https://github.com/dvf/opinionated-django.git skills-src && mkdir -p .agents/skills && cp -r skills-src/skills/dj-services .agents/skills/dj-services && rm -rf skills-srcUse ~/.agents/skills/ instead of .agents/skills for a personal install.
Codex skills documentation · loads skills from .agents/skills/
Install the "dj-services" agent skill from https://github.com/dvf/opinionated-django/tree/master/skills/dj-services into .agents/skills/dj-services/ in this project. Copy the whole folder (SKILL.md and every file beside it), keep the folder name "dj-services", then confirm the skill loads.Codex copies the folder itself, the same result as the manual copy. Check what it changed before you commit it.
$ npx skills add dvf/opinionated-django --skill dj-services -a cursorProject install goes to .agents/skills/; add -g for ~/.cursor/skills/.
$ gh skill install dvf/opinionated-django dj-services --agent cursorProject scope by default (.agents/skills/); add --scope user for a personal install.
$ git clone --depth 1 https://github.com/dvf/opinionated-django.git skills-src && mkdir -p .cursor/skills && cp -r skills-src/skills/dj-services .cursor/skills/dj-services && rm -rf skills-srcUse ~/.cursor/skills/ instead of .cursor/skills for a personal install.
Cursor skills documentation · loads skills from .cursor/skills/, .agents/skills/, .claude/skills/, .codex/skills/
Install the "dj-services" agent skill from https://github.com/dvf/opinionated-django/tree/master/skills/dj-services into .cursor/skills/dj-services/ in this project. Copy the whole folder (SKILL.md and every file beside it), keep the folder name "dj-services", then confirm the skill loads.Cursor copies the folder itself, the same result as the manual copy. Check what it changed before you commit it.
$ gemini skills install https://github.com/dvf/opinionated-django.git --path skills/dj-services--scope user (default) or --scope workspace; --path is the subfolder of the repo that holds the skill; --consent skips the security confirmation prompt.
$ npx skills add dvf/opinionated-django --skill dj-services -a gemini-cliProject install goes to .agents/skills/; add -g for ~/.gemini/skills/.
$ gh skill install dvf/opinionated-django dj-services --agent gemini-cliProject scope by default (.agents/skills/); add --scope user for a personal install.
$ git clone --depth 1 https://github.com/dvf/opinionated-django.git skills-src && mkdir -p .gemini/skills && cp -r skills-src/skills/dj-services .gemini/skills/dj-services && rm -rf skills-srcUse ~/.gemini/skills/ instead of .gemini/skills for a personal install, then run /skills reload.
Gemini CLI skills documentation · loads skills from .gemini/skills/, .agents/skills/
Install the "dj-services" agent skill from https://github.com/dvf/opinionated-django/tree/master/skills/dj-services into .gemini/skills/dj-services/ in this project. Copy the whole folder (SKILL.md and every file beside it), keep the folder name "dj-services", then confirm the skill loads.Gemini CLI copies the folder itself, the same result as the manual copy. Check what it changed before you commit it.
$ gh skill install dvf/opinionated-django dj-servicesInstalls for Copilot at project scope by default; add --scope user for a personal install. Preview a skill first with gh skill preview. Needs GitHub CLI 2.90.0 or later (public preview).
$ npx skills add dvf/opinionated-django --skill dj-services -a github-copilotProject install goes to .agents/skills/; add -g for ~/.copilot/skills/.
$ git clone --depth 1 https://github.com/dvf/opinionated-django.git skills-src && mkdir -p .github/skills && cp -r skills-src/skills/dj-services .github/skills/dj-services && rm -rf skills-srcUse ~/.copilot/skills/ instead of .github/skills for a personal install. Commit .github/skills so cloud agent and code review can use it.
GitHub Copilot skills documentation · loads skills from .github/skills/, .claude/skills/, .agents/skills/
Install the "dj-services" agent skill from https://github.com/dvf/opinionated-django/tree/master/skills/dj-services into .github/skills/dj-services/ in this project. Copy the whole folder (SKILL.md and every file beside it), keep the folder name "dj-services", then confirm the skill loads.GitHub Copilot copies the folder itself, the same result as the manual copy. Check what it changed before you commit it.
$ npx skills add dvf/opinionated-django --skill dj-services -a opencodeOpenCode documents no install command of its own. Project install goes to .agents/skills/; add -g for ~/.config/opencode/skills/.
$ gh skill install dvf/opinionated-django dj-services --agent opencodeProject scope by default (.agents/skills/); add --scope user for a personal install.
$ git clone --depth 1 https://github.com/dvf/opinionated-django.git skills-src && mkdir -p .opencode/skills && cp -r skills-src/skills/dj-services .opencode/skills/dj-services && rm -rf skills-srcUse ~/.config/opencode/skills/ instead of .opencode/skills for a personal install.
OpenCode skills documentation · loads skills from .opencode/skills/, .claude/skills/, .agents/skills/
Install the "dj-services" agent skill from https://github.com/dvf/opinionated-django/tree/master/skills/dj-services into .opencode/skills/dj-services/ in this project. Copy the whole folder (SKILL.md and every file beside it), keep the folder name "dj-services", then confirm the skill loads.OpenCode copies the folder itself, the same result as the manual copy. Check what it changed before you commit it.
dj-servicesStructure 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…
Dj Services is an agent skill from dvf/opinionated-django. 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.
Its SKILL.md is about 3k tokens, which your agent loads only when the skill is triggered. It is a single SKILL.md file with no bundled scripts.
It sits in Backend & APIs, covering Backend development, Background jobs and Refactoring. It works with Django. The repository describes itself as: An opinionated Django project with Repository pattern, Pydantic DTOs, svcs DI, and Stripe-style ULID IDs. The licence is MIT.
Read from SKILL.md and the folder at commit f17fc2d. It shows what the files ask for, not the result of running them.
Pre-approves these tools, so the agent can use them without asking each time:
ReadWriteEditBashGrepGlobFrom allowed-tools in the SKILL.md frontmatter.
Shell commands in SKILL.md call:
uvFrom the folder's file list and the shell code blocks in SKILL.md.
Links to these hosts (documentation or services it may open):
svcs.hynek.meFrom URLs in SKILL.md, links to its own repository left out.
Names no API keys, tokens, secrets or passwords.
From names ending in _API_KEY, _TOKEN, _SECRET, _KEY or _PASSWORD in SKILL.md.
Dj Services loads about 3k tokens when it runs. Until then it costs about 107 tokens; SKILL.md has 801 words of instructions outside code blocks.
Estimates: characters ÷ 4, the usual rule of thumb; real counts depend on the model's tokenizer. Scripts and assets cost tokens only if the agent reads them.
The automated check noted patterns worth knowing about, such as sudo or a known installer.
allowed-tools: Read, Write, Edit, Bash, Grep, GlobAutomated static check — not a guarantee. Review scripts before installing. It scans the text of SKILL.md for risky patterns (piping downloads into a shell, reading credential files, hidden Unicode, destructive commands); files beside SKILL.md are not scanned.
The full file from dvf/opinionated-django at commit f17fc2d, republished under its MIT licence (© dvf). 801 words, ~2,970 tokens.
.claude/skills/dj-services/SKILL.md (or your agent's skills folder).This project separates Django's framework concerns from business logic using a plain service layer, wired with the svcs service locator. The result:
__init__, call methods on them, and return DTOs.get[T]() helper. The same call works in views, tasks, commands, anywhere.svcs is a tiny, typed, well-maintained service locator — no metaclasses, no decorators, no framework coupling.get[T](type[T]) -> T preserves types through IDE/type-checker inference.src/project/services.pyimport svcs
from products.repositories.product import ProductRepository
from products.services.product import ProductService
from orders.repositories.order import OrderRepository
from orders.services.order import OrderService
registry = svcs.Registry()
# --- Services (factories pull repos from the container) ------------------
def _product_service_factory(container: svcs.Container) -> ProductService:
repo = container.get(ProductRepository)
return ProductService(repo)
def _order_service_factory(container: svcs.Container) -> OrderService:
repo = container.get(OrderRepository)
product_repo = container.get(ProductRepository)
return OrderService(repo, product_repo)
def register_services(registry: svcs.Registry) -> None:
"""Register every real factory. Tests re-run this to restore overrides."""
# --- Repositories -----------------------------------------------------
registry.register_factory(ProductRepository, ProductRepository)
registry.register_factory(OrderRepository, OrderRepository)
# --- Services ---------------------------------------------------------
registry.register_factory(ProductService, _product_service_factory)
registry.register_factory(OrderService, _order_service_factory)
register_services(registry)
def get[T](service_type: type[T]) -> T:
"""Resolve a service from the registry. Works anywhere — views, tasks, commands, tests."""
return svcs.Container(registry).get(service_type)Patterns to follow:
register_services(registry). Called once at import; tests re-run it on teardown to restore any overridden factories through the public API.register_factory(Repo, Repo) — the class is its own factory because repositories take no constructor arguments._<entity>_service_factory(container) resolves every dependency from the container and hands it to the service's __init__. No hidden imports, no module-level singletons.svcs doesn't enforce this, but it keeps the file readable.project.services.registry.File: src/<app>/services/<entity>.py
from decimal import Decimal
from typing import List
from ..dtos.product import ProductDTO
from ..repositories.product import ProductRepository
class ProductService:
def __init__(self, repo: ProductRepository):
self.repo = repo
def create_product(self, name: str, price: Decimal, stock: int) -> ProductDTO:
return self.repo.create(name=name, price=price, stock=stock)
def get_product(self, product_id: str) -> ProductDTO:
return self.repo.get_by_id(product_id)
def list_products(self) -> List[ProductDTO]:
return self.repo.list_all()Rules:
__init__. The service never instantiates its own repositories or services. If a service needs another service, pass it in..objects, no F() / Q(), no model imports, no select_related. All database access goes through a repository.list[DTO]. Never a model instance, never a queryset.str. See the dj-prefixed-ulids skill.ValueError, PermissionError, domain-specific exceptions — not Http404 or anything Django-flavored. The view layer turns them into HTTP responses.When a service method touches more than one aggregate — e.g. creating an order that decrements product stock — inject both repositories and orchestrate them. Example from OrderService:
class OrderService:
def __init__(self, repo: OrderRepository, product_repo: ProductRepository):
self.repo = repo
self.product_repo = product_repo
def create_order(self, items: List[Dict[str, Any]]) -> OrderDTO:
for item in items:
product = self.product_repo.get_by_id(item["product_id"])
if product.stock < item["quantity"]:
raise ValueError(
f"Insufficient stock for product {product.name}: "
f"requested {item['quantity']}, available {product.stock}"
)
order = self.repo.create(items=items)
for item in items:
self.product_repo.decrement_stock(item["product_id"], item["quantity"])
return orderNotes:
with transaction.atomic(): — that's one of the very few django.db imports allowed in a service.get(...) calls inside service methods.from project.services import get
from project.types import AuthedRequest
@products_router.post("/", response={201: ProductDTO})
def create_product(request: AuthedRequest, payload: CreateProductIn):
service = get(ProductService)
return Status(201, service.create_product(**payload.dict()))Annotating request as AuthedRequest (defined in src/project/types.py) makes the auth contract explicit and narrows request.user to a guaranteed-authenticated Django User. This is a typing contract, not runtime enforcement — auth is still expected to be wired via middleware or ninja's auth= parameter.
from celery import shared_task
from project.services import get
from products.services.product import ProductService
@shared_task
def reprice_product(product_id: str, new_price: str) -> None:
service = get(ProductService)
service.update_price(product_id, Decimal(new_price))from django.core.management.base import BaseCommand
from project.services import get
from products.services.product import ProductService
class Command(BaseCommand):
def handle(self, *args, **options):
service = get(ProductService)
for dto in service.list_products():
self.stdout.write(dto.name)The same get() call works in all three contexts because the registry is global and the container is cheap to construct.
Services are tested without a database. Pass in a MagicMock for each repository, configure its return values, and assert on the service's behavior.
from decimal import Decimal
from unittest.mock import MagicMock
import pytest
from products.dtos.product import ProductDTO
from products.services.product import ProductService
def test_create_product_delegates_to_repo():
repo = MagicMock()
expected = ProductDTO(id="prd_fake", name="Widget", price=Decimal("9.99"), stock=5)
repo.create.return_value = expected
service = ProductService(repo)
result = service.create_product(name="Widget", price=Decimal("9.99"), stock=5)
assert result is expected
repo.create.assert_called_once_with(name="Widget", price=Decimal("9.99"), stock=5)
def test_create_order_rejects_insufficient_stock():
order_repo = MagicMock()
product_repo = MagicMock()
product_repo.get_by_id.return_value = ProductDTO(
id="prd_fake", name="Widget", price=Decimal("9.99"), stock=1
)
service = OrderService(order_repo, product_repo)
with pytest.raises(ValueError, match="Insufficient stock"):
service.create_order(items=[{"product_id": "prd_fake", "quantity": 5}])
order_repo.create.assert_not_called()This is the most important test layer — it proves the business logic is correct independently of Django, migrations, fixtures, or the database. If a service's tests need @pytest.mark.django_db, something has leaked: find the ORM call and push it back into a repository.
For integration tests that go through the API, override a factory to substitute a fake or a stub:
from project.services import register_services, registry
from products.services.product import ProductService
@pytest.fixture
def fake_product_service():
fake = MagicMock(spec=ProductService)
registry.register_factory(ProductService, lambda _: fake)
yield fake
register_services(registry) # restore the real factories via public APIfrom app.models import X, the service is doing ORM work. Move it to the repository.SomeRepository() inside a service method. Inject it via __init__ and hold the reference.get(SomeService).method(...), and pass the result back.register_factory(Service, Service). That only works for repositories because they take no arguments. Services need a factory that resolves their dependencies.request.user from the service. Pass the caller's identity as an explicit argument (user_id: str) so the service stays framework-agnostic.src/<app>/services/ has an __init__ that takes its dependencies explicitly.src/<app>/services/ imports from django.db.models, <app>.models, or uses .objects.src/project/services.py with a factory that resolves its dependencies from the container.@pytest.mark.django_db.uv run ruff check src
uv run pyrefly check src
uv run pytest© dvf, MIT. Rendered from Markdown: HTML in the file is shown as text, images as links, and headings moved down two levels. Raw file
Just SKILL.md in skills/dj-services of dvf/opinionated-django.
Open the folder on GitHubat commit f17fc2d
Dj Services next to the 5 skills that share the most tags, products or categories with it. Stars are the repository's; “used in” counts other GitHub owners with a copy.
| Skill | Stars | Used in | Tokens | Auto-check | Licence | Repo updated |
|---|---|---|---|---|---|---|
| Dj Services this skilldvf/opinionated-django | 109 | — | ~3k | Automated safety check: Notes | MIT | |
| Django Celery Expertvintasoftware/django-ai-plugins | 151 | — | ~1.2k | Automated safety check: Pass | None | |
| Sentry Python SDKgetsentry/sentry-for-ai | 268 | — | ~4.1k | Automated safety check: Pass | Apache-2.0 | |
| Django Prodavila7/claude-code-templates | 32k | 8 repos | ~1.7k | Automated safety check: Pass | MIT | |
| Django Celeryaffaan-m/ECC | 275k | 1 repos | ~3.3k | Automated safety check: Pass | MIT | |
| Django Celeryaffaan-m/ECC | 275k | — | ~240 | Automated safety check: Pass | MIT |
vintasoftware/django-ai-plugins
Expert Django Celery guidance for asynchronous task processing.
getsentry/sentry-for-ai
Full Sentry SDK setup for Python. An agent skill from getsentry/sentry-for-ai.
davila7/claude-code-templates
Master Django 5.x with async views, DRF, Celery, and Django Channels.
affaan-m/ECC
Django + Celery async task patterns — configuration, task design, beat scheduling, retries, canvas workflows, monitoring, and testing.
affaan-m/ECC
DjangoおよびCeleryを使用した非同期タスク処理。タスクキューイング、ワーカー管理、エラー処理、スケジューリング。Redis/RabbitMQ ブローカー統合。
PostHog/posthog
Keep heavy imports off the django.setup() path that every process (web, celery, temporal, migrate, shell, CI) pays for.
dvf/opinionated-django
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…
dvf/opinionated-django
Structure Django models with proper Meta classes, verbose names, and optimized indexes.
dvf/opinionated-django
Use Stripe-style prefixed ULID primary keys (e.g. An agent skill from dvf/opinionated-django.
dvf/opinionated-django
Set up and write pytest tests for an op-django project — pytest-django configuration, two-sided Celery testing (patched dispatch sites + plain-function task bodies, never eager mode), freezegun for…
dvf/opinionated-django
Set up a Django project into the op-django layout so the architecture, signals, and settings skills have a foundation to build on.
dvf/opinionated-django
Add reliable signals (async side-effects via Celery) to a Django feature.
Works with
Categories
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…. Dj Services is an agent skill from dvf/opinionated-django. 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.
Dj Services fits situations like: adding a new service; refactoring fat views; model methods into a service; wiring a service into the registry.
Run `npx skills add dvf/opinionated-django --skill dj-services -a claude-code`. Or copy the skill folder (skills/dj-services in dvf/opinionated-django) into .claude/skills/dj-services in your project. Claude Code loads it when a task matches its description.
Run `npx skills add dvf/opinionated-django --skill dj-services -a codex`. Or copy the skill folder (skills/dj-services in dvf/opinionated-django) into .agents/skills/dj-services in your project. Codex loads it when a task matches its description.
Cursor, Gemini CLI, GitHub Copilot and OpenCode also load SKILL.md folders. With the skills CLI, run `npx skills add dvf/opinionated-django --skill dj-services -a cursor` (or -a gemini-cli, github-copilot or opencode for the others). To copy it by hand, put the folder in .cursor/skills/dj-services, .gemini/skills/dj-services, .github/skills/dj-services and .opencode/skills/dj-services in your project.
Going by SKILL.md and its folder, Dj Services needs the command-line tools its instructions call (uv). Our summary lists: Python 3. Its frontmatter pre-approves these tools: Read, Write, Edit, Bash, Grep, Glob.
SKILL.md names 1 domain. As links in the text: svcs.hynek.me. This is read from the text; nothing was executed.
Our automated static check of SKILL.md found notes only (pre-approves every shell command (allowed-tools: bash)), nothing it rates as a warning. It is not a guarantee. Review the folder before installing.
Dj Services is published under the MIT licence (the repository's licence). It allows redistribution, so the full SKILL.md is shown on this page.
About 3k tokens (SKILL.md is roughly 12k characters). Agents keep only the skill's name and description in context until a task matches; then they load SKILL.md in full.
Skills that share tags, products or a category with Dj Services: Django Celery Expert (vintasoftware/django-ai-plugins, 151 stars), Sentry Python SDK (getsentry/sentry-for-ai, 268 stars), Django Pro (davila7/claude-code-templates, 32k stars) and Django Celery (affaan-m/ECC, 275k stars). The comparison table on this page puts their stars, adoption, token cost, safety result and licence side by side.
dvf (a GitHub user) maintains it in dvf/opinionated-django, which has 109 GitHub stars. The repository holds 9 skills in this directory. The repository was last updated on August 14, 2026.
Source: dvf/opinionated-django on GitHub. Facts on this page come from the repository at the commit we read; the author's words are quoted as theirs.