Django Cheat Sheet

Essential Django commands and patterns for project setup, models, views, URLs, templates, forms, admin, authentication, and Django REST Framework. Bookmark this page for quick reference.

Project Setup

CommandDescription
pip install djangoInstall Django via pip
django-admin startproject myprojectCreate a new Django project
django-admin startproject myproject .Create project in the current directory (no extra nesting)
python manage.py startapp myappCreate a new app inside the project
python manage.py runserverStart the development server on port 8000
python manage.py runserver 0.0.0.0:8080Run dev server on a custom host and port
python manage.py checkCheck for project issues without running migrations
python manage.py shellOpen an interactive Python shell with Django loaded

Project Structure

myproject/
    manage.py               # CLI utility for the project
    myproject/
        __init__.py
        settings.py         # Project configuration
        urls.py             # Root URL configuration
        asgi.py             # ASGI entry point
        wsgi.py             # WSGI entry point
    myapp/
        __init__.py
        admin.py            # Admin site registration
        apps.py             # App configuration
        models.py           # Database models
        views.py            # View functions/classes
        urls.py             # App URL patterns (create manually)
        tests.py            # Unit tests
        migrations/         # Database migration files

settings.py Essentials

# Add your app to INSTALLED_APPS
INSTALLED_APPS = [
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',
    'myapp',                  # Your app
    'rest_framework',         # If using DRF
]

# Database configuration (default: SQLite)
DATABASES = {
    'default': {
        'ENGINE': 'django.db.backends.sqlite3',
        'NAME': BASE_DIR / 'db.sqlite3',
    }
}

# PostgreSQL example
DATABASES = {
    'default': {
        'ENGINE': 'django.db.backends.postgresql',
        'NAME': 'mydb',
        'USER': 'myuser',
        'PASSWORD': 'mypass',
        'HOST': 'localhost',
        'PORT': '5432',
    }
}

# Static and media files
STATIC_URL = '/static/'
STATIC_ROOT = BASE_DIR / 'staticfiles'
MEDIA_URL = '/media/'
MEDIA_ROOT = BASE_DIR / 'media'

Models

Common Field Types

FieldDescription
CharField(max_length=255)Short text (requires max_length)
TextField()Long text, no max_length required
IntegerField()Integer values
FloatField()Floating-point numbers
DecimalField(max_digits=10, decimal_places=2)Precise decimal numbers (for money, etc.)
BooleanField(default=False)True/False values
DateField(auto_now_add=True)Date, auto-set on creation
DateTimeField(auto_now=True)Date and time, auto-updated on save
EmailField()Email with validation
URLField()URL with validation
FileField(upload_to='uploads/')File upload field
ImageField(upload_to='images/')Image upload (requires Pillow)
SlugField(unique=True)URL-friendly slug
UUIDField(default=uuid.uuid4)UUID primary key or field
JSONField()JSON data (PostgreSQL native, SQLite 3.9+)
ForeignKey(Model, on_delete=models.CASCADE)Many-to-one relationship
ManyToManyField(Model)Many-to-many relationship
OneToOneField(Model, on_delete=models.CASCADE)One-to-one relationship

Model Example

from django.db import models
from django.urls import reverse

class Article(models.Model):
    title = models.CharField(max_length=200)
    slug = models.SlugField(unique=True)
    body = models.TextField()
    author = models.ForeignKey(
        'auth.User',
        on_delete=models.CASCADE,
        related_name='articles'
    )
    tags = models.ManyToManyField('Tag', blank=True)
    published = models.BooleanField(default=False)
    created_at = models.DateTimeField(auto_now_add=True)
    updated_at = models.DateTimeField(auto_now=True)

    class Meta:
        ordering = ['-created_at']
        verbose_name_plural = 'articles'
        indexes = [
            models.Index(fields=['-created_at']),
        ]

    def __str__(self):
        return self.title

    def get_absolute_url(self):
        return reverse('article-detail', kwargs={'slug': self.slug})

Common Meta Options

OptionDescription
ordering = ['-created_at']Default ordering for querysets
verbose_name = 'article'Human-readable singular name
verbose_name_plural = 'articles'Human-readable plural name
db_table = 'custom_table_name'Custom database table name
unique_together = [['field1', 'field2']]Unique constraint on multiple fields
indexes = [models.Index(fields=['name'])]Database indexes for performance
abstract = TrueMake this an abstract base model (no table created)

Migrations

