What is Django?

Django is a high-level Python web framework that enables rapid development of secure and maintainable websites. Created in 2005 at a newspaper company to meet fast-moving newsroom deadlines, Django has grown to power some of the world's busiest websites including Instagram, Pinterest, and Mozilla.

Think of Django as a complete kitchen with all appliances included - you don't need to buy a separate oven, microwave, or refrigerator. Everything you need to build a web application comes bundled together.

Why Django? "Batteries Included" Philosophy

Django's greatest strength is its comprehensive feature set right out of the box:

  • Admin Interface: Automatic, production-ready admin panel for managing data
  • ORM (Object-Relational Mapping): Work with databases using Python instead of SQL
  • Authentication System: User login, permissions, and security built-in
  • Form Handling: Powerful form validation and rendering
  • Template Engine: Dynamic HTML generation with inheritance
  • URL Routing: Clean, elegant URL patterns
  • Security Features: CSRF protection, XSS prevention, SQL injection protection
  • Scalability: Used by sites serving millions of users

You spend time building your unique features, not reinventing the wheel!

When to Use Django

Django is perfect when you need:

  • Rapid Development: MVP, prototypes, or startup projects with tight deadlines
  • Content Management: Blogs, news sites, e-commerce platforms
  • Data-Driven Apps: Applications with complex database interactions
  • Admin Dashboards: When you need a powerful backend interface
  • Secure Applications: Banking, healthcare, or any security-critical app
  • Scalable Systems: Apps that need to grow from thousands to millions of users

When NOT to use Django: Very simple APIs (use Flask/FastAPI), real-time apps like chat (use FastAPI with WebSockets), or microservices (Django can be too heavy).

MTV Pattern (Model-Template-View)

Django uses MTV architecture, similar to MVC (Model-View-Controller) but with different terminology:

Django MTV Architecture:

┌─────────────┐
│   Browser   │
└──────┬──────┘
       │ HTTP Request
       ▼
┌─────────────────────────────┐
│      URLs (urls.py)         │  ← Routes requests to views
└──────────┬──────────────────┘
           │
           ▼
┌─────────────────────────────┐
│     VIEW (views.py)         │  ← Business logic
│  - Process request          │
│  - Query database via Model │
│  - Return response          │
└──────┬──────────────────────┘
       │                  ▲
       ▼                  │
┌──────────────┐   ┌──────────────┐
│   MODEL      │   │   TEMPLATE   │
│ (models.py)  │   │  (.html)     │
│              │   │              │
│ - Database   │   │ - HTML       │
│ - Data       │   │ - Dynamic    │
│ - Business   │   │   content    │
│   rules      │   │              │
└──────────────┘   └──────────────┘

M = Model      → Data and database logic
T = Template   → Presentation layer (HTML)
V = View       → Business logic (what data to show)

Getting Started: Django Project Setup

# Install Django
pip install django

# Create a new project
django-admin startproject myproject
cd myproject

# Project structure
myproject/
├── manage.py              # Command-line utility
└── myproject/
    ├── __init__.py
    ├── settings.py        # Project settings
    ├── urls.py            # URL routing
    ├── asgi.py            # Async server gateway
    └── wsgi.py            # Web server gateway

# Create an app (Django projects contain multiple apps)
python manage.py startapp blog

# App structure
blog/
├── __init__.py
├── admin.py              # Admin interface config
├── apps.py               # App configuration
├── models.py             # Database models
├── views.py              # View functions/classes
├── urls.py               # App-specific URLs (create this)
├── tests.py              # Unit tests
└── migrations/           # Database migrations

# Run development server
python manage.py runserver
# Visit http://127.0.0.1:8000/

Models: Defining Your Data

Models define your database structure using Python classes. Django automatically creates database tables from these models.

# blog/models.py
from django.db import models
from django.contrib.auth.models import User

class Category(models.Model):
    name = models.CharField(max_length=100)
    slug = models.SlugField(unique=True)
    created_at = models.DateTimeField(auto_now_add=True)

    class Meta:
        verbose_name_plural = "Categories"

    def __str__(self):
        return self.name

class Post(models.Model):
    STATUS_CHOICES = [
        ('draft', 'Draft'),
        ('published', 'Published'),
    ]

    title = models.CharField(max_length=200)
    slug = models.SlugField(unique=True)
    author = models.ForeignKey(User, on_delete=models.CASCADE)
    category = models.ForeignKey(Category, on_delete=models.SET_NULL, null=True)
    content = models.TextField()
    status = models.CharField(max_length=10, choices=STATUS_CHOICES, default='draft')
    created_at = models.DateTimeField(auto_now_add=True)
    updated_at = models.DateTimeField(auto_now=True)
    views = models.IntegerField(default=0)

    class Meta:
        ordering = ['-created_at']  # Newest first

    def __str__(self):
        return self.title

