Python

1. What is Django?

Django is a high-level Python web framework that follows the MVT (Model–View–Template) architectural pattern. It helps developers build secure, scalable, and maintainable web applications quickly by providing built-in solutions for authentication, database ORM, routing, admin panel, forms, security, and caching.

Django philosophy:

  • DRY — Don't Repeat Yourself
  • Convention over Configuration
  • Batteries Included — Django provides everything out of the box

2. Django Project Structure

myproject/
            │── manage.py
            │── myproject/
            │     ├── __init__.py
            │     ├── settings.py
            │     ├── urls.py
            │     ├── wsgi.py
            │     └── asgi.py
            └── app1/
                ├── models.py
                ├── views.py
                ├── urls.py
                ├── admin.py
                ├── apps.py
                ├── tests.py
                └── templates/
            

manage.py: Command utility for running the project.
settings.py: Contains project configuration (DB, static files, middleware, installed apps).
urls.py: Maps URLs to views.
wsgi.py/asgi.py: Deployment entry points.


3. MVT Architecture (Core Concept)

Django uses MVT instead of MVC.

3.1 Model

Model represents database tables and handles business logic + ORM queries.

class Student(models.Model):
                name = models.CharField(max_length=100)
                age = models.IntegerField()
                enrolled = models.BooleanField(default=True)
            

3.2 View

View takes request, processes logic, returns a response (HTML/JSON).

def home(request):
                students = Student.objects.all()
                return render(request, "home.html", {"students": students})
            

3.3 Template

Template controls UI (HTML + Django Template Language).

<h1>Students List</h1>
            {% for s in students %}
                <p>{{ s.name }} - {{ s.age }}</p>
            {% endfor %}
            

4. URL Routing System (urls.py)

Django maps URLs to views using path() and re_path().

from django.urls import path
            from . import views

            urlpatterns = [
                path("", views.home),
                path("student/<int:id>/", views.student_detail),
            ]
            

Types of URL converters:

  • int
  • str
  • slug
  • uuid
  • path

5. Django Models (Deep Explanation)

Models define database structure using Django ORM. Think of ORM (Object Relational Mapping)as Python → SQL translator.

5.1 Field Types

  • CharField
  • TextField
  • EmailField
  • IntegerField
  • BooleanField
  • DateTimeField(auto_now_add=True)
  • ForeignKey (One-to-Many)
  • OneToOneField
  • ManyToManyField

5.2 ORM Queries


            Student.objects.create(name="Ali", age=22)

            Student.objects.filter(age__gt=18)

            Student.objects.get(id=1)

            Student.objects.all().order_by("-age")

            Student.objects.exclude(enrolled=False)
            

5.3 Migrations


            python manage.py makemigrations
            python manage.py migrate
            

6. Views in Django (Function & Class-Based Views)

6.1 Function-Based Views (FBV)


            def home(request):
                return HttpResponse("Hello World")
            

6.2 Class-Based Views (CBV)


            from django.views import View

            class HomeView(View):
                def get(self, request):
                    return HttpResponse("Hello from CBV")
            

6.3 Built-in CBVs

  • ListView
  • DetailView
  • CreateView
  • UpdateView
  • DeleteView

7. Templates & Django Template Language (DTL)

Django’s templating engine supports:

  • Variables → {{ name }}
  • Filters → {{ name|upper }}
  • Loops → {% for %}
  • Conditions → {% if %}

            {% for item in items %}
                <p>{{ item.name|upper }}</p>
            {% endfor %}
            

8. Forms in Django (Very Important for Interviews)

Django provides two types of forms:

  • Forms – manually created
  • ModelForms – automatically generated from models

8.1 Django Form Example


            class ContactForm(forms.Form):
                name = forms.CharField(max_length=100)
                email = forms.EmailField()
            

8.2 ModelForm Example


            class StudentForm(forms.ModelForm):
                class Meta:
                    model = Student
                    fields = "__all__"
            

9. Django Admin (Auto-generated CMS)

Django provides a ready-made admin panel.


            from .models import Student

            admin.site.register(Student)
            

Customizing Admin


            class StudentAdmin(admin.ModelAdmin):
                list_display = ("name", "age")
                search_fields = ("name",)
            

10. Middleware (Deep Explanation)

Middleware is a lightweight plugin system for processing requests and responses.

Middleware runs in order:

  1. Request → Middleware 1 → Middleware 2 → View
  2. Response → Middleware 2 → Middleware 1 → Client

Custom Middleware


            class SimpleMiddleware:
                def __init__(self, get_response):
                    self.get_response = get_response

                def __call__(self, request):
                    print("Before view")
                    response = self.get_response(request)
                    print("After view")
                    return response
            

11. Authentication System

