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