DS DevShelfHub Projects · AI tools
Cheatsheets / Django
Cheatsheet · Dev tooling

Django: Models, ORM, Views and URLs Reference Guide

By DevShelfHub

Models, ORM, views, URLs, templates, forms, admin, auth, settings — the everyday Django surface plus a DRF row group.

128 items 8 min ORM Views Admin

Start hereQuick start · 6 you’ll reach for daily

Scaffolddjango-admin startproject site .
New appmanage.py startapp blog
Migratemanage.py makemigrations && migrate
Runmanage.py runserver
Shellmanage.py shell
Sanitymanage.py check

Target versions · paceVersions

Targets: django ≥ 5.1 python ≥ 3.10 DRF ≥ 3.15 (optional)

Django 5.x is the LTS family. Async views, async ORM (.aget, .acount, async signals) are stable. DEFAULT_AUTO_FIELD defaults to BigAutoField. Names current as of May 2026.

install · scaffold · manageSetup

bash
# Install + scaffold
pip install "django>=5.1"
django-admin startproject mysite .
python manage.py startapp blog

# Daily-loop commands
python manage.py runserver 0.0.0.0:8000
python manage.py makemigrations
python manage.py migrate
python manage.py createsuperuser
python manage.py shell                  # IPython shell with project loaded

# Static + collect
python manage.py collectstatic --noinput
python manage.py check                  # system check, never skip

# Optional but ubiquitous
pip install djangorestframework         # REST APIs
pip install django-environ              # env-driven settings
pip install django-debug-toolbar        # dev

where things liveCommon imports

from django.db import models, transaction, connectionORM + transactional helpers.
from django.urls import path, re_path, include, reverse, reverse_lazyRouting + reverse URL building.
from django.shortcuts import render, redirect, get_object_or_404, get_list_or_404View-level shortcuts.
from django.http import HttpResponse, JsonResponse, Http404, HttpResponseRedirectLow-level responses.
from django.views.generic import View, ListView, DetailView, CreateView, UpdateView, DeleteView, FormViewCBV stdlib.
from django.contrib.auth import get_user_model, login, logout, authenticateAuth helpers. Always get_user_model().
from django.contrib.auth.decorators import login_required, permission_requiredFBV gates.
from django.contrib.auth.mixins import LoginRequiredMixin, PermissionRequiredMixinCBV gates.
from django import formsForm classes.
from django.core.cache import cacheCache backend.
from django.conf import settingsRead settings at runtime.
from django.contrib import admin, messagesAdmin + flash messages.
from rest_framework import serializers, viewsets, permissions, routersDRF core (optional).

declarative ORMModels

CharField(max_length=…)Strings. Always set max_length.
TextField()Unbounded text. No length limit.
SlugField, EmailField, URLField, UUIDField, JSONFieldValidated specialised text / json.
IntegerField, BigIntegerField, DecimalField(max_digits, decimal_places)Numbers. Money → Decimal.
DateField, DateTimeField(auto_now_add=True), TimeField, DurationFieldTemporal.
BooleanField(default=False)Always default explicitly; NULL booleans are confusing.
ForeignKey(Other, on_delete=models.CASCADE, related_name="…")M:1. Pick on_delete deliberately.
ManyToManyField(Other, through="X", related_name="…")M:N. through for extra fields.
OneToOneField(Other, on_delete=…)Subclassing-style relations.
choices = TextChoices / IntegerChoicesTyped enum. Better than tuples.
Meta: ordering, indexes, constraints, unique_togetherSchema-level metadata.
UniqueConstraint(fields=[…], name=…)Replaces legacy unique_together.
CheckConstraint(check=Q(qty__gte=0), name=…)DB-level validation.
Index(fields=["status", "-created"])Composite + descending indexes.
python
from django.db import models
from django.contrib.auth import get_user_model

User = get_user_model()

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

    title    = models.CharField(max_length=200, db_index=True)
    slug     = models.SlugField(max_length=220, unique=True)
    body     = models.TextField()
    author   = models.ForeignKey(User, on_delete=models.CASCADE, related_name="posts")
    tags     = models.ManyToManyField("Tag", related_name="posts", blank=True)
    status   = models.CharField(max_length=10, choices=Status.choices, default=Status.DRAFT)
    created  = models.DateTimeField(auto_now_add=True)
    updated  = models.DateTimeField(auto_now=True)

    class Meta:
        ordering = ["-created"]
        indexes  = [models.Index(fields=["status", "-created"])]
        constraints = [
            models.UniqueConstraint(fields=["author", "slug"], name="uniq_author_slug"),
        ]

    def __str__(self) -> str:
        return self.title

