tg-commands
Django management command patterns for the World of Darkness application. Use when creating custom management commands for data validation, monitoring, batch processing, or maintenance tasks. Triggers on command creation, validation tools, data integrity checks, or scheduled tasks.
When & Why to Use This Skill
This Claude skill provides a comprehensive library of Django management command patterns and templates designed for backend automation and system maintenance. It streamlines the development of custom CLI tools by providing standardized structures for data validation, system monitoring, and high-volume batch processing, ensuring best practices in error handling, output styling, and database transaction management.
Use Cases
- Automating data integrity audits to detect and resolve orphaned database records or model inconsistencies.
- Developing system monitoring scripts that output health statistics in JSON format for integration with external alerting tools.
- Executing large-scale data migrations or updates using batch processing patterns to optimize memory usage and transaction stability.
- Building interactive administrative commands with safety confirmation prompts and progress tracking for sensitive operations.
- Standardizing backend task development across teams using pre-defined templates for command structure and unit testing.
| name | tg-commands |
|---|---|
| description | Django management command patterns for the World of Darkness application. Use when creating custom management commands for data validation, monitoring, batch processing, or maintenance tasks. Triggers on command creation, validation tools, data integrity checks, or scheduled tasks. |
Management Command Patterns
Command Structure
Basic Command Template
from django.core.management.base import BaseCommand, CommandError
class Command(BaseCommand):
help = "Description of what this command does"
def add_arguments(self, parser):
# Positional arguments
parser.add_argument('chronicle_id', type=int)
# Optional arguments
parser.add_argument(
'--verbose',
action='store_true',
help='Show detailed output',
)
parser.add_argument(
'--limit',
type=int,
default=100,
help='Maximum records to process (default: 100)',
)
def handle(self, *args, **options):
verbose = options['verbose']
chronicle_id = options['chronicle_id']
try:
# Command logic here
self.stdout.write(self.style.SUCCESS('Command completed'))
except Exception as e:
raise CommandError(f'Command failed: {e}')
Output Styling
# Success (green)
self.stdout.write(self.style.SUCCESS('Operation successful'))
# Error (red)
self.stdout.write(self.style.ERROR('Something failed'))
# Warning (yellow)
self.stdout.write(self.style.WARNING('Potential issue'))
# Notice (cyan)
self.stdout.write(self.style.NOTICE('Information'))
# Standard output
self.stdout.write('Regular message')
# Verbose output (respect verbosity setting)
if options['verbosity'] >= 2:
self.stdout.write('Debug details...')
Data Validation Command
Pattern: validate_data_integrity
from django.core.management.base import BaseCommand
from django.db import transaction
class Command(BaseCommand):
help = "Validate data integrity across models"
def add_arguments(self, parser):
parser.add_argument(
'--fix',
action='store_true',
help='Attempt to fix issues automatically',
)
parser.add_argument(
'--model',
type=str,
help='Validate specific model only',
)
def handle(self, *args, **options):
fix_mode = options['fix']
issues = []
# Run validators
issues.extend(self.validate_characters(fix_mode))
issues.extend(self.validate_chronicles(fix_mode))
issues.extend(self.validate_orphans(fix_mode))
# Report results
if issues:
self.stdout.write(self.style.WARNING(f'Found {len(issues)} issues'))
for issue in issues:
self.stdout.write(f" - {issue}")
else:
self.stdout.write(self.style.SUCCESS('No issues found'))
def validate_characters(self, fix_mode):
"""Check character data consistency."""
issues = []
from characters.models import Character
# Check for characters without owners
orphans = Character.objects.filter(owner__isnull=True)
for char in orphans:
issues.append(f"Character {char.id} has no owner")
if fix_mode:
char.delete()
self.stdout.write(f" Fixed: Deleted orphan character {char.id}")
return issues
def validate_chronicles(self, fix_mode):
"""Check chronicle consistency."""
issues = []
# Add validation logic
return issues
def validate_orphans(self, fix_mode):
"""Check for orphaned related objects."""
issues = []
# Add orphan detection logic
return issues
Monitoring Command
Pattern: monitor_validation
import json
from datetime import timedelta
from django.core.management.base import BaseCommand
from django.utils import timezone
class Command(BaseCommand):
help = "Monitor validation status for alerting"
def add_arguments(self, parser):
parser.add_argument(
'--json',
action='store_true',
help='Output in JSON format for monitoring tools',
)
parser.add_argument(
'--period',
type=int,
default=24,
help='Analysis period in hours (default: 24)',
)
def handle(self, *args, **options):
period = timedelta(hours=options['period'])
since = timezone.now() - period
stats = self.collect_stats(since)
if options['json']:
self.stdout.write(json.dumps(stats, indent=2, default=str))
else:
self.print_report(stats)
def collect_stats(self, since):
"""Collect validation statistics."""
from characters.models import Character
from chronicles.models import Chronicle
return {
'timestamp': timezone.now().isoformat(),
'period_hours': 24,
'characters': {
'total': Character.objects.count(),
'created_recently': Character.objects.filter(created__gte=since).count(),
'modified_recently': Character.objects.filter(modified__gte=since).count(),
},
'chronicles': {
'total': Chronicle.objects.count(),
'active': Chronicle.objects.filter(end_date__isnull=True).count(),
},
'health': 'OK',
}
def print_report(self, stats):
"""Print human-readable report."""
self.stdout.write(self.style.NOTICE('=== Validation Monitor ==='))
self.stdout.write(f"Timestamp: {stats['timestamp']}")
self.stdout.write(f"Characters: {stats['characters']['total']}")
self.stdout.write(f" Created (24h): {stats['characters']['created_recently']}")
self.stdout.write(f"Chronicles: {stats['chronicles']['total']}")
self.stdout.write(self.style.SUCCESS(f"Health: {stats['health']}"))
Batch Processing Command
Pattern: Process in Batches
from django.core.management.base import BaseCommand
from django.db import transaction
class Command(BaseCommand):
help = "Process records in batches"
def add_arguments(self, parser):
parser.add_argument(
'--batch-size',
type=int,
default=1000,
help='Records per batch (default: 1000)',
)
parser.add_argument(
'--dry-run',
action='store_true',
help='Show what would be done without making changes',
)
def handle(self, *args, **options):
batch_size = options['batch_size']
dry_run = options['dry_run']
from characters.models import Character
queryset = Character.objects.filter(needs_processing=True)
total = queryset.count()
if dry_run:
self.stdout.write(f"Would process {total} records")
return
processed = 0
for batch in self.batch_iterator(queryset, batch_size):
with transaction.atomic():
for obj in batch:
self.process_record(obj)
processed += 1
self.stdout.write(f"Processed {processed}/{total}")
self.stdout.write(self.style.SUCCESS(f'Completed: {processed} records'))
def batch_iterator(self, queryset, batch_size):
"""Yield batches from queryset."""
start = 0
while True:
batch = list(queryset[start:start + batch_size])
if not batch:
break
yield batch
start += batch_size
def process_record(self, obj):
"""Process a single record."""
obj.needs_processing = False
obj.save(update_fields=['needs_processing'])
Interactive Command
Pattern: Confirmation and Progress
from django.core.management.base import BaseCommand
class Command(BaseCommand):
help = "Dangerous operation requiring confirmation"
def add_arguments(self, parser):
parser.add_argument(
'--force',
action='store_true',
help='Skip confirmation prompt',
)
def handle(self, *args, **options):
if not options['force']:
confirm = input('This will delete data. Continue? [y/N]: ')
if confirm.lower() != 'y':
self.stdout.write('Cancelled.')
return
# Show progress
from characters.models import Character
total = Character.objects.count()
for i, char in enumerate(Character.objects.iterator(), 1):
self.process(char)
if i % 100 == 0:
self.stdout.write(f'\rProcessed {i}/{total}', ending='')
self.stdout.flush()
self.stdout.write('') # Newline
self.stdout.write(self.style.SUCCESS('Complete'))
Command Testing
Test Pattern
from io import StringIO
from django.core.management import call_command
from django.test import TestCase
class CommandTestCase(TestCase):
def test_validate_command(self):
out = StringIO()
call_command('validate_data_integrity', stdout=out)
output = out.getvalue()
self.assertIn('No issues found', output)
def test_command_with_options(self):
out = StringIO()
call_command(
'validate_data_integrity',
'--fix',
'--verbose',
stdout=out
)
output = out.getvalue()
self.assertIn('SUCCESS', output)
def test_command_error_handling(self):
err = StringIO()
with self.assertRaises(SystemExit):
call_command('mycommand', '--invalid', stderr=err)
File Location
Commands go in:
app_name/
└── management/
├── __init__.py
└── commands/
├── __init__.py
└── command_name.py
Common Command Types
| Command Type | Purpose | Example |
|---|---|---|
| Validation | Check data integrity | validate_data_integrity |
| Monitoring | Health checks for alerts | monitor_validation |
| Batch processing | Update many records | recalculate_xp |
| Maintenance | Cleanup, archive | archive_old_scenes |
| Import/Export | Data migration | import_characters |
| Development | Testing helpers | create_test_data |