# Create migrations (blueprint for database changes)
python manage.py makemigrations

# Apply migrations (create actual tables)
python manage.py migrate

Django ORM: Querying the Database

The ORM lets you interact with databases using Python instead of writing SQL. It's like having a translator between Python and your database.

# CREATE - Adding data
post = Post.objects.create(
    title="My First Post",
    slug="my-first-post",
    author=user,
    content="Hello World!",
    status='published'
)

# Or create and save separately
post = Post(title="Second Post", slug="second-post")
post.author = user
post.save()

# READ - Retrieving data
# Get all posts
all_posts = Post.objects.all()

# Get published posts only
published = Post.objects.filter(status='published')

# Get single post (raises error if not found)
post = Post.objects.get(slug='my-first-post')

# Get or return None if not found
post = Post.objects.filter(slug='my-first-post').first()

# Exclude certain posts
drafts = Post.objects.exclude(status='published')

# Chaining filters
recent_posts = Post.objects.filter(
    status='published'
).filter(
    created_at__gte='2024-01-01'
).order_by('-views')[:10]

# UPDATE - Modifying data
post = Post.objects.get(id=1)
post.views += 1
post.save()

# Bulk update
Post.objects.filter(status='draft').update(status='published')

# DELETE - Removing data
post = Post.objects.get(id=1)
post.delete()

# Bulk delete
Post.objects.filter(status='draft').delete()

# ADVANCED QUERIES
# Count
post_count = Post.objects.filter(status='published').count()

# Aggregation
from django.db.models import Count, Avg, Max
stats = Post.objects.aggregate(
    total=Count('id'),
    avg_views=Avg('views'),
    max_views=Max('views')
)

# Related data (following foreign keys)
post = Post.objects.get(id=1)
author_name = post.author.username
category_name = post.category.name

# Reverse relationships
user = User.objects.get(username='alice')
user_posts = user.post_set.all()  # All posts by this user

Views: Handling Requests

Views are Python functions or classes that receive web requests and return web responses.

# blog/views.py
from django.shortcuts import render, get_object_or_404, redirect
from django.http import HttpResponse
from .models import Post, Category

# Function-based view
def post_list(request):
    posts = Post.objects.filter(status='published')
    return render(request, 'blog/post_list.html', {'posts': posts})

def post_detail(request, slug):
    post = get_object_or_404(Post, slug=slug, status='published')
    # Increment view count
    post.views += 1
    post.save()
    return render(request, 'blog/post_detail.html', {'post': post})

# Class-based view (more powerful)
from django.views.generic import ListView, DetailView

class PostListView(ListView):
    model = Post
    template_name = 'blog/post_list.html'
    context_object_name = 'posts'
    paginate_by = 10

    def get_queryset(self):
        return Post.objects.filter(status='published')

class PostDetailView(DetailView):
    model = Post
    template_name = 'blog/post_detail.html'
    context_object_name = 'post'

    def get_object(self):
        post = super().get_object()
        post.views += 1
        post.save()
        return post

URL Routing: Connecting URLs to Views

# blog/urls.py
from django.urls import path
from . import views

app_name = 'blog'

urlpatterns = [
    path('', views.PostListView.as_view(), name='post_list'),
    path('post//', views.PostDetailView.as_view(), name='post_detail'),
    path('category//', views.category_posts, name='category_posts'),
]

# myproject/urls.py (main URL config)
from django.contrib import admin
from django.urls import path, include

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

# URL patterns support:
# -       → integers
# -    → slugs (letters, numbers, hyphens, underscores)
# -     → strings
# -      → UUIDs
# -    → full paths with slashes

Templates: Dynamic HTML

Templates combine HTML with Django Template Language (DTL) to create dynamic pages.

<!-- templates/blog/post_list.html -->
{% extends 'base.html' %}

{% block title %}Blog Posts{% endblock %}