11.1 Built-in functionalities

  • User login/logout
  • Password hashing
  • Permissions
  • Groups and roles

Login Example


            from django.contrib.auth import authenticate, login

            user = authenticate(username="talha", password="123")
            if user:
                login(request, user)
            

12. Static Files & Media Files

Django separates user-uploaded files from static assets.


            STATIC_URL = "/static/"
            MEDIA_URL = "/media/"
            MEDIA_ROOT = BASE_DIR / "media"
            

13. Django Signals (Observer Pattern)

Django Signals implement the Observer Pattern, allowing certain pieces of code (listeners/receivers) to get executed automatically when specific actions occur in the system.

They help different parts of your application communicate without tightly coupling their logic. For example, when a user registers, you may want to automatically:

  • Create a profile
  • Send a welcome email
  • Update analytics or logs

Using signals, you can perform all these actions without modifying the original user creation code.


Why Use Signals?

  • Decouple business logic
  • Trigger automated background tasks on DB events
  • Maintain cleaner models and views
  • Perfect for audit logs, notifications, profile creation, etc.

Common Built-in Django Signals:

  • pre_save – runs before a model is saved
  • post_save – runs after a model is saved
  • pre_delete – before a model instance is deleted
  • post_delete – after a model instance is deleted
  • m2m_changed – runs when a ManyToManyField is modified
  • pre_init / post_init – before/after __init__ of a model
  • request_started / request_finished
  • user_logged_in, user_logged_out

Basic Example: post_save Signal

This signal runs every time an object of a model is created or updated.


            # signals.py
            from django.db.models.signals import post_save
            from django.dispatch import receiver
            from .models import Student

            @receiver(post_save, sender=Student)
            def after_student_save(sender, instance, created, **kwargs):
                if created:
                    print("New student created:", instance)
                else:
                    print("Student updated:", instance)
            

Note: Always import signals inside apps.py to ensure Django loads them.


            # apps.py
            from django.apps import AppConfig

            class MyAppConfig(AppConfig):
                name = "myapp"

                def ready(self):
                    import myapp.signals
            

pre_save Example: Modify Data Before Saving


            from django.db.models.signals import pre_save
            from django.dispatch import receiver
            from .models import Student

            @receiver(pre_save, sender=Student)
            def capitalize_name(sender, instance, **kwargs):
                instance.name = instance.name.title()
            

This ensures every saved name is capitalized automatically.


post_delete Example: Clean Up Files


            from django.db.models.signals import post_delete
            from django.dispatch import receiver
            from .models import Document

            @receiver(post_delete, sender=Document)
            def delete_file(sender, instance, **kwargs):
                if instance.file:
                    instance.file.delete(save=False)
            

m2m_changed Example

Runs when M2M relationships change (added, removed, cleared).


            from django.db.models.signals import m2m_changed
            from django.dispatch import receiver
            from .models import Course

            @receiver(m2m_changed, sender=Course.students.through)
            def students_changed(sender, instance, action, **kwargs):
                print("Students changed:", action)
            

How Signals Work Internally

Signals use the publish-subscribe mechanism:

  1. A sender (e.g., model) publishes an event: post_save.send()
  2. All connected receivers are notified
  3. Receivers execute code based on parameters (instance, created, etc.)

Built-in Parameters in Signals

  • sender — the model class
  • instance — actual object saved/deleted
  • created — true only during object creation
  • raw — true for fixture loading
  • kwargs — extra metadata

Best Practices (VERY IMPORTANT FOR INTERVIEWS)

  • Use signals only for decoupled background tasks
  • Never put business-critical logic in signals (hard to debug)
  • Keep signals in signals.py
  • Load signals inside apps.py → ready()
  • Avoid circular imports by keeping logic separate
  • Prefer Celery tasks for heavy operations

When to Use Signals?

  • Create profile after user signup
  • Send notification emails
  • Log database changes
  • Auto-update inventory or analytics

When NOT to Use Signals?

  • When the logic is core business logic
  • When maintainability matters (signals can hide logic)
  • When debugging complex flows

Django Project Folder Structure

            project_root/
            │
            ├── app_name/
            │   ├── migrations/
            │   │   └── __init__.py
            │   ├── __init__.py
            │   ├── admin.py
            │   ├── apps.py
            │   ├── models.py => models go here
            │   ├── forms.py => forms go here
            │   ├── signals.py => signals go here
            │   ├── views.py
            │   ├── urls.py
            │   └── templates/
            │       └── app_name/
            │           └── index.html
            │
            ├── media/
            │   └── documents/
            │       └── uploaded_file.pdf
            │
            ├── project_name/
            │   ├── __init__.py
            │   ├── settings.py
            │   ├── urls.py
            │   └── wsgi.py
            │
            ├── manage.py
            └── requirements.txt
            

