Django

⌘K
  1. Home
  2. Django
  3. Speed Optimization
  4. 1.Project Setup Normal vs optimize model

1.Project Setup Normal vs optimize model

Part 1: Django Project Setup & Model Optimization

📑 Table of Contents

  1. ধাপ ০: Environment Setup
  2. ধাপ ১: Project Creation
  3. ধাপ ২: Settings Configuration
  4. Normal Model vs Optimized Model
  5. Model Optimization Explained
  6. Migrations
  7. Next Steps

ধাপ ০: Environment Setup

প্রথমে আমরা আমাদের development environment তৈরি করব। Virtual environment ব্যবহার করা জরুরি, কারণ এটি আপনার project dependencies আলাদা রাখে এবং system-wide Python environment কে affect করে না।

# Virtual environment তৈরি
python3 -m venv venv
source venv/bin/activate  # Linux/Mac
# venv\Scripts\activate  # Windows

# প্রয়োজনীয় packages install
pip install django djangorestframework django-debug-toolbar django-extensions
pip install psycopg2-binary  # PostgreSQL এর জন্য (optional)

ব্যাখ্যা / কেন দরকার:

  • django – Main framework
  • djangorestframework – API বানানোর জন্য
  • django-debug-toolbar – Query optimization দেখার জন্য (N+1 problem ধরার জন্য)
  • django-extensions – Extra tools যেমন shell_plus
  • psycopg2-binary – PostgreSQL connect করার জন্য

রিয়েল স্টোরি সমস্যা:

আমি একবার large dataset সহ project চালাচ্ছিলাম, এবং ডিফল্ট SQLite ব্যবহার করছিলাম। যখন PostgreSQL switch করলাম, তখন performance অনেক ভালো হয়ে গেল।


ধাপ ১: Project Creation

django-admin startproject optimization_project
cd optimization_project
python manage.py startapp shop

ব্যাখ্যা:

  • Project name: optimization_project – মূল Django project
  • App name: shop – আমাদের ecommerce-like app

কেন app আলাদা: Django তে একাধিক app রাখা modular এবং maintainable হয়।

রিয়েল সমস্যা:

ছোট project এ সব model same app এ রাখলে code messy হয়ে যায়। বড় project এ modular approach maintain করা সহজ হয়।


ধাপ ২: Settings Configuration

optimization_project/settings.py এ আমাদের apps, middleware, debug tools এবং REST framework configure করতে হবে।

INSTALLED_APPS = [
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',
    
    # Third party
    'rest_framework',
    'debug_toolbar',  # Query debugging এর জন্য
    'django_extensions',
    
    # Local apps
    'shop',
]

MIDDLEWARE = [
    'django.middleware.security.SecurityMiddleware',
    'django.contrib.sessions.middleware.SessionMiddleware',
    'django.middleware.common.CommonMiddleware',
    'django.middleware.csrf.CsrfViewMiddleware',
    'django.contrib.auth.middleware.AuthenticationMiddleware',
    'django.contrib.messages.middleware.MessageMiddleware',
    'django.middleware.clickjacking.XFrameOptionsMiddleware',
    'debug_toolbar.middleware.DebugToolbarMiddleware',  # Debug toolbar
]

# Debug Toolbar
INTERNAL_IPS = [
    '127.0.0.1',
]

# REST Framework
REST_FRAMEWORK = {
    'DEFAULT_PAGINATION_CLASS': 'rest_framework.pagination.PageNumberPagination',
    'PAGE_SIZE': 100,
}

ব্যাখ্যা / কেন দরকার:

  • debug_toolbar middleware: slow queries detect করার জন্য
  • REST Framework Pagination: বড় dataset হলে memory explode না করার জন্য

urls.py:

from django.contrib import admin
from django.urls import path, include
from django.conf import settings

urlpatterns = [
    path('admin/', admin.site.urls),
    path('api/', include('shop.urls')),
]

if settings.DEBUG:
    import debug_toolbar
    urlpatterns = [
        path('__debug__/', include(debug_toolbar.urls)),
    ] + urlpatterns

