Django Cheat Sheet

Commands, ORM queries and settings.

Django has a lot of surface area, and its docs are excellent but long. This sheet is the short list: the commands you'll run every day and the ORM queries that show up in nearly every project. It assumes Django 5.x, which is what this site itself runs on.

Commands

startproject, startapp, makemigrations, migrate, createsuperuser, runserver, shell, collectstatic, check --deploy

ORM

Product.objects.filter(price__gte=100, name__icontains="pro").order_by("-price")[:10]
Product.objects.exclude(in_stock=False).count()
Order.objects.select_related("customer").prefetch_related("items")
from django.db.models import Sum, Count, Q, F
Order.objects.values("city").annotate(total=Sum("amount"))
Product.objects.filter(Q(a=1) | Q(b=2))
Product.objects.update(price=F("price") * 1.1)

What each command is for

  • makemigrations writes a migration file describing your model changes. migrate applies it to the database. Run both after every change to models.py, and commit the migration files.
  • shell opens Python with your project loaded, perfect for trying an ORM query before putting it in a view.
  • check --deploy lists the security settings you've missed. Run it before every launch. This site's deploy script refuses to continue if it reports anything.
  • collectstatic copies CSS, JS and images into one folder for your web server. Forget it after a change and the live site keeps showing the old styles.

Getting the ORM right

The N+1 problem. Looping over orders and printing order.customer.name runs one extra query per order. With 500 orders, that's 501 queries and a slow page. select_related("customer") fetches them in one go with a JOIN. Use prefetch_related for many-to-many and reverse relations.

F() for updates based on the current value. Product.objects.update(price=F("price") * 1.1) raises every price in a single database query, and it's safe even if two people do it at the same time.

Q() for OR conditions. .filter(a=1, b=2) means AND. For OR you need Q(a=1) | Q(b=2).

Querysets are lazy. Nothing hits the database until you loop over, slice or print the queryset. That's why you can build one up across several lines without any cost.

.exists() and .count() are much cheaper than loading objects just to check if there are any.

Settings to double-check before going live

DEBUG = False, a real SECRET_KEY from environment variables, ALLOWED_HOSTS set to your domain, SECURE_SSL_REDIRECT = True, and CSRF_TRUSTED_ORIGINS including your https:// domain.