CommandDescription
python manage.py makemigrationsGenerate migration files from model changes
python manage.py makemigrations myappGenerate migrations for a specific app
python manage.py migrateApply all pending migrations to the database
python manage.py migrate myapp 0003Migrate to a specific migration number
python manage.py showmigrationsShow all migrations and their status
python manage.py sqlmigrate myapp 0001Show the SQL for a specific migration
python manage.py migrate myapp zeroUnapply all migrations for an app

QuerySet API

# Create
article = Article.objects.create(title='Hello', body='World')
article = Article(title='Hello')
article.save()

# Read
Article.objects.all()                          # All objects
Article.objects.get(pk=1)                      # Single object (raises DoesNotExist)
Article.objects.filter(published=True)         # Filter by condition
Article.objects.exclude(published=False)       # Exclude by condition
Article.objects.order_by('-created_at')        # Order results
Article.objects.first()                        # First object or None
Article.objects.last()                         # Last object or None
Article.objects.count()                        # Count results
Article.objects.exists()                       # Check if any exist
Article.objects.values('title', 'slug')        # Return dictionaries
Article.objects.values_list('title', flat=True) # Return flat list

# Filter lookups
Article.objects.filter(title__contains='Django')      # Case-sensitive contains
Article.objects.filter(title__icontains='django')     # Case-insensitive contains
Article.objects.filter(title__startswith='How')        # Starts with
Article.objects.filter(created_at__year=2026)          # Year filter
Article.objects.filter(created_at__gte='2026-01-01')   # Greater than or equal
Article.objects.filter(tags__name__in=['python', 'web']) # In list
Article.objects.filter(author__isnull=True)            # Is NULL

# Update
Article.objects.filter(pk=1).update(title='New Title')
article.title = 'New Title'
article.save()

# Delete
article.delete()
Article.objects.filter(published=False).delete()

# Chaining
Article.objects.filter(published=True).order_by('-created_at')[:5]

# Aggregation
from django.db.models import Count, Avg, Sum, Max, Min
Article.objects.aggregate(total=Count('id'))
Article.objects.values('author').annotate(count=Count('id'))

Views

Function-Based Views

from django.shortcuts import render, get_object_or_404, redirect
from django.http import HttpResponse, JsonResponse
from .models import Article

# Simple view
def index(request):
    return HttpResponse('Hello, world!')

# Template view with context
def article_list(request):
    articles = Article.objects.filter(published=True)
    return render(request, 'myapp/article_list.html', {
        'articles': articles,
    })

# Detail view with 404 handling
def article_detail(request, slug):
    article = get_object_or_404(Article, slug=slug)
    return render(request, 'myapp/article_detail.html', {
        'article': article,
    })

# Handle form POST
def article_create(request):
    if request.method == 'POST':
        form = ArticleForm(request.POST)
        if form.is_valid():
            article = form.save(commit=False)
            article.author = request.user
            article.save()
            return redirect('article-detail', slug=article.slug)
    else:
        form = ArticleForm()
    return render(request, 'myapp/article_form.html', {'form': form})

# JSON response
def api_articles(request):
    articles = list(Article.objects.values('title', 'slug', 'created_at'))
    return JsonResponse({'articles': articles})

Class-Based Views

from django.views.generic import (
    ListView, DetailView, CreateView,
    UpdateView, DeleteView, TemplateView
)
from django.urls import reverse_lazy
from .models import Article

# List view
class ArticleListView(ListView):
    model = Article
    template_name = 'myapp/article_list.html'
    context_object_name = 'articles'
    paginate_by = 10
    queryset = Article.objects.filter(published=True)

# Detail view
class ArticleDetailView(DetailView):
    model = Article
    template_name = 'myapp/article_detail.html'
    slug_field = 'slug'

# Create view
class ArticleCreateView(CreateView):
    model = Article
    fields = ['title', 'slug', 'body', 'tags']
    template_name = 'myapp/article_form.html'

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

# Update view
class ArticleUpdateView(UpdateView):
    model = Article
    fields = ['title', 'body', 'tags', 'published']
    template_name = 'myapp/article_form.html'

# Delete view
class ArticleDeleteView(DeleteView):
    model = Article
    template_name = 'myapp/article_confirm_delete.html'
    success_url = reverse_lazy('article-list')

Common CBV Reference

