TechLead
Lesson 16 of 25
5 min read
Python

Django Fundamentals

Learn Django, the batteries-included web framework for building full-stack web applications in Python

What is Django?

Django is a high-level Python web framework that encourages rapid development and clean, pragmatic design. Often called the "batteries-included" framework, Django provides an ORM, admin panel, authentication, URL routing, template engine, form handling, and security features out of the box. It powers sites like Instagram, Pinterest, and Mozilla.

Django vs FastAPI

  • Django: Full-stack, batteries-included, great for traditional web apps with server-rendered HTML, admin panels, and complex data models
  • FastAPI: API-focused, lightweight, best for building REST/GraphQL APIs consumed by frontend frameworks or mobile apps

Getting Started

# Install Django
# pip install django

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

# Create an app within the project
# python manage.py startapp blog

# Project structure:
# myproject/
#   manage.py             # CLI tool
#   myproject/
#     __init__.py
#     settings.py         # Configuration
#     urls.py             # Root URL config
#     wsgi.py             # WSGI entry point
#   blog/
#     __init__.py
#     admin.py            # Admin panel config
#     apps.py             # App config
#     models.py           # Database models
#     views.py            # Request handlers
#     urls.py             # App URL config (create this)
#     templates/          # HTML templates (create this)
#     tests.py            # Tests

# Run development server
# python manage.py runserver

Models (ORM)

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

class Category(models.Model):
    name = models.CharField(max_length=100, unique=True)
    slug = models.SlugField(unique=True)
    description = models.TextField(blank=True)

    class Meta:
        verbose_name_plural = "categories"
        ordering = ["name"]

    def __str__(self):
        return self.name

class Post(models.Model):
    class Status(models.TextChoices):
        DRAFT = "draft", "Draft"
        PUBLISHED = "published", "Published"

    title = models.CharField(max_length=200)
    slug = models.SlugField(unique=True)
    author = models.ForeignKey(User, on_delete=models.CASCADE, related_name="posts")
    category = models.ForeignKey(Category, on_delete=models.SET_NULL, null=True)
    body = models.TextField()
    status = models.CharField(max_length=10, choices=Status.choices, default=Status.DRAFT)
    created_at = models.DateTimeField(auto_now_add=True)
    updated_at = models.DateTimeField(auto_now=True)
    published_at = models.DateTimeField(null=True, blank=True)

    class Meta:
        ordering = ["-created_at"]

    def __str__(self):
        return self.title

    def publish(self):
        self.status = self.Status.PUBLISHED
        self.published_at = timezone.now()
        self.save()

# Apply migrations
# python manage.py makemigrations
# python manage.py migrate

Views and URLs

# blog/views.py
from django.shortcuts import render, get_object_or_404, redirect
from django.views.generic import ListView, DetailView, CreateView
from django.contrib.auth.mixins import LoginRequiredMixin
from .models import Post, Category

# Function-based view
def post_list(request):
    posts = Post.objects.filter(status="published").select_related("author", "category")
    categories = Category.objects.all()
    context = {"posts": posts, "categories": categories}
    return render(request, "blog/post_list.html", context)

def post_detail(request, slug):
    post = get_object_or_404(Post, slug=slug, status="published")
    return render(request, "blog/post_detail.html", {"post": post})

# Class-based views (more reusable)
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").select_related("author")

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

class PostCreateView(LoginRequiredMixin, CreateView):
    model = Post
    fields = ["title", "slug", "category", "body"]
    template_name = "blog/post_form.html"

    def form_valid(self, form):
        form.instance.author = self.request.user
        return super().form_valid(form)

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

app_name = "blog"
urlpatterns = [
    path("", views.PostListView.as_view(), name="post_list"),
    path("/", views.PostDetailView.as_view(), name="post_detail"),
    path("new/", views.PostCreateView.as_view(), name="post_create"),
]

# myproject/urls.py
# from django.urls import path, include
# urlpatterns = [
#     path("admin/", admin.site.urls),
#     path("blog/", include("blog.urls")),
# ]

Admin Panel

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

@admin.register(Category)
class CategoryAdmin(admin.ModelAdmin):
    list_display = ["name", "slug"]
    prepopulated_fields = {"slug": ("name",)}

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

    actions = ["make_published"]

    @admin.action(description="Mark selected posts as published")
    def make_published(self, request, queryset):
        queryset.update(status="published")

# Create superuser: python manage.py createsuperuser
# Admin panel at: http://localhost:8000/admin/

Key Takeaways

  • Batteries included: Django gives you ORM, admin, auth, and more out of the box
  • ORM is powerful: Define models as Python classes, Django handles SQL
  • CBVs for reuse: Use class-based views for common patterns
  • Admin panel: Get a free admin interface by registering your models

Continue Learning