14. Django Request-Response Lifecycle

Very important for interviews!

  1. User sends request
  2. WSGI/ASGI server receives it
  3. Middleware processing
  4. URL resolver matches the path
  5. View executes business logic
  6. Template rendered (if any)
  7. Response passes back through middleware
  8. Returned to user

15. Django Security Features (Very Detailed)

Django provides built-in security mechanisms to protect web applications from common vulnerabilities including CSRF, XSS, SQL Injection, Clickjacking, insecure passwords, open redirects, host header attacks, and unsafe file uploads. Below is a detailed explanation with examples.

1. CSRF Protection (Cross-Site Request Forgery)

CSRF attacks trick logged-in users into submitting unwanted requests. Django generates a unique CSRF token for each session and validates it on POST requests.

Usage in Forms

<form method="POST">
                {% csrf_token %}
                <input type="text" name="title">
                <button type="submit">Submit</button>
            </form>

CSRF Exempt (Not Recommended)

from django.views.decorators.csrf import csrf_exempt

            @csrf_exempt
            def webhook(request):
                return HttpResponse("OK")

CSRF Token for AJAX Requests

fetch("/save/", {
                method: "POST",
                headers: { "X-CSRFToken": "{{ csrf_token }}" },
                body: JSON.stringify({ data: "test" })
            })

2. XSS Protection (Cross-Site Scripting)

XSS occurs when attackers inject malicious JavaScript into web pages. Django escapes HTML automatically in templates.

Safe Rendering

<p>{{ username }}</p>

Unsafe Rendering (Only for Trusted Content)

{% autoescape off %}
                {{ html_content }}
            {% endautoescape %}

How to Protect

  • Always escape user input in templates.
  • Validate input using Django forms or custom validators.
  • Use HTML sanitizers like bleach for rich text inputs.
  • Enable Content Security Policy (CSP) headers to restrict script execution.

3. SQL Injection Protection

Django ORM automatically escapes query parameters, preventing SQL injection.

Safe ORM Query

User.objects.filter(username=request.GET['name'])

Unsafe Raw SQL (Do Not Use)

query = f"SELECT * FROM users WHERE username = '{name}';"

Safe Parameterized Raw SQL

from django.db import connection

            cursor = connection.cursor()
            cursor.execute("SELECT * FROM users WHERE username = %s", [name])

4. Clickjacking Protection

Clickjacking tricks users into clicking hidden elements. Django sets X-Frame-Options headers to prevent this.

Deny All Embedding

X_FRAME_OPTIONS = "DENY"

Allow Only Same Domain

X_FRAME_OPTIONS = "SAMEORIGIN"

Decorator Example

from django.views.decorators.clickjacking import xframe_options_sameorigin

            @xframe_options_sameorigin
            def my_view(request):
                return HttpResponse("OK")

5. Secure Session Management

Django stores sessions on the server side for better security.

Secure Session Cookies

SESSION_COOKIE_SECURE = True
            SESSION_COOKIE_HTTPONLY = True
            SESSION_COOKIE_SAMESITE = "Strict"

Signed Cookies

SESSION_ENGINE = "django.contrib.sessions.backends.signed_cookies"

6. Password Hashing & Authentication Security

Django hashes passwords using strong algorithms like PBKDF2, Argon2, and BCrypt.

Create Hashed Password

from django.contrib.auth.hashers import make_password
            hashed = make_password("mypassword123")

Verify Password

from django.contrib.auth.hashers import check_password
            check_password("mypassword123", hashed)

Set Preferred Hashing Algorithms

PASSWORD_HASHERS = [
                "django.contrib.auth.hashers.Argon2PasswordHasher",
                "django.contrib.auth.hashers.PBKDF2PasswordHasher",
            ]

7. HTTPS & SSL Security

Always serve your site over HTTPS and enforce HSTS.

Enable HSTS & Redirect HTTP to HTTPS

SECURE_HSTS_SECONDS = 31536000
            SECURE_HSTS_INCLUDE_SUBDOMAINS = True
            SECURE_SSL_REDIRECT = True

8. Open Redirect Protection

Django prevents unsafe redirects to external sites.

Safe Redirect Example

from django.shortcuts import redirect

            def login_success(request):
                return redirect("dashboard")

9. Host Header Injection Protection

Restrict allowed domains to avoid host header attacks.

ALLOWED_HOSTS = ["yourdomain.com", "127.0.0.1"]

10. File Upload Security

Always validate file type and store uploads securely.

Validate File Type

def validate_file(file):
                if not file.name.endswith(".pdf"):
                    raise ValidationError("Only PDFs allowed!")

Store Files Outside Document Root

Never allow direct execution of uploaded files; store them in a safe directory.