View ClassPurpose
TemplateViewRender a template with optional context
ListViewDisplay a list of objects with pagination
DetailViewDisplay a single object by pk or slug
CreateViewDisplay a form and create an object on POST
UpdateViewDisplay a form pre-filled with object data and update on POST
DeleteViewDisplay confirmation page and delete on POST
RedirectViewRedirect to a given URL
FormViewDisplay and process a form (not tied to a model)

URL Patterns

Root urls.py

from django.contrib import admin
from django.urls import path, include

urlpatterns = [
    path('admin/', admin.site.urls),
    path('', include('myapp.urls')),
    path('api/', include('myapp.api_urls')),
]

App urls.py

from django.urls import path
from . import views

app_name = 'myapp'  # Namespace for reverse lookups

urlpatterns = [
    path('', views.article_list, name='article-list'),
    path('create/', views.article_create, name='article-create'),
    path('<slug:slug>/', views.article_detail, name='article-detail'),
    path('<slug:slug>/edit/', views.article_update, name='article-update'),
    path('<slug:slug>/delete/', views.article_delete, name='article-delete'),
]

# Class-based views in urls.py
urlpatterns = [
    path('', ArticleListView.as_view(), name='article-list'),
    path('<slug:slug>/', ArticleDetailView.as_view(), name='article-detail'),
]

Path Converters

ConverterPatternExample
<int:pk>Positive integers/articles/42/
<str:name>Non-empty string (no slashes)/category/python/
<slug:slug>Slug (letters, digits, hyphens, underscores)/article/my-post/
<uuid:id>UUID format/item/550e8400-e29b.../
<path:file_path>Any string including slashes/files/docs/readme.txt

Templates

Template Tags

TagDescription
{% if condition %} ... {% elif %} ... {% else %} ... {% endif %}Conditional rendering
{% for item in list %} ... {% empty %} ... {% endfor %}Loop with optional empty fallback
{% block content %} ... {% endblock %}Define a template block for inheritance
{% extends "base.html" %}Inherit from a parent template
{% include "partial.html" %}Include another template
{% url 'article-detail' slug=article.slug %}Reverse URL by name
{% csrf_token %}CSRF token for forms (required for POST)
{% load static %}Load the static template tag library
{% static 'css/style.css' %}Generate URL for a static file
{% with total=articles.count %} ... {% endwith %}Assign a value to a variable
{% comment %} ... {% endcomment %}Block comment (not rendered)

Template Filters

FilterDescription
{{ value|default:"N/A" }}Show default if value is falsy
{{ value|length }}Length of string or list
{{ value|lower }} / {{ value|upper }}Change text case
{{ value|title }}Title case
{{ value|truncatewords:30 }}Truncate after N words
{{ value|truncatechars:100 }}Truncate after N characters
{{ value|date:"F j, Y" }}Format date
{{ value|timesince }}Time since date (e.g., "3 days ago")
{{ value|slugify }}Convert to URL slug
{{ value|linebreaks }}Convert newlines to <p> and <br>
{{ value|safe }}Mark string as safe HTML (skip auto-escaping)
{{ value|escape }}HTML-escape the value
{{ value|join:", " }}Join list items with separator
{{ value|floatformat:2 }}Round to N decimal places
{{ value|pluralize }}Append "s" if value is not 1

Template Inheritance Example

<!-- base.html -->
<!DOCTYPE html>
<html>
<head>
    <title>{% block title %}My Site{% endblock %}</title>
    {% load static %}
    <link rel="stylesheet" href="{% static 'css/style.css' %}">
</head>
<body>
    <nav>...</nav>
    <main>
        {% block content %}{% endblock %}
    </main>
    <footer>...</footer>
</body>
</html>

<!-- article_list.html -->
{% extends "base.html" %}
{% block title %}Articles{% endblock %}
{% block content %}
    <h1>Articles</h1>
    {% for article in articles %}
        <div>
            <h2><a href="{% url 'article-detail' slug=article.slug %}">
                {{ article.title }}
            </a></h2>
            <p>{{ article.body|truncatewords:50 }}</p>
            <span>{{ article.created_at|date:"M d, Y" }}</span>
        </div>
    {% empty %}
        <p>No articles found.</p>
    {% endfor %}
{% endblock %}

Forms

ModelForm

from django import forms
from .models import Article