class Tag(models.Model):
    name = models.CharField(max_length=50, unique=True)

schema changesMigrations

manage.py makemigrationsDetect model changes → new file.
manage.py makemigrations app --name add_indexesCustom name. Helps history.
manage.py migrateApply pending migrations.
manage.py migrate app 0003Migrate forward / backward to a specific point.
manage.py sqlmigrate app 0003Show generated SQL without running.
manage.py showmigrationsList + applied flags.
manage.py makemigrations --empty appHand-written data migration.
RunPython(forward, reverse)In-Python data fix-ups inside a migration.
manage.py squashmigrations app 0001 0050Collapse history once stable.

QuerySet APIORM

Post.objects.all() / .filter(status=…) / .exclude(…)Lazy QuerySets. Compose freely.
.get(id=1) / .first() / .last()Single-row reads. get raises if not exactly one.
__exact, __icontains, __gt, __gte, __in, __isnull, __rangeLookup suffixes.
__date / __year / __week_dayDate-component lookups.
Q(a=1) | Q(b=2) / ~Q(c=3)Boolean composition.
F("qty") + 1Reference DB columns inside expressions.
.annotate(n=Count("tags"), total=Sum("price"))Compute per-row aggregates.
.aggregate(Avg("score"))Single-row aggregate over the whole set.
.values("id", "name") / .values_list(…, flat=True)Project to dicts / tuples.
.select_related("author")Preferred JOIN for FK / O2O. Cuts N+1.
.prefetch_related("tags")Separate query for M2M / reverse FK. Cuts N+1.
.only("id", "title") / .defer("body")Limit columns loaded.
.update(status="archived")SQL-level update. Skips save().
.bulk_create(objs, batch_size=500)One INSERT for many rows.
.iterator(chunk_size=1000)Stream a big QuerySet without caching.
await Post.objects.aget(id=1)Async variants exist for most methods.

FBV & CBVViews

def view(request): return render(request, "t.html", ctx)Function-based view (FBV).
class PostList(ListView): model = PostClass-based view (CBV).
ListView, DetailView, CreateView, UpdateView, DeleteView, FormViewStock CBVs covering 80% of CRUD.
paginate_by = 20Pagination built into ListView.
get_queryset / get_context_dataOverride hook points.
@login_required / @require_http_methods(["POST"])FBV decorators.
LoginRequiredMixin, PermissionRequiredMixinCBV mixins. Put first.
async def view(request) / @sync_to_asyncAsync views. ORM has async too (aget, …).
return JsonResponse({"ok": True})JSON response. safe=False for non-dict roots.
raise Http404("reason")Equivalent to get_object_or_404.

routingURLs

path("posts/<int:id>/", view, name="detail")Preferred Path converters.
<str:>, <int:>, <slug:>, <uuid:>, <path:>Built-in converters.
re_path(r"^posts/(?P<slug>[-\w]+)/$", view)Regex fallback for complex patterns.
app_name = "blog"Namespace. Reference as blog:detail.
include("blog.urls", namespace="blog")Mount under a prefix.
reverse("blog:detail", kwargs={"id":1})Build URLs from names.
{% url "blog:detail" pk=p.pk %}Same in templates.
python
# blog/urls.py
from django.urls import path
from . import views

app_name = "blog"
urlpatterns = [
    path("",                      views.PostList.as_view(),   name="list"),
    path("posts//",    views.PostDetail.as_view(), name="detail"),
    path("posts/new/",            views.PostCreate.as_view(), name="create"),
]

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

# blog/views.py
from django.shortcuts import get_object_or_404, render, redirect
from django.contrib.auth.decorators import login_required
from django.views.generic import ListView, DetailView, CreateView
from django.urls import reverse_lazy
from .models import Post

class PostList(ListView):
    model = Post
    paginate_by = 10
    queryset = Post.objects.filter(status=Post.Status.PUBLISHED)

