Django Fundamentals - Architecture, ORM & Core Concepts

Master Django's MTV architecture, ORM, signals, middleware, migrations, and essential concepts for building robust web applications.

πŸ—οΈ Django Architecture

  • 1. Describe Django's architecture (MTV)
    Model: Handles data and database structure
    Template: Manages UI/presentation logic
    View: Contains business logic and interacts with both Model and Template
  • 2. Explain Django's Request–Response Cycle
    1. Request enters Django through WSGI or ASGI
    2. It passes through middleware
    3. URLconf maps the request to the correct view
    4. The view processes the request, interacts with models if necessary
    5. Data is rendered via a template
    6. Response passes back through middleware and is returned to the client
  • 3. What is Middleware in Django?
    Middleware is a lightweight framework-level hook that processes requests and responses globally before or after the view is called.
    Examples: AuthenticationMiddleware, SessionMiddleware, CsrfViewMiddleware

    Execution Order:
    β€’ Request: Top β†’ Bottom
    β€’ Response: Bottom β†’ Top
    class LogMiddleware:
        def __init__(self, get_response):
            self.get_response = get_response
        
        def __call__(self, request):
            print("Request received")
            response = self.get_response(request)
            return response

πŸ“‘ Django Signals

  • 4. What are Django Signals?
    Signals let different parts of a Django app communicate by sending notifications when specific events (like saving or deleting a model) occur.
    Common Signals:pre_savepost_savepre_deletepost_deletem2m_changed
    from django.db.models.signals import post_save
    from django.dispatch import receiver
    
    @receiver(post_save, sender=User)
    def create_profile(sender, instance, created, **kwargs):
        if created:
            Profile.objects.create(user=instance)
  • 5. What is m2m_changed Signal Used For?
    Triggered when a ManyToManyField changes (add, remove, clear operations).

πŸ› οΈ Management Commands

  • 6. What are Django's Manage.py Commands?
    Utility commands for project management:
    runservermakemigrationsmigratecreatesuperusershelldbshelltestdumpdataloaddatacollectstatic

πŸ—„οΈ Django ORM - Query Optimization

  • 7. What is select_related() in Django ORM?
    Performs SQL JOINs to fetch related ForeignKey data in a single query β€” used for one-to-one and many-to-one relations.
    # Without select_related - N+1 queries
    books = Book.objects.all()
    for book in books:
        print(book.author.name)  # Hits DB each time
    
    # With select_related - Single query
    books = Book.objects.select_related('author')
    for book in books:
        print(book.author.name)  # No extra DB hits
  • 8. What is prefetch_related()?
    Performs separate lookups and joins them in Python β€” ideal for many-to-many and reverse foreign key relationships.
    # For many-to-many or reverse FK
    books = Book.objects.prefetch_related('reviews')
    for book in books:
        for review in book.reviews.all():  # No extra queries
            print(review.content)
  • 9. What is the Difference Between annotate() and aggregate()?
    annotate(): Adds calculated fields to each object in a QuerySet
    aggregate(): Returns a summary (single dict) for the entire QuerySet
    from django.db.models import Count, Sum
    
    # annotate - per object
    books = Book.objects.annotate(review_count=Count('reviews'))
    for book in books:
        print(f"{book.title}: {book.review_count} reviews")
    
    # aggregate - overall summary
    total = Order.objects.aggregate(total=Sum('price'))
    print(total)  # {'total': 15000}
  • 10. What is Lazy Evaluation in Django ORM?
    QuerySets are not executed immediately; queries are built and only executed when data is accessed β€” enabling query chaining and optimization.
    # No DB hit yet
    users = User.objects.filter(is_active=True)
    users = users.filter(age__gt=18)
    users = users.order_by('-date_joined')
    
    # DB hit happens here
    for user in users:  # Query executed now
        print(user.name)
  • 11. What are QuerySets?
    A QuerySet represents a collection of database records. They are lazy, chainable, and filterable via ORM methods.
  • 12. What are only() and defer() Used For?
    Used to optimize queries by loading only specific fields or deferring large fields.
    # only() - Load only specified fields
    User.objects.only('id', 'name')
    
    # defer() - Exclude specified fields
    User.objects.defer('large_text_field')

πŸ” Advanced Queries

  • 13. What is a Q Object?
    Used for complex queries involving OR/AND conditions.
    from django.db.models import Q
    
    # OR condition
    users = User.objects.filter(
        Q(is_staff=True) | Q(is_superuser=True)
    )
    
    # Complex conditions
    users = User.objects.filter(
        Q(age__gte=18) & (Q(city='NYC') | Q(city='LA'))
    )

πŸ”„ Migrations & Transactions

  • 14. What are Django Migrations?
    Migrations propagate model changes to the database schema.
    β€’ makemigrations – create migration files
    β€’ migrate – apply migrations to database
    # Create migrations
    python manage.py makemigrations
    
    # Apply migrations
    python manage.py migrate
    
    # Show migration status
    python manage.py showmigrations
  • 15. How to Handle Database Transactions in Django?
    Use atomic() for safe, all-or-nothing database operations.
    from django.db import transaction
    
    # As context manager
    with transaction.atomic():
        user = User.objects.create(name='John')
        profile = Profile.objects.create(user=user)
        # If any fails, all rollback
    
    # As decorator
    @transaction.atomic
    def create_order(user, items):
        order = Order.objects.create(user=user)
        for item in items:
            OrderItem.objects.create(order=order, item=item)
  • 16. What is get_or_create() vs update_or_create()?
    get_or_create(): Fetches or creates a new object if not found
    update_or_create(): Updates existing or creates new if not found
    # get_or_create
    user, created = User.objects.get_or_create(
        email='john@example.com',
        defaults={'name': 'John'}
    )
    
    # update_or_create
    user, created = User.objects.update_or_create(
        email='john@example.com',
        defaults={'name': 'John Doe', 'age': 30}
    )

⚑ Query Optimization Tips

  • 17. How to Optimize Django ORM Queries?
    • Use select_related(), prefetch_related()
    • Use only() / defer() for specific fields
    • Avoid N+1 queries
    • Add database indexes on frequently queried fields
    • Use values() and values_list() for lighter queries
    • Use caching where appropriate
    • Use iterator() for large QuerySets
    • Use bulk_create() and bulk_update()
  • 18. What is the Purpose of runserver 0.0.0.0:8000?
    Makes the app accessible from any IP in the network on port 8000 β€” not just localhost.

πŸ’Ύ Caching

  • 19. What is Caching in Django?
    Improves performance by storing frequently accessed data in memory or cache backends.
    Levels:
    β€’ Page caching (cache_page)
    β€’ Template fragment caching
    β€’ Low-level caching (cache.set(), cache.get())

    Backends: Memory, Redis, Memcached, Database
    from django.core.cache import cache
    
    # Set cache
    cache.set('my_key', 'my_value', 300)  # 5 minutes
    
    # Get cache
    value = cache.get('my_key')
    
    # Page caching
    from django.views.decorators.cache import cache_page
    
    @cache_page(60 * 15)  # 15 minutes
    def my_view(request):
        return HttpResponse("Cached for 15 mins")

πŸ“ Django Admin

  • 22. Explain Django's Admin Interface
    An auto-generated, customizable UI for managing site data and users. Makes CRUD operations easy without writing custom views.
    from django.contrib import admin
    from .models import Book
    
    @admin.register(Book)
    class BookAdmin(admin.ModelAdmin):
        list_display = ('title', 'author', 'published_date')
        list_filter = ('author', 'published_date')
        search_fields = ('title', 'author__name')
        ordering = ('-published_date',)