class ArticleForm(forms.ModelForm):
    class Meta:
        model = Article
        fields = ['title', 'slug', 'body', 'tags', 'published']
        # or: exclude = ['author', 'created_at']
        widgets = {
            'body': forms.Textarea(attrs={
                'rows': 10,
                'placeholder': 'Write your article...'
            }),
            'title': forms.TextInput(attrs={
                'class': 'form-control'
            }),
        }
        labels = {
            'body': 'Article Content',
        }
        help_texts = {
            'slug': 'URL-friendly identifier (letters, numbers, hyphens)',
        }

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

Regular Form

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

    def clean_email(self):
        email = self.cleaned_data['email']
        if not email.endswith('@example.com'):
            raise forms.ValidationError('Must use a company email.')
        return email

Form in Template

<form method="post">
    {% csrf_token %}
    {{ form.as_p }}
    <button type="submit">Save</button>
</form>

<!-- Manual field rendering -->
<form method="post">
    {% csrf_token %}
    {% for field in form %}
        <div class="form-group">
            <label for="{{ field.id_for_label }}">{{ field.label }}</label>
            {{ field }}
            {% if field.errors %}
                <span class="error">{{ field.errors.0 }}</span>
            {% endif %}
        </div>
    {% endfor %}
    <button type="submit">Save</button>
</form>

Admin

from django.contrib import admin
from .models import Article, Tag

@admin.register(Article)
class ArticleAdmin(admin.ModelAdmin):
    list_display = ['title', 'author', 'published', 'created_at']
    list_filter = ['published', 'created_at', 'author']
    search_fields = ['title', 'body']
    prepopulated_fields = {'slug': ('title',)}
    list_editable = ['published']
    date_hierarchy = 'created_at'
    ordering = ['-created_at']
    readonly_fields = ['created_at', 'updated_at']
    raw_id_fields = ['author']
    list_per_page = 25
    fieldsets = (
        (None, {
            'fields': ('title', 'slug', 'body')
        }),
        ('Publishing', {
            'fields': ('author', 'published', 'tags'),
            'classes': ('collapse',),
        }),
        ('Timestamps', {
            'fields': ('created_at', 'updated_at'),
        }),
    )

# Create superuser for admin access
# python manage.py createsuperuser
Admin OptionDescription
list_displayFields shown in the list page
list_filterSidebar filters on the list page
search_fieldsFields searchable from the search bar
prepopulated_fieldsAuto-fill slug from another field
list_editableFields editable directly on the list page
readonly_fieldsFields displayed but not editable
raw_id_fieldsUse input widget instead of dropdown for ForeignKey
inlinesEdit related objects on the same page

Authentication

Built-in Auth URLs

# urls.py
from django.contrib.auth import views as auth_views

urlpatterns = [
    # Include all auth URLs at once:
    path('accounts/', include('django.contrib.auth.urls')),
    # This provides:
    #   accounts/login/
    #   accounts/logout/
    #   accounts/password_change/
    #   accounts/password_change/done/
    #   accounts/password_reset/
    #   accounts/password_reset/done/
    #   accounts/reset/<uidb64>/<token>/
    #   accounts/reset/done/
]

# settings.py
LOGIN_REDIRECT_URL = '/'
LOGOUT_REDIRECT_URL = '/'
LOGIN_URL = '/accounts/login/'

Protecting Views

from django.contrib.auth.decorators import login_required, permission_required
from django.contrib.auth.mixins import LoginRequiredMixin, PermissionRequiredMixin

# Function-based view
@login_required
def dashboard(request):
    return render(request, 'dashboard.html')

@permission_required('myapp.can_publish')
def publish_article(request, pk):
    ...

# Class-based view
class DashboardView(LoginRequiredMixin, TemplateView):
    template_name = 'dashboard.html'

class PublishView(PermissionRequiredMixin, UpdateView):
    permission_required = 'myapp.can_publish'
    ...

User in Templates

{% if user.is_authenticated %}
    <p>Welcome, {{ user.username }}!</p>
    <a href="{% url 'logout' %}">Logout</a>
{% else %}
    <a href="{% url 'login' %}">Login</a>
{% endif %}

{% if perms.myapp.can_publish %}
    <a href="{% url 'publish' %}">Publish</a>
{% endif %}

Custom User Model

# models.py (set up BEFORE first migration)
from django.contrib.auth.models import AbstractUser

class CustomUser(AbstractUser):
    bio = models.TextField(blank=True)
    avatar = models.ImageField(upload_to='avatars/', blank=True)

# settings.py
AUTH_USER_MODEL = 'myapp.CustomUser'

REST Framework Basics

Installation and Setup