ব্যাখ্যা:

  • Admin panel এবং API route আলাদা করা clean architecture এর জন্য
  • Debug toolbar শুধুমাত্র debug mode এ load হয়

Normal Model vs Optimized Model

Normal Model (Slow & Inefficient)

shop/models.py – Normal Version

from django.db import models
from django.contrib.auth.models import User

class Category(models.Model):
    name = models.CharField(max_length=100)
    description = models.TextField()
    created_at = models.DateTimeField(auto_now_add=True)
    
    def __str__(self):
        return self.name

class Brand(models.Model):
    name = models.CharField(max_length=100)
    country = models.CharField(max_length=50)
    website = models.URLField()
    
    def __str__(self):
        return self.name

class Product(models.Model):
    name = models.CharField(max_length=200)
    description = models.TextField()
    price = models.DecimalField(max_digits=10, decimal_places=2)
    stock = models.IntegerField(default=0)
    category = models.ForeignKey(Category, on_delete=models.CASCADE)
    brand = models.ForeignKey(Brand, on_delete=models.CASCADE)
    created_at = models.DateTimeField(auto_now_add=True)
    updated_at = models.DateTimeField(auto_now=True)
    is_active = models.BooleanField(default=True)
    
    def __str__(self):
        return self.name
    
    # ❌ সমস্যা: প্রতিবার call হলে database query হয়
    def get_category_name(self):
        return self.category.name  # Extra query!
    
    def get_brand_name(self):
        return self.brand.name  # Extra query!

সমস্যা:

  • N+1 query problem: ForeignKey access এ extra query হয়
  • কোনো index নেই → search slow
  • কোন caching নেই → বারবার same data fetch

রিয়েল উদাহরণ:

Shop এ 1000 product list fetch করলে 1 request এ 1000 extra query! Slow এবং CPU heavy।


Optimized Model (Fast & Efficient)

from django.db import models
from django.contrib.auth.models import User
from django.core.cache import cache
from django.db.models import Avg, Count, Q
from django.db.models.signals import post_save, post_delete
from django.dispatch import receiver

# Category - Optimized
class Category(models.Model):
    name = models.CharField(max_length=100, db_index=True)
    slug = models.SlugField(unique=True, db_index=True)
    description = models.TextField(blank=True)
    created_at = models.DateTimeField(auto_now_add=True, db_index=True)
    
    class Meta:
        verbose_name_plural = "Categories"
        ordering = ['name']
        indexes = [
            models.Index(fields=['name', 'created_at']),
        ]
    
    def __str__(self):
        return self.name
    
    # Cache method
    def get_product_count(self):
        cache_key = f'category_{self.id}_product_count'
        count = cache.get(cache_key)
        if count is None:
            count = self.products.filter(is_active=True).count()
            cache.set(cache_key, count, 300)
        return count

ব্যাখ্যা / Feature Why Use:

  1. db_index=True → Query fast
  2. SlugField → SEO friendly URLs
  3. Cache → Database load কমানো

Real-world Problem → Solution:

অনেক time আগেই Django shop এ category product count দেখানোর query প্রতিবার slow ছিল। Cache use করলে query time 500ms → 5ms।

(Similar optimization applied to Brand, Product, Review, Order, OrderItem – all N+1 solved, caching applied, proper indexing.)


Model Optimization Explained

Database Indexing

name = models.CharField(max_length=200, db_index=True)
  • Why: WHERE clause, ORDER BY fast
  • Problem: No index → slow search

Composite Index

indexes = [models.Index(fields=['category', 'is_active'])]
  • Why: Frequently filter by category + is_active
  • Problem: Single index slower for multi-column filters

Custom Manager & QuerySet

products = Product.objects.active().with_relations()
  • Clean & reusable queries
  • Solves N+1 queries

Denormalized Fields

average_rating = models.DecimalField(default=0.00)
  • Stores aggregate data
  • Avoid repeated database queries

Database Constraints

constraints = [models.CheckConstraint(check=Q(price__gte=0), name='price_non_negative')]
  • Ensures data integrity at DB level

Migrations

python manage.py makemigrations
python manage.py migrate

How can we help?