Summary Table of Django Security Features

Feature Protection Example / Code
CSRF Prevents forged POST requests {% csrf_token %}
XSS Auto HTML escaping {{ user_input }}
SQL Injection ORM sanitizes inputs .filter()
Clickjacking X-Frame-Options headers X_FRAME_OPTIONS = "DENY"
Password Hashing Strong hashing algorithms make_password()
Session Security Secure cookies SESSION_COOKIE_SECURE = True
HTTPS & SSL Encrypt communication SECURE_SSL_REDIRECT = True
Open Redirect Prevents unsafe redirects redirect("dashboard")
Host Header Restrict allowed hosts ALLOWED_HOSTS
File Upload Validate & store safely validate_file()

16. Deployment (WSGI / ASGI)

  • WSGI → For synchronous Django (classic behavior)
  • ASGI → Async support (WebSockets, long polling)
Examples:
  • Gunicorn + Nginx → WSGI
  • Daphne/Uvicorn → ASGI

17. Caching in Django

Caching improves performance by storing frequently accessed data or computation results so that future requests can be served faster without recalculating or querying the database repeatedly. Django provides multiple caching strategies suitable for different use cases.

Why Use Caching?

  • Reduces database queries and expensive computations.
  • Speeds up response time for views and templates.
  • Reduces server load during high traffic.

Types of Caches in Django

  • In-memory cache: Fastest cache stored in server memory. Example: LocMemCache.
  • File-based cache: Stores cached data in files on disk. Suitable for small-scale or single-server apps.
  • Database cache: Stores cache in a database table. Useful for persistent cache across servers.
  • Memcached: Distributed in-memory cache, very fast, used in production for multiple servers.
  • Redis: Advanced in-memory cache with persistence, pub/sub, and data structures.

1. Setting Up Cache in settings.py

Example using in-memory cache:

CACHES = {
                'default': {
                    'BACKEND': 'django.core.cache.backends.locmem.LocMemCache',
                    'LOCATION': 'unique-snowflake',
                }
            }

2. View-Level Caching (Cache Entire View)

Use @cache_page(timeout_in_seconds) decorator to cache a whole view:

from django.views.decorators.cache import cache_page
            from django.http import HttpResponse

            @cache_page(60)  # Cache this view for 60 seconds
            def home(request):
                return HttpResponse("Cached response")

3. Template Fragment Caching

Cache only a part of the template to reduce rendering time.

{% load cache %}
            {% cache 300 sidebar %}
                <div class="sidebar">
                    <!-- Expensive rendering logic here -->
                </div>
            {% endcache %}

4. Low-Level Cache API

Programmatically store and retrieve cached data:

from django.core.cache import cache

            # Set cache
            cache.set('my_key', 'my_value', 60)  # Expires in 60 seconds

            # Get cache
            value = cache.get('my_key')
            print(value)  # Output: my_value

            # Delete cache
            cache.delete('my_key')

5. Per-Site Cache

Cache entire site using middleware:

MIDDLEWARE = [
                'django.middleware.cache.UpdateCacheMiddleware',
                'django.middleware.common.CommonMiddleware',
                'django.middleware.cache.FetchFromCacheMiddleware',
            ]

Settings:

CACHE_MIDDLEWARE_SECONDS = 600  # Cache site for 10 minutes
            CACHE_MIDDLEWARE_KEY_PREFIX = 'mysite'

6. Cache Backends Example (Redis)

CACHES = {
                'default': {
                    'BACKEND': 'django_redis.cache.RedisCache',
                    'LOCATION': 'redis://127.0.0.1:6379/1',
                    'OPTIONS': {
                        'CLIENT_CLASS': 'django_redis.client.DefaultClient',
                    }
                }
            }

7. Best Practices

  • Cache only expensive queries or computations.
  • Use short TTL (time-to-live) for frequently changing data.
  • Combine template fragment caching with view-level caching for better performance.
  • Use distributed cache (Redis or Memcached) for multi-server setups.
  • Invalidate cache properly when underlying data changes.

            from django.views.decorators.cache import cache_page

            @cache_page(60)
            def home(request):
                return HttpResponse("Cached response")
            

18. Django Sessions

Sessions store user-specific data on the server.


            request.session["name"] = "Talha"
            value = request.session.get("name")
            

19. Environment Management (.env)


            SECRET_KEY="abc"
            DEBUG=False
            DATABASE_URL="postgres://..."
            

20. Summary (For Fast Interview Revision)

  • Django uses MVT
  • ORM is used instead of raw SQL
  • URL dispatcher maps routes
  • Views handle request/response
  • Templates handle UI
  • Forms handle validation
  • Admin provides auto CMS
  • Middleware processes global logic
  • Signals = Observer pattern
  • Django is secure by default