review-django-commands
Review Django management commands for proper structure and refactor if needed
Best use case
review-django-commands is best used when you need a repeatable AI agent workflow instead of a one-off prompt.
Review Django management commands for proper structure and refactor if needed
Teams using review-django-commands should expect a more consistent output, faster repeated execution, less prompt rewriting.
When to use this skill
- You want a reusable workflow that can be run more than once with consistent structure.
When not to use this skill
- You only need a quick one-off answer and do not need a reusable workflow.
- You cannot install or maintain the underlying files, dependencies, or repository context.
Installation
Claude Code / Cursor / Codex
Manual Installation
- Download SKILL.md from GitHub
- Place it in
.claude/skills/review-django-commands/SKILL.mdinside your project - Restart your AI agent — it will auto-discover the skill
How review-django-commands Compares
| Feature / Agent | review-django-commands | Standard Approach |
|---|---|---|
| Platform Support | Not specified | Limited / Varies |
| Context Awareness | High | Baseline |
| Installation Complexity | Unknown | N/A |
Frequently Asked Questions
What does this skill do?
Review Django management commands for proper structure and refactor if needed
Where can I find the source code?
You can find the source code on GitHub using the link provided at the top of the page.
SKILL.md Source
# review-django-commands
**Category**: Django Development
## Usage
```bash
/review-django-commands [<app-path>] [--fix] [--verbose]
```
## Arguments
| Argument | Required | Description |
|----------|----------|-------------|
| `<app-path>` | No | Path to Django app or project (default: current directory) |
| `--fix` | No | Automatically refactor non-compliant commands |
| `--verbose` | No | Show detailed analysis for each command |
## Purpose
Review Django management commands to ensure they follow the thin-command/service-layer pattern:
1. **Scans** all management commands in the project
2. **Analyzes** command structure for anti-patterns
3. **Reports** compliance with best practices
4. **Refactors** non-compliant commands (with `--fix`)
## Best Practice Pattern
Commands should be thin wrappers that delegate to services:
```
apps/{app}/
├── management/
│ └── commands/
│ └── {command_name}.py # Thin: parse args, call service, format output
└── services/
└── {domain}.py # Fat: business logic, DB operations
```
### Good Pattern
```python
# management/commands/publish_scheduled.py
class Command(BaseCommand):
def add_arguments(self, parser):
parser.add_argument("--dry-run", action="store_true")
def handle(self, *args, **options):
result = publish_scheduled_articles(dry_run=options["dry_run"])
self.stdout.write(self.style.SUCCESS(f"Published {result.count} articles"))
```
```python
# services/publishing.py
def publish_scheduled_articles(dry_run: bool = False) -> PublishResult:
"""Business logic lives here - testable without command scaffolding."""
...
```
### Anti-Patterns to Detect
| Anti-Pattern | Description | Severity |
|--------------|-------------|----------|
| Fat commands | Business logic in `handle()` | HIGH |
| Direct ORM queries | Complex querysets in command | HIGH |
| Missing service layer | No corresponding service file | MEDIUM |
| No type hints | Missing return types, parameters | LOW |
| No docstrings | Missing command help text | LOW |
| Hardcoded values | Magic numbers/strings in logic | MEDIUM |
---
## Execution Instructions for Claude Code
When this command is run, Claude Code should:
### 1. Parse Arguments
```
APP_PATH = first positional argument or "."
FIX_MODE = true if --fix specified
VERBOSE = true if --verbose specified
```
### 2. Find Management Commands
Scan for management command files:
```bash
find "$APP_PATH" -path "*/management/commands/*.py" -not -name "__init__.py"
```
### 3. Analyze Each Command
For each command file, analyze:
#### 3.1 Command Structure Check
```python
CHECKS = {
"thin_handle": {
"description": "handle() delegates to service",
"severity": "HIGH",
"check": "handle method < 30 lines, calls external function"
},
"no_orm_in_handle": {
"description": "No direct ORM queries in handle()",
"severity": "HIGH",
"check": "No Model.objects.* calls in handle()"
},
"service_exists": {
"description": "Corresponding service file exists",
"severity": "MEDIUM",
"check": "services/{domain}.py exists in same app"
},
"type_hints": {
"description": "Methods have type hints",
"severity": "LOW",
"check": "handle() has return type annotation"
},
"has_help": {
"description": "Command has help text",
"severity": "LOW",
"check": "help attribute or docstring defined"
},
"no_hardcoded": {
"description": "No hardcoded values in logic",
"severity": "MEDIUM",
"check": "No magic numbers/strings in handle()"
}
}
```
#### 3.2 Complexity Metrics
Calculate:
- Lines of code in `handle()` method
- Number of ORM calls
- Cyclomatic complexity (if/for/while count)
- Number of external function calls
### 4. Report Results
#### Default Output
```
Django Command Review
=====================
Project: ./apps
Commands found: 5
Results:
apps/blog/management/commands/publish_scheduled.py: PASS
apps/blog/management/commands/cleanup_drafts.py: FAIL
- [HIGH] Fat command: handle() has 85 lines, should be < 30
- [HIGH] Direct ORM: 3 Model.objects.* calls in handle()
- [MEDIUM] No service: services/drafts.py not found
apps/users/management/commands/sync_users.py: WARN
- [LOW] No type hints on handle()
- [LOW] Missing help text
apps/orders/management/commands/process_orders.py: FAIL
- [HIGH] Fat command: handle() has 120 lines
- [HIGH] Direct ORM: 8 Model.objects.* calls
- [MEDIUM] Hardcoded values: found 3 magic numbers
Summary:
Passed: 2/5
Warnings: 1/5
Failed: 2/5
Run with --fix to refactor non-compliant commands.
```
#### Verbose Output (--verbose)
```
Django Command Review
=====================
=== apps/blog/management/commands/cleanup_drafts.py ===
Current Structure:
handle() lines: 85
ORM calls: 3
Complexity: 12
External calls: 0
Issues:
[HIGH] Fat command
Line 25-110: Business logic should move to service
[HIGH] Direct ORM queries
Line 32: Draft.objects.filter(status='draft', created_at__lt=cutoff)
Line 45: Draft.objects.exclude(author__is_active=True)
Line 78: draft.delete()
[MEDIUM] No service layer
Expected: apps/blog/services/drafts.py
Recommended Refactoring:
1. Create apps/blog/services/drafts.py
2. Move lines 25-110 to cleanup_old_drafts() function
3. Command should only: parse args, call service, format output
...
```
### 5. Fix Mode (--fix)
If `--fix` is specified for failing commands:
#### 5.1 Create Service File
```python
# apps/blog/services/drafts.py
from dataclasses import dataclass
from django.utils import timezone
from apps.blog.models import Draft
@dataclass
class CleanupResult:
deleted_count: int
skipped_ids: list[int]
def cleanup_old_drafts(days: int = 30, dry_run: bool = False) -> CleanupResult:
"""
Delete drafts older than specified days.
Business logic extracted from management command.
"""
cutoff = timezone.now() - timezone.timedelta(days=days)
drafts = Draft.objects.filter(
status='draft',
created_at__lt=cutoff,
).exclude(author__is_active=True)
if dry_run:
return CleanupResult(deleted_count=drafts.count(), skipped_ids=[])
deleted = 0
skipped = []
for draft in drafts:
try:
draft.delete()
deleted += 1
except Exception:
skipped.append(draft.id)
return CleanupResult(deleted_count=deleted, skipped_ids=skipped)
```
#### 5.2 Refactor Command
```python
# apps/blog/management/commands/cleanup_drafts.py
from django.core.management.base import BaseCommand
from apps.blog.services.drafts import cleanup_old_drafts
class Command(BaseCommand):
help = "Delete old draft articles that haven't been published"
def add_arguments(self, parser):
parser.add_argument(
"--days",
type=int,
default=30,
help="Delete drafts older than this many days (default: 30)",
)
parser.add_argument(
"--dry-run",
action="store_true",
help="Show what would be deleted without making changes",
)
def handle(self, *args, **options) -> None:
days = options["days"]
dry_run = options["dry_run"]
if dry_run:
self.stdout.write(self.style.WARNING("DRY RUN - no changes will be made"))
result = cleanup_old_drafts(days=days, dry_run=dry_run)
if result.skipped_ids:
self.stderr.write(
self.style.ERROR(f"Failed to delete: {result.skipped_ids}")
)
self.stdout.write(
self.style.SUCCESS(f"Deleted {result.deleted_count} drafts")
)
```
#### 5.3 Report Fixes
```
Refactoring non-compliant commands...
apps/blog/management/commands/cleanup_drafts.py:
Created: apps/blog/services/drafts.py
Backup: cleanup_drafts.py.backup
Refactored: cleanup_drafts.py
Status: FIXED
apps/orders/management/commands/process_orders.py:
Created: apps/orders/services/orders.py
Backup: process_orders.py.backup
Refactored: process_orders.py
Status: FIXED
Fixed: 2 commands
Re-run to verify: /review-django-commands
```
### 6. Exit Codes
- `0`: All commands compliant
- `1`: Some commands need refactoring (and --fix not specified)
- `2`: No management commands found
---
## Examples
```bash
# Review all commands in current project
/review-django-commands
# Review specific app
/review-django-commands apps/blog
# Detailed analysis
/review-django-commands --verbose
# Auto-refactor non-compliant commands
/review-django-commands --fix
# Review and fix specific app
/review-django-commands apps/orders --fix --verbose
```
## Service Layer Guidelines
When refactoring commands, services should:
| Aspect | Guideline |
|--------|-----------|
| **Location** | `apps/{app}/services/{domain}.py` |
| **Return type** | Use `@dataclass` for complex results |
| **Parameters** | Accept primitive types, not Django request/options |
| **Side effects** | Clearly document any side effects |
| **Testability** | No command scaffolding required to test |
| **Reusability** | Callable from views, tasks, other commands |
## Related Skills
| Skill | Purpose |
|-------|---------|
| `python-experts:django-dev` | Django management command patterns |
| `python-experts:python-style` | Python coding standards |
| `python-experts:python-testing-expert` | Testing service functions |
## Related Commands
| Command | Purpose |
|---------|---------|
| `/parallel-ready-django` | Check Django project for parallel development |
| `/parallel-fix-django` | Fix Django blockers for parallelization |Related Skills
python-code-review
Python code review guidelines (security, performance, bugs, style). Auto-loads when reviewing Python code or analyzing code quality.
parallel-ready-django
Audit and prepare a Django codebase for parallel multi-agent development. Use when asked to check if a Django project is ready for parallelization, prepare a repo for multi-agent work, audit codebase structure, set up orchestration infrastructure, or identify blockers for parallel development. Analyzes Django apps, models, migrations, and module boundaries.
parallel-fix-django
Fix Django-specific blockers identified in parallelization readiness assessment
django-project-setup
Set up a new Django 6.0 project with modern tooling (uv, direnv, HTMX, OAuth, DRF, testing). Use when the user wants to create a Django project from scratch with production-ready configuration.
django
Django development patterns and conventions (2025). Auto-loads when working with Django models, views, URLs, forms, templates, management commands, or project structure. Includes async support and type hints.
django-api
Django API development for 2025. Covers Django Ninja (modern, async-first, type-safe) and Django REST Framework (mature, ecosystem-rich). Use when building REST APIs, choosing between frameworks, implementing authentication, permissions, filtering, pagination, or async endpoints.
code-review
Review code changes between commits for security, logic, performance, and style issues
typescript-code-review
TypeScript and React code review guidelines (type safety, React patterns, performance). Auto-loads when reviewing TypeScript/React code.
zod
Zod schema validation patterns and type inference. Auto-loads when validating schemas, parsing data, validating forms, checking types at runtime, or using z.object/z.string/z.infer in TypeScript.
typescript-import-style
Merge-friendly import formatting (one-per-line, alphabetical). Auto-loads when writing TypeScript/JavaScript imports to minimize merge conflicts in parallel development. Enforces consistent grouping and sorting.
setup-mcp-auth
Configure authentication for an existing FastMCP server
fastmcp
FastMCP TypeScript framework patterns for MCP servers. Auto-loads when building MCP servers, creating tools/resources/prompts, implementing authentication, configuring transports, or working with FastMCP in TypeScript.