Skip to content
review-django-commands logo

Review Django Commands

review-django-commands

Review Django management commands for proper structure and refactor if needed

jpoutrin/product-forge0installs16stars

SKILL.md

Full skill instructions

review-django-commands

Category: Django Development

Usage

/​review-django-commands [<app-path>] [--fix] [--verbose]

Arguments

ArgumentRequiredDescription
<app-path>NoPath to Django app or project (default: current directory)
--fixNoAutomatically refactor non-compliant commands
--verboseNoShow 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

# 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"))
# 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-PatternDescriptionSeverity
Fat commandsBusiness logic in handle()HIGH
Direct ORM queriesComplex querysets in commandHIGH
Missing service layerNo corresponding service fileMEDIUM
No type hintsMissing return types, parametersLOW
No docstringsMissing command help textLOW
Hardcoded valuesMagic numbers/​strings in logicMEDIUM

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:

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
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
# 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
# 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

# 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:

AspectGuideline
Locationapps/​{app}/​services/​{domain}.py
Return typeUse @dataclass for complex results
ParametersAccept primitive types, not Django request/​options
Side effectsClearly document any side effects
TestabilityNo command scaffolding required to test
ReusabilityCallable from views, tasks, other commands

Related Skills

SkillPurpose
python-experts:django-devDjango management command patterns
python-experts:python-stylePython coding standards
python-experts:python-testing-expertTesting service functions

Related Commands

CommandPurpose
/​parallel-ready-djangoCheck Django project for parallel development
/​parallel-fix-djangoFix Django blockers for parallelization

More skills from jpoutrin

mcp-security logo
jpoutrin/product-forge

mcp-security

Use when securing MCP servers, preventing prompt injection, implementing authorization, validating user input, or building secure multi-agent pipelines. Provides 5-layer defense architecture patterns.

16 0
View
create-skill logo
jpoutrin/product-forge

create-skill

Create a new Claude Code agent skill with proper folder structure and SKILL.md format. Use when the user wants to add a model-invoked skill that Claude uses autonomously based on context. Handles skill folder creation with SKILL.md and optional reference files.

16 0
View
design-system logo
jpoutrin/product-forge

design-system

Design system management for building and reusing UI components, tokens, and patterns. Use when working with component libraries, design tokens, style guides, or reusable UI patterns to ensure consistency and promote component reuse.

16 0
View
fastmcp logo
jpoutrin/product-forge

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.

16 0
View
portman logo
jpoutrin/product-forge

portman

Port management for local development with git worktrees. Use when setting up local development environments, booking ports for services to avoid collisions between parallel worktrees, or configuring docker-compose with dynamic ports.

16 0
View
qa-test-management logo
jpoutrin/product-forge

qa-test-management

Automatic QA test lifecycle management, naming conventions, and directory structure. Use when creating, organizing, or tracking QA tests to ensure proper naming, directory structure, and status transitions.

16 0
View
create-command logo
jpoutrin/product-forge

create-command

Create a new Claude Code slash command with proper YAML frontmatter structure. Use when the user wants to add a custom slash command to a plugin. Handles command file creation with description, argument hints, allowed tools, and all advanced features.

16 0
View
dependency-alignment logo
jpoutrin/product-forge

dependency-alignment

Dependency version alignment and conflict resolution for parallel development. Use when extracting dependencies from Tech Specs to ensure version compatibility with existing project dependencies and transitive dependencies.

16 0
View
code-review logo
jpoutrin/product-forge

code-review

Review code changes between commits for security, logic, performance, and style issues

16 0
View
create-persona logo
jpoutrin/product-forge

create-persona

Create detailed user persona with guided questions

16 0
View
bl-undo logo
jpoutrin/product-forge

bl-undo

Undo git-branchless operations - recover from mistakes interactively

16 0
View

Popular AI tools

Kaiber logo
Video

Kaiber

Generate, edit, and beat-sync AI video with leading models in one workspace.

Paid
View
Vimcal logo
Productivity

Vimcal

The world's fastest calendar for remote work

Free
View

Transform Your Design with AI Designer by ImgCreator.ai

Freemium
View
Akool AI logo
Content & writing

Akool AI

Revolutionizing Video Production with AI-Powered Creativity

Paid
View

Extend an image past the frame and let AI fill the new aspect ratio.

Freemium
View
StarByFace logo
Security

StarByFace

Discover your celebrity doppelgänger with StarByFace!

Free
View
C

ChainClarity explains 700+ crypto whitepapers in plain English, with layered summaries, comparisons, research tools, alerts, and a $4.99 Pro plan.

Freemium
View
Opus Clip logo
Coding & apps

Opus Clip

Opus.ai: Revolutionize Your Web Experience

Free
View