Interview question
What are performance considerations when using decorators and signals in Django? Django में decorators और signals use करते समय performance considerations क्या हैं?
Answer
Both decorators and signals add overhead. Minimize expensive operations in decorators, use caching strategically, and profile your code to identify bottlenecks. Async operations prevent request blocking.
| Issue | Impact | Solution |
|---|---|---|
| Heavy decorator logic | Slows response time | Cache results, defer heavy work |
| Multiple decorators | Each adds overhead | Combine related logic |
| Synchronous signals | Blocks request | Use Celery for async |
| N+1 queries in signals | Database strain | Use select_related |
// ❌ SLOW - Heavy decorator
def check_permission_decorator(func):
def wrapper(request, *args, **kwargs):
# This runs on EVERY request
all_permissions = Permission.objects.all() # Heavy query
allowed_perms = set(p.id for p in all_permissions)
if request.user.id not in allowed_perms:
return JsonResponse({"error": "No access"}, status=403)
return func(request, *args, **kwargs)
return wrapper
// ✅ FAST - Cached decorator
from django.core.cache import cache
def check_permission_decorator_cached(func):
def wrapper(request, *args, **kwargs):
cache_key = f"perms_{request.user.id}"
permissions = cache.get(cache_key)
if permissions is None:
permissions = set(
request.user.groups.values_list("permissions__id", flat=True)
)
cache.set(cache_key, permissions, 3600)
if not permissions:
return JsonResponse({"error": "No access"}, status=403)
return func(request, *args, **kwargs)
return wrapper
// Performance Testing
import time
from django.test import TestCase
class DecoratorPerformanceTest(TestCase):
def test_decorator_overhead(self):
def slow_decorator(func):
def wrapper(*args, **kwargs):
time.sleep(0.1) # Simulates 100ms overhead
return func(*args, **kwargs)
return wrapper
@slow_decorator
def view(request):
return JsonResponse({"data": "response"})
start = time.time()
for _ in range(100):
view(MagicMock())
total = time.time() - start
print(f"100 requests: {total}s (10s overhead from decorator)")
// ❌ SLOW - Signal with N+1 queries
@receiver(post_save, sender=Book)
def update_categories(sender, instance, **kwargs):
# N+1 problem: queries all related categories
for category in Category.objects.all():
category.book_count = category.books.count()
category.save()
// ✅ FAST - Optimized signal
@receiver(post_save, sender=Book)
def update_category_optimized(sender, instance, **kwargs):
# Only update related category
if instance.category:
instance.category.book_count = instance.category.books.count()
instance.category.save()
// Profile Decorators
from django.test.utils import override_settings
import cProfile
import pstats
@override_settings(DEBUG=True)
def profile_view():
pr = cProfile.Profile()
pr.enable()
# Run expensive view
expensive_view(MagicMock())
pr.disable()
ps = pstats.Stats(pr)
ps.print_stats() # Shows where time is spent
// Minimize Decorator Stack
# ❌ Multiple decorators per view
@decorator1
@decorator2
@decorator3
@decorator4
@expensive_decorator
def view1(request):
pass
# ✅ Combine related decorators
def combined_auth_and_logging(func):
def wrapper(request, *args, **kwargs):
# Do both auth and logging efficiently
logger.info(f"Request from {request.user}")
if not request.user.is_authenticated:
return redirect("login")
return func(request, *args, **kwargs)
return wrapper
@combined_auth_and_logging
def view2(request):
passDecorators और signals overhead add करते हैं। Heavy operations को defer करो, caching use करो, async use करो।
| Issue | Impact | Solution |
|---|---|---|
| Heavy decorator | Slow response | Cache, defer work |
| Sync signals | Blocks request | Use Celery |
| N+1 queries | DB strain | Optimize queries |
// ❌ Slow
@decorator_with_heavy_query
def view(request):
pass
// ✅ Fast
@cached_permission_decorator
def view(request):
pass
// Use Celery for heavy signals
@receiver(post_save, sender=Book)
def trigger_async(sender, instance, **kwargs):
process_async.delay(instance.id)Was this answer clear?