🔴 Normal API (Slow)
shop/api/serializers.py – Normal Version:
from rest_framework import serializers
from shop.models import Product, Category, Brand, Review
# ❌ Basic serializer (No optimization)
class CategorySerializer(serializers.ModelSerializer):
class Meta:
model = Category
fields = ['id', 'name', 'slug']
class BrandSerializer(serializers.ModelSerializer):
class Meta:
model = Brand
fields = ['id', 'name', 'slug', 'country']
class ReviewSerializer(serializers.ModelSerializer):
# ❌ প্রতিটি review এর জন্য user fetch করতে extra query
user_name = serializers.CharField(source='user.username')
class Meta:
model = Review
fields = ['id', 'user_name', 'rating', 'comment', 'created_at']
class ProductSerializer(serializers.ModelSerializer):
# ❌ Nested serializers - N+1 problem
category = CategorySerializer()
brand = BrandSerializer()
reviews = ReviewSerializer(many=True, source='review_set')
class Meta:
model = Product
fields = [
'id', 'name', 'slug', 'description', 'price', 'stock',
'category', 'brand', 'reviews', 'created_at'
]shop/api/views.py – Normal Version:
from rest_framework import viewsets
from rest_framework.decorators import action
from rest_framework.response import Response
from shop.models import Product, Category
from .serializers import ProductSerializer, CategorySerializer
# ❌ Basic ViewSet (No optimization)
class ProductViewSet(viewsets.ModelViewSet):
queryset = Product.objects.all() # ❌ No select_related
serializer_class = ProductSerializer
# ❌ N+1 problem হবে serialization এ
# প্রতিটি product এর জন্য:
# - category fetch করতে 1 query
# - brand fetch করতে 1 query
# - reviews fetch করতে 1 query
# 100 products = 1 + 100 + 100 + 100 = 301 queries!
class CategoryViewSet(viewsets.ModelViewSet):
queryset = Category.objects.all()
serializer_class = CategorySerializer✅ Optimized API (Fast)
shop/api/serializers.py – Optimized Version:
from rest_framework import serializers
from shop.models import Product, Category, Brand, Review
from django.core.cache import cache
# ✅ Lightweight serializers
class CategoryLightSerializer(serializers.ModelSerializer):
class Meta:
model = Category
fields = ['id', 'name', 'slug']
class BrandLightSerializer(serializers.ModelSerializer):
class Meta:
model = Brand
fields = ['id', 'name', 'slug']
class ReviewLightSerializer(serializers.ModelSerializer):
# ✅ user already prefetched থাকলে no extra query
user_name = serializers.CharField(source='user.username', read_only=True)
class Meta:
model = Review
fields = ['id', 'user_name', 'rating', 'comment', 'created_at']
read_only_fields = ['created_at']
# ✅ List এর জন্য lightweight serializer
class ProductListSerializer(serializers.ModelSerializer):
category_name = serializers.CharField(source='category.name', read_only=True)
brand_name = serializers.CharField(source='brand.name', read_only=True)
class Meta:
model = Product
fields = [
'id', 'name', 'slug', 'price', 'stock',
'category_name', 'brand_name',
'average_rating', 'review_count', 'is_featured'
]
read_only_fields = ['average_rating', 'review_count']
# ✅ Detail এর জন্য full serializer
class ProductDetailSerializer(serializers.ModelSerializer):
category = CategoryLightSerializer(read_only=True)
brand = BrandLightSerializer(read_only=True)
reviews = ReviewLightSerializer(many=True, read_only=True)
class Meta:
model = Product
fields = [
'id', 'name', 'slug', 'description', 'price', 'stock',
'category', 'brand', 'reviews',
'average_rating', 'review_count',
'is_active', 'is_featured', 'created_at', 'updated_at'
]
read_only_fields = ['average_rating', 'review_count', 'created_at', 'updated_at']
# ✅ Category with product count
class CategoryDetailSerializer(serializers.ModelSerializer):
product_count = serializers.IntegerField(read_only=True)
class Meta:
model = Category
fields = ['id', 'name', 'slug', 'description', 'product_count', 'created_at']shop/api/views.py – Optimized Version:
from rest_framework import viewsets, filters, status
from rest_framework.decorators import action
from rest_framework.response import Response
from rest_framework.permissions import IsAuthenticatedOrReadOnly
from django.core.cache import cache
from django.db.models import Prefetch, Q, Count, Avg
from django.utils.decorators import method_decorator
from django.views.decorators.cache import cache_page
from shop.models import Product, Category, Brand, Review
from .serializers import (
ProductListSerializer, ProductDetailSerializer,
CategoryDetailSerializer, ReviewLightSerializer
)
# ✅ Fully Optimized Product API
class ProductViewSetOptimized(viewsets.ModelViewSet):
permission_classes = [IsAuthenticatedOrReadOnly]
filter_backends = [filters.SearchFilter, filters.OrderingFilter]
search_fields = ['name', 'description']
ordering_fields = ['price', 'created_at', 'average_rating']
def get_queryset(self):
"""✅ Optimized queryset based on action"""
queryset = Product.objects.filter(is_active=True)
# ✅ List action - lightweight
if self.action == 'list':
queryset = queryset.select_related('category', 'brand')\
.only(
'id', 'name', 'slug', 'price', 'stock',
'average_rating', 'review_count', 'is_featured',
'category__name', 'brand__name'
)
# ✅ Detail action - full data
elif self.action == 'retrieve':
queryset = queryset.select_related('category', 'brand')\
.prefetch_related(
Prefetch(
'reviews',
queryset=Review.objects.select_related('user')
.order_by('-created_at')[:10]
)
)
# ✅ Filtering
category = self.request.query_params.get('category')
if category:
queryset = queryset.filter(category__slug=category)
brand = self.request.query_params.get('brand')
if brand:
queryset = queryset.filter(brand__slug=brand)
min_price = self.request.query_params.get('min_price')
if min_price:
queryset = queryset.filter(price__gte=min_price)
max_price = self.request.query_params.get('max_price')
if max_price:
queryset = queryset.filter(price__lte=max_price)
return queryset
def get_serializer_class(self):
"""✅ Different serializers for different actions"""
if self.action == 'list':
return ProductListSerializer
return ProductDetailSerializer
@action(detail=False, methods=['get'])
@method_decorator(cache_page(60 * 5)) # ✅ Cache 5 minutes
def featured(self, request):
"""Featured products - Cached"""
products = self.get_queryset().filter(is_featured=True)[:10]
serializer = ProductListSerializer(products, many=True)
return Response(serializer.data)
@action(detail=False, methods=['get'])
def popular(self, request):
"""Popular products by order count"""
cache_key = 'popular_products'
data = cache.get(cache_key)
if data is None:
products = self.get_queryset().annotate(
order_count=Count('order_items')
).filter(order_count__gt=0)\
.order_by('-order_count')[:10]
serializer = ProductListSerializer(products, many=True)
data = serializer.data
cache.set(cache_key, data, 300) # 5 minutes
return Response(data)
@action(detail=True, methods=['get'])
def reviews(self, request, pk=None):
"""Get all reviews for a product"""
product = self.get_object()
reviews = product.reviews.select_related('user').order_by('-created_at')
serializer = ReviewLightSerializer(reviews, many=True)
return Response(serializer.data)
# ✅ Optimized Category API
class CategoryViewSetOptimized(viewsets.ReadOnlyModelViewSet):
serializer_class = CategoryDetailSerializer
lookup_field = 'slug'
def get_queryset(self):
"""✅ Annotate with product count"""
return Category.objects.annotate(
product_count=Count('products', filter=Q(products__is_active=True))
).filter(product_count__gt=0)
@action(detail=True, methods=['get'])
def products(self, request, slug=None):
"""Get all products in a category"""
category = self.get_object()
products = Product.objects.filter(
category=category,
is_active=True
).select_related('brand')\
.only('id', 'name', 'slug', 'price', 'average_rating', 'brand__name')
serializer = ProductListSerializer(products, many=True)
return Response(serializer.data)🚀 Django Bolt Integration
Django Bolt হলো একটি performance library যা API response কে 10-100x দ্রুত করে।
Installation
pip install django-bolt redis django-redisSettings Configuration
# settings.py
INSTALLED_APPS = [
# ...
'django_bolt',
]
# Redis Cache
CACHES = {
'default': {
'BACKEND': 'django_redis.cache.RedisCache',
'LOCATION': 'redis://127.0.0.1:6379/1',
'OPTIONS': {
'CLIENT_CLASS': 'django_redis.client.DefaultClient',
}
}
}
# Django Bolt Configuration
DJANGO_BOLT = {
'ENABLED': True,
'CACHE_TIMEOUT': 300,
}Bolt-Optimized Views
from django_bolt.views import BoltAPIView, BoltListAPIView
from rest_framework.permissions import IsAuthenticatedOrReadOnly
class BoltProductListView(BoltListAPIView):
"""✅ 10-100x faster than normal DRF"""
queryset = Product.objects.filter(is_active=True)\
.select_related('category', 'brand')
serializer_class = ProductListSerializer
permission_classes = [IsAuthenticatedOrReadOnly]
# Bolt-specific settings
bolt_cache_enabled = True
bolt_cache_timeout = 300 # 5 minutes
class BoltProductDetailView(BoltAPIView):
"""✅ Single product - Bolt optimized"""
permission_classes = [IsAuthenticatedOrReadOnly]
bolt_cache_enabled = True
def get(self, request, pk):
try:
product = Product.objects.select_related('category', 'brand')\
.prefetch_related('reviews__user')\
.get(pk=pk, is_active=True)
serializer = ProductDetailSerializer(product)
return Response(serializer.data)
except Product.DoesNotExist:
return Response({'error': 'Not found'}, status=404)URLs
# shop/api/urls.py
from django.urls import path, include
from rest_framework.routers import DefaultRouter
from .views import ProductViewSetOptimized, CategoryViewSetOptimized
from .bolt_views import BoltProductListView, BoltProductDetailView
router = DefaultRouter()
router.register('products', ProductViewSetOptimized, basename='product')
router.register('categories', CategoryViewSetOptimized, basename='category')
urlpatterns = [
path('', include(router.urls)),
# Bolt-optimized endpoints
path('bolt/products/', BoltProductListView.as_view(), name='bolt-products'),
path('bolt/products/<int:pk>/', BoltProductDetailView.as_view(), name='bolt-product-detail'),
]