class PostDetail(DetailView):
    model = Post

class PostCreate(CreateView):
    model    = Post
    fields   = ["title", "slug", "body"]
    success_url = reverse_lazy("blog:list")

Django Template LanguageTemplates

{{ value }}Variable interpolation (autoescaped).
{{ value|date:"Y-m-d" }}Filters.
{% for x in items %} … {% endfor %}Loops + forloop.counter / .last.
{% if cond %} … {% elif %} … {% endif %}Conditional.
{% extends "base.html" %} / {% block content %}Inheritance.
{% include "partial.html" with x=1 %}Inline another template.
{% static "css/app.css" %}From {% load static %}.
{% url "blog:detail" pk=1 %}Reverse a URL by name.
{% csrf_token %}Required inside HTML forms (POST).
{{ value|safe }}Bypass autoescape. Trusted strings only.

validation + renderForms

class MyForm(forms.Form): name = forms.CharField()Plain form.
class MyForm(forms.ModelForm): class Meta: model, fieldsTied to a model.
form.is_valid() / form.cleaned_dataValidate + access typed data.
form.errors / form.non_field_errors()Per-field / form-wide errors.
def clean_email(self): …Per-field validation. Raise ValidationError.
def clean(self): …Cross-field validation.
widgets = {"body": forms.Textarea(…)}Override widgets.
form.save(commit=False)Get an unsaved model instance.

batteries-included CRUDAdmin

admin.site.register(Post)Quickest. Default ModelAdmin.
@admin.register(Post)Decorator form, paired with a class.
list_display = ("title", "status", "created")Columns in the changelist.
list_filter / search_fields / date_hierarchy / orderingStandard knobs.
prepopulated_fields = {"slug": ("title",)}Auto-fill from another field.
readonly_fields / inlinesLock + nested editing.
def get_queryset(self, request): …Scope admin per-user.
@admin.action(description="Publish")Bulk actions on selected rows.

users, sessions, permsAuth & permissions

get_user_model()Preferred Avoid importing User directly.
authenticate(request, username=…, password=…)Returns user or None.
login(request, user) / logout(request)Session-based.
@login_required(login_url="/login")FBV gate.
@permission_required("blog.change_post")Permission codename gate.
user.has_perm("blog.change_post")Programmatic check.
AUTH_USER_MODEL = "accounts.User"Set before first migration to avoid pain.
AbstractBaseUser / AbstractUserCustom user base classes.
PermissionsMixinAdds groups + per-object perms support.

configurationSettings & deployment

DEBUG = False (in prod)Never True with public traffic.
ALLOWED_HOSTS = ["example.com"]Required when DEBUG is off.
SECRET_KEY = env("SECRET_KEY")Load from env / secrets manager.
DATABASES["default"] = env.db()Connection from DATABASE_URL.
CACHES["default"] = redis backendAdd Redis for sessions / fragments.
SECURE_HSTS_SECONDS, SECURE_SSL_REDIRECT, CSRF_TRUSTED_ORIGINSSet behind HTTPS / proxy.
STATIC_ROOT + collectstaticRun before deploy; serve via nginx / whitenoise.
gunicorn site.wsgi:application --workers 3WSGI server.
uvicorn site.asgi:application --workers 3ASGI for async views / WebSockets.
manage.py check --deployProduction-readiness audit.

REST APIsDjango REST framework

serializers.ModelSerializerAuto serialiser from a model.
viewsets.ModelViewSetCRUD endpoints in one class.
permission_classes = [IsAuthenticatedOrReadOnly]Per-view auth policy.
DefaultRouter().register("posts", ViewSet)Auto URL generation.
filter_backends = [SearchFilter, OrderingFilter]Plug-in query backends.
pagination_class = PageNumberPaginationPage / cursor / limit-offset paginators.
throttle_classes = [UserRateThrottle]Rate limits.
@action(detail=True, methods=["post"])Custom endpoint on a ViewSet.
python
# api/serializers.py
from rest_framework import serializers
from blog.models import Post

class PostSerializer(serializers.ModelSerializer):
    class Meta:
        model  = Post
        fields = ["id", "title", "slug", "body", "status", "created"]
        read_only_fields = ["id", "created"]