{% block content %}
    <h1>Latest Blog Posts</h1>

    {% for post in posts %}
        <article>
            <h2>
                <a href="{% url 'blog:post_detail' post.slug %}">
                    {{ post.title }}
                </a>
            </h2>
            <p class="meta">
                By {{ post.author.username }} on {{ post.created_at|date:"F d, Y" }}
            </p>
            <p>{{ post.content|truncatewords:30 }}</p>
        </article>
    {% empty %}
        <p>No posts available.</p>
    {% endfor %}

    <!-- Pagination -->
    {% if is_paginated %}
        <div class="pagination">
            {% if page_obj.has_previous %}
                <a href="?page=1">First</a>
                <a href="?page={{ page_obj.previous_page_number }}">Previous</a>
            {% endif %}

            Page {{ page_obj.number }} of {{ page_obj.paginator.num_pages }}

            {% if page_obj.has_next %}
                <a href="?page={{ page_obj.next_page_number }}">Next</a>
                <a href="?page={{ page_obj.paginator.num_pages }}">Last</a>
            {% endif %}
        </div>
    {% endif %}
{% endblock %}

<!-- templates/base.html -->
<!DOCTYPE html>
<html>
<head>
    <title>{% block title %}My Site{% endblock %}</title>
</head>
<body>
    <nav>
        <a href="{% url 'blog:post_list' %}">Home</a>
    </nav>

    {% block content %}{% endblock %}

    <footer>
        <p>&copy; 2024 My Blog</p>
    </footer>
</body>
</html>

Django Admin: Automatic Admin Interface

One of Django's killer features - a production-ready admin interface with zero code!

# blog/admin.py
from django.contrib import admin
from .models import Post, Category

# Simple registration
admin.site.register(Category)

# Customized admin
@admin.register(Post)
class PostAdmin(admin.ModelAdmin):
    list_display = ['title', 'author', 'status', 'created_at', 'views']
    list_filter = ['status', 'created_at', 'category']
    search_fields = ['title', 'content']
    prepopulated_fields = {'slug': ('title',)}
    date_hierarchy = 'created_at'
    ordering = ['-created_at']

    # Customize form layout
    fieldsets = (
        ('Basic Info', {
            'fields': ('title', 'slug', 'author', 'category')
        }),
        ('Content', {
            'fields': ('content', 'status')
        }),
        ('Metadata', {
            'fields': ('views',),
            'classes': ('collapse',)
        }),
    )

# Create superuser
python manage.py createsuperuser
# Visit http://127.0.0.1:8000/admin/

Forms: User Input Handling

# blog/forms.py
from django import forms
from .models import Post

class PostForm(forms.ModelForm):
    class Meta:
        model = Post
        fields = ['title', 'slug', 'category', 'content', 'status']
        widgets = {
            'content': forms.Textarea(attrs={'rows': 10}),
        }

    def clean_title(self):
        title = self.cleaned_data['title']
        if len(title) < 10:
            raise forms.ValidationError("Title must be at least 10 characters")
        return title

# blog/views.py
from .forms import PostForm

def create_post(request):
    if request.method == 'POST':
        form = PostForm(request.POST)
        if form.is_valid():
            post = form.save(commit=False)
            post.author = request.user
            post.save()
            return redirect('blog:post_detail', slug=post.slug)
    else:
        form = PostForm()
    return render(request, 'blog/post_form.html', {'form': form})

Authentication: User Management

# Built-in authentication views
from django.contrib.auth import views as auth_views

urlpatterns = [
    path('login/', auth_views.LoginView.as_view(), name='login'),
    path('logout/', auth_views.LogoutView.as_view(), name='logout'),
]

# Protecting views with login required
from django.contrib.auth.decorators import login_required

@login_required
def create_post(request):
    # Only logged-in users can access this
    pass

# Check permissions in templates
{% if user.is_authenticated %}
    <a href="{% url 'create_post' %}">Create Post</a>
{% else %}
    <a href="{% url 'login' %}">Login</a>
{% endif %}

Best Practices

  • Use environment variables: Never hardcode secrets in settings.py
  • Create multiple apps: Separate concerns (blog, users, api, etc.)
  • Use Class-Based Views: For complex views with reusable logic
  • Optimize queries: Use select_related() and prefetch_related()
  • Use Django's forms: Built-in validation and security
  • Write tests: Django has excellent testing tools
  • Keep migrations in version control: Track database changes
  • Use Django signals sparingly: Can make code hard to debug
  • Follow the 12-factor app methodology: For deployment

Master Django with Expert Mentorship

Our Full Stack Python program covers Django from basics to building production-ready applications. Learn MTV architecture, ORM, authentication, and deployment with personalized guidance.

Explore Full Stack Python Program

Related Articles