# Install
pip install djangorestframework

# settings.py
INSTALLED_APPS = [
    ...
    'rest_framework',
]

REST_FRAMEWORK = {
    'DEFAULT_PERMISSION_CLASSES': [
        'rest_framework.permissions.IsAuthenticatedOrReadOnly',
    ],
    'DEFAULT_PAGINATION_CLASS': 'rest_framework.pagination.PageNumberPagination',
    'PAGE_SIZE': 20,
}

Serializers

from rest_framework import serializers
from .models import Article

class ArticleSerializer(serializers.ModelSerializer):
    author_name = serializers.ReadOnlyField(source='author.username')

    class Meta:
        model = Article
        fields = ['id', 'title', 'slug', 'body', 'author', 'author_name',
                  'published', 'created_at']
        read_only_fields = ['author', 'created_at']

ViewSets and Routers

from rest_framework import viewsets, permissions
from rest_framework.decorators import action
from rest_framework.response import Response
from .models import Article
from .serializers import ArticleSerializer

class ArticleViewSet(viewsets.ModelViewSet):
    queryset = Article.objects.all()
    serializer_class = ArticleSerializer
    permission_classes = [permissions.IsAuthenticatedOrReadOnly]
    lookup_field = 'slug'

    def perform_create(self, serializer):
        serializer.save(author=self.request.user)

    @action(detail=True, methods=['post'])
    def publish(self, request, slug=None):
        article = self.get_object()
        article.published = True
        article.save()
        return Response({'status': 'published'})

# api_urls.py
from rest_framework.routers import DefaultRouter
from .views import ArticleViewSet

router = DefaultRouter()
router.register(r'articles', ArticleViewSet)

urlpatterns = router.urls

DRF Quick Reference

ClassDescription
serializers.ModelSerializerAuto-generates fields from a model
viewsets.ModelViewSetFull CRUD (list, create, retrieve, update, destroy)
viewsets.ReadOnlyModelViewSetRead-only (list and retrieve)
generics.ListCreateAPIViewGET (list) and POST (create)
generics.RetrieveUpdateDestroyAPIViewGET, PUT/PATCH, DELETE for one object
permissions.IsAuthenticatedAllow only authenticated users
permissions.IsAdminUserAllow only admin/staff users
permissions.AllowAnyAllow all (public)

Common Commands

CommandDescription
python manage.py runserverStart development server
python manage.py makemigrationsCreate new migration files
python manage.py migrateApply database migrations
python manage.py createsuperuserCreate an admin superuser
python manage.py shellInteractive Python shell with Django
python manage.py dbshellOpen native database CLI
python manage.py testRun all tests
python manage.py test myapp.tests.TestArticleRun a specific test class
python manage.py collectstaticCollect static files into STATIC_ROOT
python manage.py flushDelete all data from the database
python manage.py dumpdata myapp > data.jsonExport app data as JSON
python manage.py loaddata data.jsonImport data from a JSON fixture
python manage.py showmigrationsList all migrations and their status
python manage.py inspectdbGenerate models from an existing database
python manage.py changepassword <username>Change a user's password
python manage.py clearsessionsRemove expired sessions from the database

Frequently Asked Questions

What is the difference between a Django project and a Django app?

A Django project is the entire web application, created with django-admin startproject. It contains settings, root URL configuration, and WSGI/ASGI entry points. A Django app is a modular component within a project that handles a specific feature (e.g., blog, users, payments). Apps are created with python manage.py startapp and must be added to INSTALLED_APPS in settings.py. A single project can contain many apps, and apps can be reused across multiple projects.

When should I use function-based views vs class-based views in Django?

Function-based views (FBVs) are simpler, more explicit, and easier to understand for beginners or for straightforward logic. Class-based views (CBVs) provide built-in mixins and generic views (ListView, DetailView, CreateView) that reduce boilerplate for common patterns like CRUD operations. Use FBVs when the view logic is simple or highly custom. Use CBVs when you need inheritance, reusable mixins, or when your views follow standard patterns that Django's generic views already handle.

How do Django migrations work and when should I create them?

Django migrations are Python files that describe changes to your database schema. When you modify a model (add a field, change a field type, delete a model), run python manage.py makemigrations to generate a migration file, then python manage.py migrate to apply it to the database. Django tracks which migrations have been applied in a django_migrations table. Create migrations every time you change your models. Never edit migration files manually unless you understand the consequences, and always commit migration files to version control.