pip install django && django-admin startproject mysite
Django is a batteries-included Python web framework — routing, ORM, admin, auth, and templates, generated as a working skeleton from one command.
django-admin startproject mysite
cd mysite && python manage.py runserver
INSTALLED_APPS = ["django.contrib.admin", "django.contrib.auth", ...]
Django ships an ORM, admin site, auth, sessions, and templating together, designed to interlock rather than picked and glued together separately.
INSTALLED_APPS = [
"django.contrib.admin",
"django.contrib.auth",
]
Model (data) → View (logic) → Template (presentation)
Django's MTV pattern: Model holds data and rules, View reads the request and coordinates, Template renders output — roughly MVC with the View/Controller names swapped.
def article_detail(request, pk):
article = Article.objects.get(pk=pk)
return render(request, "detail.html", {"article": article})
django-admin startproject mysite · python manage.py startapp blog
A project is the whole site (one settings module); an app is a self-contained, reusable feature package registered in INSTALLED_APPS.
python manage.py startapp blog
# then add "blog" to INSTALLED_APPS
from django.apps import apps; apps.get_model("blog", "Post")
The app registry is Django's in-memory catalogue of installed apps and models, populated once at startup — AppConfig.ready() is the safe place for cross-app imports.
class BlogConfig(AppConfig):
name = "blog"
def ready(self):
from . import signals
from django.conf import settings
Settings are a plain Python module selected via DJANGO_SETTINGS_MODULE; read values through django.conf.settings, never by importing the module directly.
from django.conf import settings
if settings.DEBUG:
...
path("articles/<int:year>/", views.year_archive)
A URLconf maps URL patterns to views via path()/include(); ROOT_URLCONF names the root module, and the first matching pattern wins.
urlpatterns = [
path("articles/", include("articles.urls")),
]
def view(request, ...): return HttpResponse(...)
A view is a callable taking an HttpRequest and returning an HttpResponse — function-based for simple logic, class-based for common reusable patterns.
def detail(request, pk):
article = Article.objects.get(pk=pk)
return render(request, "detail.html", {"article": article})
def middleware(get_response): def wrapper(request): ...; return wrapper
Middleware is an ordered chain wrapping get_response — request-phase code runs top-to-bottom on the way in, response-phase code runs bottom-to-top on the way out.
def timing_middleware(get_response):
def middleware(request):
response = get_response(request)
return response
return middleware
class Name(models.Model): field = models.CharField(max_length=N)
A model is a Python class mapping to a database table; makemigrations + migrate turns field changes into real schema changes; Model.objects is the query entry point.
class BlogPost(models.Model):
title = models.CharField(max_length=200)
BlogPost.objects.filter(title="Hello")
@admin.register(Model)
class ModelAdmin(admin.ModelAdmin): ...
The admin site auto-generates a CRUD interface from a registered model; ModelAdmin customizes list_display, list_filter, and search_fields.
@admin.register(Article)
class ArticleAdmin(admin.ModelAdmin):
list_display = ["title", "author"]
<app>/management/commands/<name>.py — class Command(BaseCommand): def handle(self, *args, **options): ...
A custom management command is a Command(BaseCommand) subclass discovered by file location, run via python manage.py <name>.
class Command(BaseCommand):
def handle(self, *args, **options):
self.stdout.write(self.style.SUCCESS("done"))
render(request, "template.html", {"key": value})
The Django Template Language renders {{ variables }} and {% tags %} against a context dict passed in by the view — it cannot run arbitrary Python.
{% if story.published %}
{{ story.content }}
{% endif %}
form = MyForm(request.POST); if form.is_valid(): form.cleaned_data["field"]
A Form class validates submitted data via is_valid(); read converted, validated values from cleaned_data afterward, never straight from request.POST.
class ContactForm(forms.Form):
subject = forms.CharField(max_length=100)
sender = forms.EmailField()
@receiver(post_save, sender=Model)
def handler(sender, instance, created, **kwargs): ...
Signals decouple senders from receivers — post_save fires after every save; check the `created` kwarg to distinguish insert from update.
@receiver(post_save, sender=User)
def on_user_saved(sender, instance, created, **kwargs):
if created:
...
{% load static %}{% static "app/file.css" %}
Static files (CSS/JS/images shipped with the app, not user uploads) are namespaced per-app and referenced via {% static %}; collectstatic gathers them for production.
{% load static %}
<link rel="stylesheet" href="{% static 'blog/post.css' %}">
photo = models.ImageField(upload_to="cars")
Media files are user uploads (FileField/ImageField), stored under MEDIA_ROOT and served from MEDIA_URL — distinct from project-shipped static files.
car.photo.url # '/media/cars/chevy.jpg'
user = authenticate(request, username=, password=); if user: login(request, user)
Django auth: authenticate() verifies credentials and returns User or None; login(request, user) is the separate step that attaches the user to the session.
@login_required
def dashboard(request):
return render(request, "dashboard.html")
request.session["key"] = value
Sessions store per-visitor data server-side (SESSION_ENGINE), identified by a session-ID cookie; request.session behaves like a plain dict.
request.session["cart_id"] = 42
request.session.get("cart_id")
messages.success(request, "text")
The messages framework queues one-time flash notifications that survive a redirect; a template iterating {% for message in messages %} both displays and clears them.
messages.success(request, "Saved!")
return redirect("profile")
from django.utils.translation import gettext as _
i18n marks text translatable via gettext/_() in Python or {% trans %} in templates; translators later supply per-language .po files.
message = _("Welcome to my site")
from django.utils import timezone; timezone.now()
USE_TZ=True stores datetimes in UTC and converts for display; timezone.now() returns an aware UTC value — datetime.datetime.now() returns an unsafe naive one.
order.placed_at = timezone.now()