Simplify overly complex Django code and detect Django-specific anti-patterns. Use when user asks to simplify, refactor, or improve Django code, find N+1 queries, detect Django anti-patterns, optimize QuerySets, or analyze Django project architecture. Triggers on requests involving Django models, views, QuerySets, ORM optimization, serializers, forms, signals, middleware, or Django best practices. For general Python analysis, use the python-simplifier skill instead.
name: django-simplifier
description: Simplify overly complex Django code and detect Django-specific anti-patterns. Use when user asks to simplify, refactor, or improve Django code, find N+1 queries, detect Django anti-patterns, optimize QuerySets, or analyze Django project architecture. Triggers on requests involving Django models, views, QuerySets, ORM optimization, serializers, forms, signals, middleware, or Django best practices. For general Python analysis, use the python-simplifier skill instead.
Django Code Simplifier
Transform complex Django code into clean, performant, idiomatic solutions.
# Before: N+1 problem
for order in Order.objects.all():
print(order.customer.name) # Query per iteration!
# After: select_related (ForeignKey/OneToOne)
for order in Order.objects.select_related('customer'):
print(order.customer.name) # Single query
# After: prefetch_related (ManyToMany/reverse FK)
for customer in Customer.objects.prefetch_related('orders'):
print(customer.orders.count())
Bulk Operations
# Before: Loop creates (N queries)
for data in items:
Model.objects.create(**data)
# After: bulk_create (1 query)
Model.objects.bulk_create([Model(**d) for d in items])
# Before: Loop updates
for obj in queryset:
obj.status = 'done'
obj.save()
# After: Single update
queryset.update(status='done')
# With F() for safe increment
from django.db.models import F
Product.objects.filter(id=1).update(stock=F('stock') - 1)
Fat View → Thin View
# Before: Business logic in view
def order_view(request, order_id):
order = Order.objects.get(id=order_id)
# 50 lines of business logic...
return render(...)
# After: Logic in model/service
def order_view(request, order_id):
order = get_object_or_404(Order, id=order_id)
result = order.process() # Logic moved to model
return render(request, 'order.html', {'result': result})
Signals → Explicit Methods
# Before: Hidden signal
@receiver(post_save, sender=Order)
def update_inventory(sender, instance, **kwargs):
# Hard to trace, implicit behavior
...
# After: Explicit method
class Order(models.Model):
def complete(self):
self.status = 'completed'
self.save()
self._update_inventory() # Explicit, traceable
URL Best Practices
# Before: Hardcoded
return redirect('/orders/123/')
# After: Named URL
from django.urls import reverse
return redirect(reverse('order-detail', args=[123]))
# Or with shortcut
from django.shortcuts import redirect
return redirect('order-detail', pk=123)
Model Best Practices
class Order(models.Model):
# Always add __str__
def __str__(self):
return f"Order #{self.number}"
# Use TextChoices
class Status(models.TextChoices):
PENDING = 'pending', 'Pending'
COMPLETED = 'completed', 'Completed'
status = models.CharField(max_length=20, choices=Status.choices)
# Don't use null=True on CharField
# BAD: name = models.CharField(null=True, blank=True)
# GOOD:
name = models.CharField(blank=True, default='')
# Always set related_name
customer = models.ForeignKey(Customer, related_name='orders', on_delete=models.CASCADE)
class Meta:
ordering = ['-created_at']
indexes = [models.Index(fields=['status', 'created_at'])]
Django Over-Engineering Signs
Pattern
Problem
Solution
Abstract model with 1 child
Premature abstraction
Merge into concrete model
CBV with 6+ overrides
Too complex
Use function-based view
Single-use mixin
No reuse benefit
Inline the code
Many signal handlers
Hard to trace
Use explicit method calls
Custom manager for simple filter
Unnecessary
Use QuerySet methods
Service layer for CRUD
Extra indirection
Use model methods
Security Checklist
# Use get_object_or_404 with ownership check
order = get_object_or_404(Order, id=id, owner=request.user)
# Never use mark_safe with user data
# BAD: mark_safe(user_input)
# GOOD:
from django.utils.html import format_html
format_html('<p>{}</p>', user_input)
# Keep secrets out of code
# BAD: SECRET_KEY = 'hardcoded-secret'
# GOOD: SECRET_KEY = os.environ.get('SECRET_KEY')