# api/views.py
from rest_framework import viewsets, permissions, filters
from .serializers import PostSerializer
from blog.models import Post

class PostViewSet(viewsets.ModelViewSet):
    queryset           = Post.objects.all().order_by("-created")
    serializer_class   = PostSerializer
    permission_classes = [permissions.IsAuthenticatedOrReadOnly]
    filter_backends    = [filters.SearchFilter, filters.OrderingFilter]
    search_fields      = ["title", "body"]
    ordering_fields    = ["created", "title"]

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

# api/urls.py
from rest_framework.routers import DefaultRouter
from .views import PostViewSet

router = DefaultRouter()
router.register("posts", PostViewSet)
urlpatterns = router.urls

model · CBV · URLEnd-to-end · Notes app

Smallest viable Django app: model, two CBVs, URLconf. Hook into INSTALLED_APPS, migrate, runserver, done.

python
# End-to-end: model -> migrate -> URL -> class-based view -> template -> serve.

# blog/models.py
from django.db import models
class Note(models.Model):
    body    = models.CharField(max_length=280)
    created = models.DateTimeField(auto_now_add=True)
    class Meta: ordering = ["-created"]

# blog/views.py
from django.views.generic import ListView, CreateView
from django.urls import reverse_lazy
from .models import Note
class NoteList(ListView):
    model = Note; paginate_by = 20
class NoteCreate(CreateView):
    model = Note; fields = ["body"]; success_url = reverse_lazy("blog:list")

# blog/urls.py
from django.urls import path
from .views import NoteList, NoteCreate
app_name = "blog"
urlpatterns = [
    path("",     NoteList.as_view(),   name="list"),
    path("new/", NoteCreate.as_view(), name="create"),
]

# Then:
#   python manage.py makemigrations blog
#   python manage.py migrate
#   python manage.py runserver

Best practiceGood to know

Use select_related / prefetch_related early. The N+1 query is the most common Django performance bug. Add them as soon as you iterate a queryset and touch a relation.
Pin a custom user model from project start. AUTH_USER_MODEL = "accounts.User" before the first migration costs nothing. Adding it later is a multi-day surgery.
Run manage.py check --deploy in CI. Catches missing security headers, debug-on-in-prod, weak SECRET_KEY — the classic deploy-day surprises.

Common trapsWatch out for

Migrations don’t move data automatically. Renaming a column or splitting a model triggers schema migrations but never copies rows. Add an explicit RunPython data migration in between.
.update() bypasses save() and signals. Custom save() logic, auto_now, post_save signals — none of these fire. Use loop + save() when you need them.
Don’t import User directly. from django.contrib.auth.models import User hard-codes the default and breaks when someone swaps in a custom user. Always get_user_model().

Go deeperSee also

Django FAQ

What is Django used for?

Django is a high-level Python web framework for building secure, database-backed web applications rapidly. It includes an ORM for database access, a templating engine, URL routing, form handling, authentication, and an auto-generated admin panel — batteries included by design.

Is Django free to use?

Yes. Django is open source and released under the BSD license. There is no commercial edition; the framework, all official apps, and Django REST Framework are free to use in personal and commercial projects.

What is the difference between function-based views and class-based views in Django?

Function-based views (FBVs) are plain Python functions that take a request and return a response — simple and explicit. Class-based views (CBVs) inherit from generic classes like ListView and DetailView that provide reusable behaviour through inheritance and mixins.

How does the Django ORM work?

Django's ORM maps Python classes to database tables. Each Model subclass defines fields that become columns. Calling .objects.filter(), .exclude(), or .get() returns a lazy QuerySet that translates to SQL only when evaluated — by iterating, slicing, or calling .values(), .count(), etc.

What is Django REST Framework?

Django REST Framework (DRF) is a third-party library that adds API-building tools on top of Django — serializers that convert models to JSON, generic API views like ModelViewSet, authentication classes, permissions, throttling, and a browsable API. Install with pip install djangorestframework.

How does Django handle database migrations?

Django's migration system auto-generates SQL from model changes via python manage.py makemigrations, which creates versioned migration files. python manage.py migrate applies them in order, tracking state in a django_migrations table. For team workflows, always commit migration files to version control and run migrate in CI/CD. Use squashmigrations to consolidate a long history.