Django Code Simplifier
Transform complex Django code into clean, performant, idiomatic solutions.
Analysis Scripts
Comprehensive Django analysis (runs all Django checks)
python scripts/analyze_django.py /path/to/project
Individual analyzers:
python scripts/find_django_issues.py . # N+1 queries, fat views, basic issues python scripts/find_django_antipatterns.py . # ORM misuse, security issues, model problems python scripts/find_django_overengineering.py . # Unnecessary abstractions, premature patterns
Filter by category or severity:
python scripts/find_django_antipatterns.py . --category performance python scripts/find_django_antipatterns.py . --category security python scripts/find_django_antipatterns.py . --min-severity medium
JSON output for CI/tooling
python scripts/analyze_django.py . --format json > report.json
What Each Script Detects
find_django_issues.py
-
Basic Issues
-
N+1 query risks (.all() in loops)
-
.save() / .delete() / .create() in loops
-
Fat views (100+ lines)
-
Hardcoded URLs instead of reverse()
-
Missing select_related / prefetch_related
find_django_antipatterns.py
- Comprehensive Anti-Patterns
Category Issues Detected
query save_in_loop , create_in_loop , unbounded_queryset , update_without_f , raw_sql , deprecated_extra
performance excessive_queries , nested template loops, template queries
model missing_str_method , fat_model , too_many_fields
view hardcoded_url , fat_view_method , url_without_name
form query_in_form_clean
security hardcoded_secret , debug_true , mark_safe_usage , eval_exec_usage
find_django_overengineering.py
- Over-Engineering
Issue Description
single_impl_abstract_model
Abstract model with only one concrete child
unused_abstract_model
Abstract model never extended
unnecessary_manager
Custom manager with ≤1 method
unnecessary_signal
Signal for simple save logic
single_use_mixin
Mixin used only once
deep_form_inheritance
Form/Serializer with 3+ inheritance levels
unnecessary_middleware
Middleware with minimal logic
unnecessary_service_layer
Service wrapping simple CRUD
Common Django Patterns
N+1 Query Prevention
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')
When to Use What
Need Use
Simple CRUD Generic views, ModelForm
Complex business logic Model methods
Cross-cutting concerns Middleware (sparingly)
Decoupled apps Signals (sparingly)
Query optimization select_related, prefetch_related
Bulk operations bulk_create, bulk_update, update()