# Code Reference — Django

_40 pages_

---
# Django (/docs/django)



# Django [#django]

ORM, views, auth, DRF.

Browse the sidebar for every cheat sheet in this section.

## Sections [#sections]

* [Examples](./examples)


---

# Admin (/docs/django/admin)



# Admin [#admin]

*Django · Reference cheat sheet*

***

## 📋 Overview [#-overview]

Django Admin is an auto-generated CRUD UI for models registered with `admin.site`. Enable `django.contrib.admin`, create a superuser, and customize list displays, filters, and search. Use it for internal ops—not as a public end-user interface.

## 🔧 Core concepts [#-core-concepts]

| Piece                                    | Role                                                          |
| ---------------------------------------- | ------------------------------------------------------------- |
| `admin.site.register(Model, ModelAdmin)` | Expose a model                                                |
| `@admin.register(Model)`                 | Decorator form                                                |
| `ModelAdmin`                             | list\_display, list\_filter, search\_fields, readonly\_fields |
| `InlineModelAdmin`                       | Edit related objects on the parent page                       |
| Permissions                              | Staff + model perms; superuser bypasses                       |
| URLs                                     | Included via `path("admin/", admin.site.urls)`                |

Requires `django.contrib.admin`, `auth`, `contenttypes`, `sessions`, `messages` in `INSTALLED_APPS`.

## 💡 Examples [#-examples]

**Register with list options:**

```python
# app/admin.py
from django.contrib import admin
from .models import Article


@admin.register(Article)
class ArticleAdmin(admin.ModelAdmin):
    list_display = ("title", "author", "published_at", "is_published")
    list_filter = ("is_published", "published_at")
    search_fields = ("title", "body")
    prepopulated_fields = {"slug": ("title",)}
    date_hierarchy = "published_at"
    ordering = ("-published_at",)
```

**Inlines:**

```python
from django.contrib import admin
from .models import Order, OrderItem


class OrderItemInline(admin.TabularInline):
    model = OrderItem
    extra = 1


@admin.register(Order)
class OrderAdmin(admin.ModelAdmin):
    inlines = [OrderItemInline]
    list_display = ("id", "user", "created_at")
```

**URLs:**

```python
# config/urls.py
from django.contrib import admin
from django.urls import path

urlpatterns = [
    path("admin/", admin.site.urls),
]
```

## ⚠️ Pitfalls [#️-pitfalls]

* Forgetting `admin.autodiscover` is fine on modern Django—apps' `admin.py` load via `AppConfig`.
* Heavy `list_display` callables without `list_select_related` cause N+1 queries.
* Exposing admin on the public internet without HTTPS, 2FA, and IP limits is risky.
* Custom user models must be registered carefully; swap before the first migration.

## 🔗 Related [#-related]

* [Custom admin](/docs/django/custom-admin)
* [Models](/docs/django/models)
* [Authentication](/docs/django/authentication)
* [Settings](/docs/django/settings)
* [URLs](/docs/django/urls)
* [Forms](/docs/django/forms)


---

# ASGI / Channels (/docs/django/asgi-channels)



# ASGI / Channels [#asgi--channels]

*Django · Reference cheat sheet*

***

## 📋 Overview [#-overview]

ASGI enables async Django and long-lived connections. Django 4.2+/5.x runs async views on ASGI servers (Uvicorn, Daphne, Hypercorn). **Channels** adds WebSockets, background channel layers (Redis), and consumers. Use ASGI for websockets/SSE; keep ORM calls safe (sync ORM in `sync_to_async` or async ORM where available).

## 🔧 Core concepts [#-core-concepts]

| Piece                                 | Role                         |
| ------------------------------------- | ---------------------------- |
| `asgi.py`                             | ASGI application entry       |
| `ProtocolTypeRouter`                  | HTTP vs WebSocket (Channels) |
| `URLRouter`                           | Websocket URL patterns       |
| `AuthMiddlewareStack`                 | Session user on WS           |
| `AsyncConsumer` / `WebsocketConsumer` | Handlers                     |
| Channel layer                         | Cross-process pub/sub        |
| `async_to_sync` / `sync_to_async`     | Bridge sync/async            |

Django async views: `async def view(request)`. Prefer async-safe libraries.

## 💡 Examples [#-examples]

**asgi.py (Channels):**

```python
import os
from django.core.asgi import get_asgi_application
from channels.routing import ProtocolTypeRouter, URLRouter
from channels.auth import AuthMiddlewareStack
from channels.security.websocket import AllowedHostsOriginValidator
import chat.routing

os.environ.setdefault("DJANGO_SETTINGS_MODULE", "config.settings")
django_asgi_app = get_asgi_application()

application = ProtocolTypeRouter(
    {
        "http": django_asgi_app,
        "websocket": AllowedHostsOriginValidator(
            AuthMiddlewareStack(URLRouter(chat.routing.websocket_urlpatterns))
        ),
    }
)
```

**Consumer:**

```python
from channels.generic.websocket import AsyncJsonWebsocketConsumer


class ChatConsumer(AsyncJsonWebsocketConsumer):
    async def connect(self):
        self.room = self.scope["url_route"]["kwargs"]["room"]
        await self.channel_layer.group_add(self.room, self.channel_name)
        await self.accept()

    async def disconnect(self, code):
        await self.channel_layer.group_discard(self.room, self.channel_name)

    async def receive_json(self, content, **kwargs):
        await self.channel_layer.group_send(
            self.room, {"type": "chat.message", "text": content["text"]}
        )

    async def chat_message(self, event):
        await self.send_json({"text": event["text"]})
```

**Settings:**

```python
ASGI_APPLICATION = "config.asgi.application"
CHANNEL_LAYERS = {
    "default": {
        "BACKEND": "channels_redis.core.RedisChannelLayer",
        "CONFIG": {"hosts": [("127.0.0.1", 6379)]},
    }
}
```

## ⚠️ Pitfalls [#️-pitfalls]

* Calling sync ORM directly in async consumers without `sync_to_async` → blocking / errors.
* In-memory channel layer—does not work across multiple processes.
* Missing origin validation on WebSockets.
* Mixing WSGI deploy with Channels—need ASGI server.
* Forgetting `type` method name mapping (`chat.message` → `chat_message`).

## 🔗 Related [#-related]

* [Settings](/docs/django/settings)
* [Middleware](/docs/django/middleware)
* [Authentication](/docs/django/authentication)
* [Security](/docs/django/security)
* [Views](/docs/django/views)


---

# Authentication (/docs/django/authentication)



# Authentication [#authentication]

*Django · Reference cheat sheet*

***

## 📋 Overview [#-overview]

Django’s auth system provides users, permissions, groups, password hashing, and session login. Use `django.contrib.auth` views/forms for classic sites, or packages like django-allauth for social login. Always authenticate against hashed passwords—never store plaintext.

## 🔧 Core concepts [#-core-concepts]

| Piece                                        | Role                                                  |
| -------------------------------------------- | ----------------------------------------------------- |
| `User` / custom user                         | `AUTH_USER_MODEL`                                     |
| `authenticate` / `login` / `logout`          | Session auth helpers                                  |
| `login_required` / `PermissionRequiredMixin` | Protect views                                         |
| Permissions                                  | `app_label.codename` (`add`/`change`/`delete`/`view`) |
| Password validators                          | `AUTH_PASSWORD_VALIDATORS`                            |
| Backends                                     | `AUTHENTICATION_BACKENDS` (ModelBackend default)      |

Sessions middleware + `AuthenticationMiddleware` attach `request.user`.

## 💡 Examples [#-examples]

**Login view (function):**

```python
from django.contrib.auth import authenticate, login
from django.shortcuts import redirect, render


def login_view(request):
    if request.method == "POST":
        user = authenticate(
            request,
            username=request.POST["username"],
            password=request.POST["password"],
        )
        if user is not None:
            login(request, user)
            return redirect("home")
    return render(request, "registration/login.html")
```

**Protect a view:**

```python
from django.contrib.auth.decorators import login_required
from django.contrib.auth.mixins import LoginRequiredMixin
from django.views.generic import TemplateView


@login_required
def dashboard(request):
    return render(request, "dashboard.html")


class DashboardView(LoginRequiredMixin, TemplateView):
    template_name = "dashboard.html"
    login_url = "login"
```

**Custom user (recommended early):**

```python
# accounts/models.py
from django.contrib.auth.models import AbstractUser
from django.db import models


class User(AbstractUser):
    bio = models.TextField(blank=True)

# settings.py
AUTH_USER_MODEL = "accounts.User"
```

## ⚠️ Pitfalls [#️-pitfalls]

* Changing `AUTH_USER_MODEL` after migrations is painful—set it at project start.
* `authenticate` returns `None` on failure; do not leak whether username exists.
* CSRF must stay enabled on login POSTs.
* API token/JWT auth is separate—use DRF authentication classes for APIs.

## 🔗 Related [#-related]

* [django-allauth](/docs/django/django-allauth)
* [Admin](/docs/django/admin)
* [Views](/docs/django/views)
* [Built-in class-based views](/docs/django/buildin-views)
* [Settings](/docs/django/settings)
* [REST framework](/docs/django/rest-framework)


---

# Built-in class-based views (/docs/django/buildin-views)



# Built-in class-based views [#built-in-class-based-views]

*Django · Reference cheat sheet*

***

## 📋 Overview [#-overview]

Django ships generic class-based views (CBVs) for common patterns: templates, lists, details, create/update/delete, redirects, and dates. Subclass, set attributes (`model`, `queryset`, `template_name`), and wire with `as_view()` in URLconfs.

## 🔧 Core concepts [#-core-concepts]

| View                                       | Purpose                                                                |
| ------------------------------------------ | ---------------------------------------------------------------------- |
| `TemplateView`                             | Render a template with optional extra context                          |
| `RedirectView`                             | HTTP redirect                                                          |
| `ListView` / `DetailView`                  | Object collections / single object                                     |
| `CreateView` / `UpdateView` / `DeleteView` | Model form CRUD                                                        |
| `FormView`                                 | Arbitrary form                                                         |
| Date views                                 | `ArchiveIndexView`, `YearArchiveView`, …                               |
| Mixins                                     | `LoginRequiredMixin`, `PermissionRequiredMixin`, `UserPassesTestMixin` |

Lifecycle hooks: `get_queryset`, `get_context_data`, `form_valid`, `get_success_url`.

## 💡 Examples [#-examples]

**List and detail:**

```python
from django.views.generic import DetailView, ListView
from .models import Article


class ArticleListView(ListView):
    model = Article
    paginate_by = 20
    context_object_name = "articles"
    template_name = "blog/article_list.html"

    def get_queryset(self):
        return Article.objects.filter(is_published=True).select_related("author")


class ArticleDetailView(DetailView):
    model = Article
    slug_field = "slug"
    context_object_name = "article"
```

**Create with auth:**

```python
from django.contrib.auth.mixins import LoginRequiredMixin
from django.urls import reverse_lazy
from django.views.generic import CreateView
from .models import Article


class ArticleCreateView(LoginRequiredMixin, CreateView):
    model = Article
    fields = ("title", "body", "slug")
    success_url = reverse_lazy("article-list")

    def form_valid(self, form):
        form.instance.author = self.request.user
        return super().form_valid(form)
```

**URLs:**

```python
from django.urls import path
from .views import ArticleCreateView, ArticleDetailView, ArticleListView

urlpatterns = [
    path("", ArticleListView.as_view(), name="article-list"),
    path("new/", ArticleCreateView.as_view(), name="article-create"),
    path("<slug:slug>/", ArticleDetailView.as_view(), name="article-detail"),
]
```

## ⚠️ Pitfalls [#️-pitfalls]

* Forgetting `.as_view()` in `urlpatterns` passes the class, not a callable.
* `fields = "__all__"` on Create/Update can expose sensitive model fields.
* Overriding `get()` without calling `super()` drops mixin behavior.
* Multiple inheritance mixin order matters—mixins left of the base view.

## 🔗 Related [#-related]

* [Views](/docs/django/views)
* [URLs](/docs/django/urls)
* [Forms](/docs/django/forms)
* [Models](/docs/django/models)
* [Authentication](/docs/django/authentication)
* [Admin](/docs/django/admin)


---

# Caching (/docs/django/caching)



# Caching [#caching]

*Django · Reference cheat sheet*

***

## 📋 Overview [#-overview]

Django’s cache framework stores expensive results (pages, fragments, query results) behind a pluggable backend: local memory, Memcached, Redis, database, or file. Configure `CACHES`, then use the low-level API, per-view decorators, or template fragment caching.

## 🔧 Core concepts [#-core-concepts]

| Layer      | API                                                                   |
| ---------- | --------------------------------------------------------------------- |
| Low-level  | `cache.get` / `set` / `add` / `delete` / `get_or_set`                 |
| Per-view   | `@cache_page(timeout)`                                                |
| Template   | `\{% cache timeout fragment_name %\}`                                 |
| Middleware | `UpdateCacheMiddleware` + `FetchFromCacheMiddleware`                  |
| Keys       | `make_template_fragment_key`, versioning via `KEY_PREFIX` / `VERSION` |

Backends: `LocMemCache` (dev), `RedisCache` (Django 4.0+), Memcached, DB, file.

## 💡 Examples [#-examples]

**Settings (Redis):**

```python
CACHES = {
    "default": {
        "BACKEND": "django.core.cache.backends.redis.RedisCache",
        "LOCATION": "redis://127.0.0.1:6379/1",
        "KEY_PREFIX": "myapp",
        "TIMEOUT": 300,
    }
}
```

**Low-level:**

```python
from django.core.cache import cache

def get_article(slug: str):
    key = f"article:{slug}"
    article = cache.get(key)
    if article is None:
        article = Article.objects.select_related("author").get(slug=slug)
        cache.set(key, article, timeout=60 * 10)
    return article
```

**View + template fragment:**

```python
from django.views.decorators.cache import cache_page

@cache_page(60 * 15)
def homepage(request):
    ...
```

```html
{% load cache %}
{% cache 500 sidebar request.user.pk %}
  ... expensive ...
{% endcache %}
```

## ⚠️ Pitfalls [#️-pitfalls]

* Caching personalized pages without varying on user/session → data leaks.
* `LocMemCache` is per-process—wrong for multi-worker production.
* Stale cache after writes—invalidate keys in signals/services.
* Pickling large querysets—cache IDs or rendered HTML instead.
* Forgetting `KEY_PREFIX` when sharing Redis across apps.

## 🔗 Related [#-related]

* [Settings](/docs/django/settings)
* [Signals](/docs/django/signals)
* [Templates](/docs/django/templates)
* [Sessions](/docs/django/sessions)
* [Middleware](/docs/django/middleware)


---

# Context processors (/docs/django/context-processors)



# Context processors [#context-processors]

*Django · Reference cheat sheet*

***

## 📋 Overview [#-overview]

Context processors add variables to every template context for a given engine. Register callables under `TEMPLATES[i]["OPTIONS"]["context_processors"]`. Built-ins expose `request`, `user`, `perms`, `messages`, and debug info. Keep processors cheap—they run on every render.

## 🔧 Core concepts [#-core-concepts]

| Built-in           | Provides                       |
| ------------------ | ------------------------------ |
| `debug`            | `debug`, `sql_queries` (DEBUG) |
| `request`          | `request`                      |
| `auth`             | `user`, `perms`                |
| `messages`         | `messages`                     |
| `media` / `static` | `MEDIA_URL`, `STATIC_URL`      |

Signature: `def processor(request) -> dict`. Only applied when using `RequestContext` / `render()` (not bare `Context` + `Template.render` without request).

## 💡 Examples [#-examples]

**Custom processor:**

```python
# myapp/context_processors.py
from django.conf import settings


def site_meta(request):
    return {
        "SITE_NAME": getattr(settings, "SITE_NAME", "My Site"),
        "SUPPORT_EMAIL": "support@example.com",
    }
```

**Settings:**

```python
TEMPLATES = [
    {
        "BACKEND": "django.template.backends.django.DjangoTemplates",
        "DIRS": [BASE_DIR / "templates"],
        "APP_DIRS": True,
        "OPTIONS": {
            "context_processors": [
                "django.template.context_processors.debug",
                "django.template.context_processors.request",
                "django.contrib.auth.context_processors.auth",
                "django.contrib.messages.context_processors.messages",
                "myapp.context_processors.site_meta",
            ],
        },
    },
]
```

**Template usage:**

```html
<title>{{ SITE_NAME }}</title>
{% if user.is_authenticated %}
  Hi {{ user.username }}
{% endif %}
```

## ⚠️ Pitfalls [#️-pitfalls]

* DB queries in processors → N× cost on every page.
* Assuming `request` exists when rendering without `RequestContext`.
* Name collisions with view context keys (view wins or merges—avoid overlap).
* Putting secrets into global template context.
* Forgetting to add the processor path after creating it.

## 🔗 Related [#-related]

* [Templates](/docs/django/templates)
* [Settings](/docs/django/settings)
* [Messages framework](/docs/django/messages-framework)
* [Views](/docs/django/views)
* [Authentication](/docs/django/authentication)


---

# Custom admin (/docs/django/custom-admin)



# Custom admin [#custom-admin]

*Django · Reference cheat sheet*

***

## 📋 Overview [#-overview]

Customize Admin beyond basic `ModelAdmin` options: actions, custom forms/widgets, computed columns, filters, permissions, and alternate `AdminSite` instances. Keep business logic in models/services; admin should orchestrate display and safe edits.

## 🔧 Core concepts [#-core-concepts]

| Technique            | Use                                                  |
| -------------------- | ---------------------------------------------------- |
| Admin actions        | Bulk operations on selected rows                     |
| `form` / `fieldsets` | Layout and validation                                |
| `@admin.display`     | Annotated list columns (Django 3.2+)                 |
| Custom filters       | `SimpleListFilter`                                   |
| Overrides            | `save_model`, `get_queryset`, `has_*_permission`     |
| Custom templates     | `change_list_template`, app templates under `admin/` |
| Multiple sites       | Subclass `AdminSite`                                 |

## 💡 Examples [#-examples]

**Action and display:**

```python
from django.contrib import admin, messages
from .models import Article


@admin.register(Article)
class ArticleAdmin(admin.ModelAdmin):
    list_display = ("title", "word_count", "is_published")
    actions = ("publish_selected",)

    @admin.display(description="Words")
    def word_count(self, obj: Article) -> int:
        return len(obj.body.split())

    @admin.action(description="Mark selected as published")
    def publish_selected(self, request, queryset):
        updated = queryset.update(is_published=True)
        self.message_user(request, f"Published {updated} articles.", messages.SUCCESS)
```

**Restrict queryset and save:**

```python
@admin.register(Article)
class ArticleAdmin(admin.ModelAdmin):
    def get_queryset(self, request):
        qs = super().get_queryset(request)
        if request.user.is_superuser:
            return qs
        return qs.filter(author=request.user)

    def save_model(self, request, obj, form, change):
        if not change:
            obj.author = request.user
        super().save_model(request, obj, form, change)

    def has_delete_permission(self, request, obj=None):
        return request.user.is_superuser
```

**Custom list filter:**

```python
from django.contrib import admin


class PublishedFilter(admin.SimpleListFilter):
    title = "publication"
    parameter_name = "pub"

    def lookups(self, request, model_admin):
        return (("yes", "Published"), ("no", "Draft"))

    def queryset(self, request, queryset):
        if self.value() == "yes":
            return queryset.filter(is_published=True)
        if self.value() == "no":
            return queryset.filter(is_published=False)
        return queryset
```

## ⚠️ Pitfalls [#️-pitfalls]

* Admin actions should be idempotent and permission-checked.
* Custom HTML in columns needs careful escaping (`format_html`).
* Overriding `get_queryset` without `super()` can drop default optimizations.
* Heavy work in `list_display` methods runs per row—annotate in `get_queryset`.

## 🔗 Related [#-related]

* [Admin](/docs/django/admin)
* [Models](/docs/django/models)
* [Forms](/docs/django/forms)
* [Authentication](/docs/django/authentication)
* [Settings](/docs/django/settings)
* [Views](/docs/django/views)


---

# django-allauth (/docs/django/django-allauth)



# django-allauth [#django-allauth]

*Django · Reference cheat sheet*

***

## 📋 Overview [#-overview]

[django-allauth](https://docs.allauth.org/) adds local and social authentication: registration, email verification, password reset, and providers (Google, GitHub, etc.). Prefer it over rolling your own auth UI. Works with Django 4.2+/5.x; configure via settings and include its URLs.

## 🔧 Core concepts [#-core-concepts]

| Piece                         | Role                                    |
| ----------------------------- | --------------------------------------- |
| `allauth` / `allauth.account` | Email/username accounts                 |
| `allauth.socialaccount`       | OAuth providers                         |
| `ACCOUNT_*` settings          | Verification, login methods, adapters   |
| Templates                     | Override under `templates/account/`     |
| Adapters                      | Customize redirects and signup behavior |
| Headless / API                | Newer allauth headless APIs for SPAs    |

Install: `pip install django-allauth` (add provider extras as needed).

## 💡 Examples [#-examples]

**Settings sketch:**

```python
INSTALLED_APPS = [
    # Django contrib…
    "django.contrib.sites",
    "allauth",
    "allauth.account",
    "allauth.socialaccount",
    "allauth.socialaccount.providers.github",
]

SITE_ID = 1

AUTHENTICATION_BACKENDS = [
    "django.contrib.auth.backends.ModelBackend",
    "allauth.account.auth_backends.AuthenticationBackend",
]

MIDDLEWARE = [
    # …
    "allauth.account.middleware.AccountMiddleware",  # allauth 0.56+
]

LOGIN_REDIRECT_URL = "/"
ACCOUNT_LOGOUT_REDIRECT_URL = "/"
ACCOUNT_EMAIL_REQUIRED = True
ACCOUNT_EMAIL_VERIFICATION = "mandatory"  # "optional" | "none"
ACCOUNT_AUTHENTICATION_METHOD = "email"  # or "username" / "username_email"
```

**URLs:**

```python
from django.urls import include, path

urlpatterns = [
    path("accounts/", include("allauth.urls")),
]
```

**Provider settings (GitHub):**

```python
SOCIALACCOUNT_PROVIDERS = {
    "github": {
        "APP": {
            "client_id": "…",
            "secret": "…",
            "key": "",
        },
        "SCOPE": ["user:email"],
    }
}
```

Prefer env vars / `django-environ` over hardcoding secrets.

## ⚠️ Pitfalls [#️-pitfalls]

* Missing `django.contrib.sites` + `SITE_ID` breaks allauth.
* Email backend must work in production for verification (`EMAIL_HOST` or console in dev).
* Custom user models need correct `ACCOUNT_USER_MODEL_*` / adapter settings.
* Provider callback URLs must match the OAuth app config exactly (https, path).

## 🔗 Related [#-related]

* [Authentication](/docs/django/authentication)
* [Settings](/docs/django/settings)
* [URLs](/docs/django/urls)
* [Views](/docs/django/views)
* [Admin](/docs/django/admin)
* [REST framework](/docs/django/rest-framework)


---

# Email (/docs/django/email)



# Email [#email]

*Django · Reference cheat sheet*

***

## 📋 Overview [#-overview]

Send mail via `django.core.mail` using a configurable `EMAIL_BACKEND`. Dev: console or locmem; prod: SMTP or transactional providers. Prefer `EmailMessage` / `EmailMultiAlternatives` for headers, attachments, and HTML+text.

## 🔧 Core concepts [#-core-concepts]

| API                             | Role                        |
| ------------------------------- | --------------------------- |
| `send_mail`                     | Simple helper               |
| `mail_admins` / `mail_managers` | Ops alerts                  |
| `EmailMessage`                  | Full control                |
| `EmailMultiAlternatives`        | text + HTML                 |
| `get_connection`                | Explicit backend/connection |
| `EMAIL_*` settings              | Host, TLS, from address     |

Backends: SMTP, console, locmem, filebased, dummy.

## 💡 Examples [#-examples]

**Settings:**

```python
EMAIL_BACKEND = "django.core.mail.backends.smtp.EmailBackend"
EMAIL_HOST = "smtp.example.com"
EMAIL_PORT = 587
EMAIL_USE_TLS = True
EMAIL_HOST_USER = "apikey"
EMAIL_HOST_PASSWORD = env("EMAIL_PASSWORD")
DEFAULT_FROM_EMAIL = "noreply@example.com"
```

**Simple send:**

```python
from django.core.mail import send_mail

send_mail(
    subject="Welcome",
    message="Thanks for joining.",
    from_email=None,  # DEFAULT_FROM_EMAIL
    recipient_list=["user@example.com"],
    fail_silently=False,
)
```

**HTML + attachment:**

```python
from django.core.mail import EmailMultiAlternatives

msg = EmailMultiAlternatives(
    subject="Invoice",
    body="Your invoice is attached.",
    from_email="billing@example.com",
    to=["user@example.com"],
)
msg.attach_alternative("<p>Your <strong>invoice</strong> is attached.</p>", "text/html")
msg.attach("invoice.pdf", pdf_bytes, "application/pdf")
msg.send()
```

**Tests:**

```python
from django.core import mail

self.assertEqual(len(mail.outbox), 1)
self.assertEqual(mail.outbox[0].subject, "Welcome")
```

## ⚠️ Pitfalls [#️-pitfalls]

* Sending synchronously in request path—use a queue for volume.
* `fail_silently=True` hiding misconfiguration.
* Header injection via unsanitized subject/addresses.
* HTML-only mail without text alternative.
* Hardcoding credentials—use env / secrets manager.

## 🔗 Related [#-related]

* [Settings](/docs/django/settings)
* [Testing](/docs/django/testing)
* [Management commands](/docs/django/management-commands)
* [Security](/docs/django/security)
* [Authentication](/docs/django/authentication)


---

# Examples (/docs/django/examples)



# Examples [#examples]

Django notes in **Examples**.


---

# CRUD CBV (/docs/django/examples/crud-cbv)



# CRUD CBV [#crud-cbv]

*Django · Example / how-to*

***

## 📋 Overview [#-overview]

Implement list/create/update/delete for a model with Django class-based views and a simple URL conf.

## 🔧 Core concepts [#-core-concepts]

| Piece                       | Role                 |
| --------------------------- | -------------------- |
| `ListView` / `CreateView`   | Read + create        |
| `UpdateView` / `DeleteView` | Edit + remove        |
| `get_success_url`           | Post-action redirect |
| `LoginRequiredMixin`        | Gate mutations       |

## 💡 Examples [#-examples]

**models.py:**

```python
from django.db import models

class Note(models.Model):
    title = models.CharField(max_length=200)
    body = models.TextField(blank=True)
    created_at = models.DateTimeField(auto_now_add=True)

    def __str__(self) -> str:
        return self.title
```

**views.py:**

```python
from django.contrib.auth.mixins import LoginRequiredMixin
from django.urls import reverse_lazy
from django.views.generic import (
    CreateView,
    DeleteView,
    ListView,
    UpdateView,
)

from .models import Note


class NoteListView(ListView):
    model = Note
    paginate_by = 20


class NoteCreateView(LoginRequiredMixin, CreateView):
    model = Note
    fields = ["title", "body"]
    success_url = reverse_lazy("note-list")


class NoteUpdateView(LoginRequiredMixin, UpdateView):
    model = Note
    fields = ["title", "body"]
    success_url = reverse_lazy("note-list")


class NoteDeleteView(LoginRequiredMixin, DeleteView):
    model = Note
    success_url = reverse_lazy("note-list")
```

**urls.py:**

```python
from django.urls import path
from .views import NoteCreateView, NoteDeleteView, NoteListView, NoteUpdateView

urlpatterns = [
    path("", NoteListView.as_view(), name="note-list"),
    path("new/", NoteCreateView.as_view(), name="note-create"),
    path("<int:pk>/edit/", NoteUpdateView.as_view(), name="note-update"),
    path("<int:pk>/delete/", NoteDeleteView.as_view(), name="note-delete"),
]
```

## ⚠️ Pitfalls [#️-pitfalls]

* CBVs need templates named `<app>/<model>_form.html` etc. unless you set `template_name`.
* Always authorize object ownership for multi-user data — mixin alone is not enough.
* Prefer `reverse_lazy` at class level; `reverse` needs a request/ready URLconf.

## 🔗 Related [#-related]

* [DRF list create](/docs/django/examples/drf-list-create)
* [Custom user snippet](/docs/django/examples/custom-user-snippet)


---

# Custom User Snippet (/docs/django/examples/custom-user-snippet)



# Custom User Snippet [#custom-user-snippet]

*Django · Example / how-to*

***

## 📋 Overview [#-overview]

Swap in a custom user model early with email as the username field using `AbstractUser` and `AUTH_USER_MODEL`.

## 🔧 Core concepts [#-core-concepts]

| Piece             | Role                            |
| ----------------- | ------------------------------- |
| `AbstractUser`    | Keep Django auth features       |
| `USERNAME_FIELD`  | Login identifier                |
| `AUTH_USER_MODEL` | Project-wide reference          |
| Migrations        | Must exist before first migrate |

## 💡 Examples [#-examples]

**accounts/models.py:**

```python
from django.contrib.auth.models import AbstractUser
from django.db import models


class User(AbstractUser):
    email = models.EmailField(unique=True)

    USERNAME_FIELD = "email"
    REQUIRED_FIELDS = ["username"]

    def __str__(self) -> str:
        return self.email
```

**settings.py:**

```python
INSTALLED_APPS = [
    # ...
    "accounts",
    "django.contrib.auth",
    # ...
]

AUTH_USER_MODEL = "accounts.User"
```

**Foreign keys elsewhere:**

```python
from django.conf import settings
from django.db import models

class Profile(models.Model):
    user = models.OneToOneField(
        settings.AUTH_USER_MODEL,
        on_delete=models.CASCADE,
        related_name="profile",
    )
    bio = models.TextField(blank=True)
```

## ⚠️ Pitfalls [#️-pitfalls]

* Changing `AUTH_USER_MODEL` after migrations is painful — set it on day one.
* Always reference `settings.AUTH_USER_MODEL`, never hard-code `auth.User`.
* Admin / forms may need a custom user admin when fields change.

## 🔗 Related [#-related]

* [CRUD CBV](/docs/django/examples/crud-cbv)
* [DRF list create](/docs/django/examples/drf-list-create)


---

# DRF List Create (/docs/django/examples/drf-list-create)



# DRF List Create [#drf-list-create]

*Django · Example / how-to*

***

## 📋 Overview [#-overview]

Expose a JSON list + create endpoint with Django REST Framework `ListCreateAPIView` and a ModelSerializer.

## 🔧 Core concepts [#-core-concepts]

| Piece               | Role                  |
| ------------------- | --------------------- |
| `ModelSerializer`   | Map model ↔ JSON      |
| `ListCreateAPIView` | GET list, POST create |
| Permissions         | Who can write         |
| Pagination          | Bound list size       |

## 💡 Examples [#-examples]

**serializers.py:**

```python
from rest_framework import serializers
from .models import Note


class NoteSerializer(serializers.ModelSerializer):
    class Meta:
        model = Note
        fields = ["id", "title", "body", "created_at"]
        read_only_fields = ["id", "created_at"]
```

**views.py:**

```python
from rest_framework import generics, permissions
from .models import Note
from .serializers import NoteSerializer


class NoteListCreateView(generics.ListCreateAPIView):
    queryset = Note.objects.all().order_by("-created_at")
    serializer_class = NoteSerializer
    permission_classes = [permissions.IsAuthenticatedOrReadOnly]

    def perform_create(self, serializer):
        serializer.save()
```

**urls.py:**

```python
from django.urls import path
from .views import NoteListCreateView

urlpatterns = [
    path("api/notes/", NoteListCreateView.as_view(), name="api-note-list"),
]
```

**curl:**

```shellscript
curl -s http://127.0.0.1:8000/api/notes/
curl -s -X POST http://127.0.0.1:8000/api/notes/ \
  -H "Content-Type: application/json" \
  -H "Authorization: Token <token>" \
  -d '{"title":"Hello","body":"World"}'
```

## ⚠️ Pitfalls [#️-pitfalls]

* Default permissions may be wide open — set project defaults in settings.
* N+1 queries: use `select_related` / `prefetch_related` on the queryset.
* Validation errors return 400 with field keys — match your client expectations.

## 🔗 Related [#-related]

* [CRUD CBV](/docs/django/examples/crud-cbv)
* [Custom user snippet](/docs/django/examples/custom-user-snippet)


---

# File uploads (/docs/django/file-uploads)



# File uploads [#file-uploads]

*Django · Reference cheat sheet*

***

## 📋 Overview [#-overview]

Uploaded files arrive on `request.FILES`. Model `FileField` / `ImageField` store paths under `MEDIA_ROOT` and expose URLs via `MEDIA_URL`. Validate size/type; never trust client filenames. In production, serve media from object storage or a dedicated host—not the app process.

## 🔧 Core concepts [#-core-concepts]

| Piece                           | Role                          |
| ------------------------------- | ----------------------------- |
| `request.FILES`                 | Uploaded file mapping         |
| `FileField` / `ImageField`      | Model storage                 |
| `enctype="multipart/form-data"` | Required on forms             |
| `upload_to`                     | Path callable / string        |
| `STORAGES["default"]`           | Backend (local, S3, …)        |
| Validators                      | Size, extension, content-type |

Temporary files use `TemporaryUploadedFile` above `FILE_UPLOAD_MAX_MEMORY_SIZE`.

## 💡 Examples [#-examples]

**Model + form:**

```python
import uuid
from pathlib import Path

from django.db import models


def avatar_path(instance, filename):
    ext = Path(filename).suffix.lower()
    return f"avatars/{instance.pk}/{uuid.uuid4().hex}{ext}"


class Profile(models.Model):
    user = models.OneToOneField("auth.User", on_delete=models.CASCADE)
    avatar = models.ImageField(upload_to=avatar_path, blank=True)
```

```python
from django import forms


class ProfileForm(forms.ModelForm):
    class Meta:
        model = Profile
        fields = ("avatar",)
```

**View:**

```python
def upload_avatar(request):
    if request.method == "POST":
        form = ProfileForm(request.POST, request.FILES, instance=request.user.profile)
        if form.is_valid():
            form.save()
            return redirect("profile")
    else:
        form = ProfileForm(instance=request.user.profile)
    return render(request, "upload.html", {"form": form})
```

```html
<form method="post" enctype="multipart/form-data">
  {% csrf_token %}
  {{ form.as_p }}
  <button type="submit">Upload</button>
</form>
```

**Settings:**

```python
MEDIA_URL = "/media/"
MEDIA_ROOT = BASE_DIR / "media"
DATA_UPLOAD_MAX_MEMORY_SIZE = 5 * 1024 * 1024
```

## ⚠️ Pitfalls [#️-pitfalls]

* Missing `enctype` → empty `request.FILES`.
* Serving user uploads as static executable content → XSS/RCE risk.
* Trusting `Content-Type` / extension alone—inspect content when needed.
* Not deleting old files on replace (storage orphans).
* Huge uploads without size limits or streaming.

## 🔗 Related [#-related]

* [Forms](/docs/django/forms)
* [Models](/docs/django/models)
* [Validators](/docs/django/validators)
* [Static files](/docs/django/static-files)
* [Security](/docs/django/security)
* [Settings](/docs/django/settings)


---

# Forms (/docs/django/forms)



# Forms [#forms]

*Django · Reference cheat sheet*

***

## 📋 Overview [#-overview]

Django forms validate and clean user input, then render HTML widgets. Use `Form` for unbound data and `ModelForm` to map model fields. Always validate with `is_valid()` and read `cleaned_data`—never raw `request.POST` for business logic.

## 🔧 Core concepts [#-core-concepts]

| Piece                     | Role                                 |
| ------------------------- | ------------------------------------ |
| `forms.Form`              | Standalone fields + validation       |
| `forms.ModelForm`         | Bound to a model                     |
| `clean_<field>` / `clean` | Field-level / cross-field validation |
| Widgets                   | Control HTML input types             |
| CSRF                      | `\{% csrf_token %\}` on POST forms   |
| Formsets                  | Edit collections of forms            |

Bound form: data from request; unbound: empty for GET display.

## 💡 Examples [#-examples]

**ModelForm:**

```python
from django import forms
from .models import Article


class ArticleForm(forms.ModelForm):
    class Meta:
        model = Article
        fields = ("title", "slug", "body")
        widgets = {
            "body": forms.Textarea(attrs={"rows": 10}),
        }

    def clean_title(self):
        title = self.cleaned_data["title"].strip()
        if len(title) < 3:
            raise forms.ValidationError("Title too short.")
        return title
```

**View usage:**

```python
from django.shortcuts import redirect, render
from .forms import ArticleForm


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)
    else:
        form = ArticleForm()
    return render(request, "blog/article_form.html", {"form": form})
```

**Template:**

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

## ⚠️ Pitfalls [#️-pitfalls]

* Skipping CSRF on POSTs yields 403.
* `form.save()` on invalid forms is wrong—check `is_valid()` first.
* `fields = "__all__"` may expose fields you did not intend.
* File uploads need `request.FILES` and `enctype="multipart/form-data"`.

## 🔗 Related [#-related]

* [Views](/docs/django/views)
* [Models](/docs/django/models)
* [Built-in class-based views](/docs/django/buildin-views)
* [Admin](/docs/django/admin)
* [Serializers](/docs/django/serializers)
* [Authentication](/docs/django/authentication)


---

# Glossary (/docs/django/glossary)



# Glossary [#glossary]

*Django · Reference cheat sheet*

***

## 📋 Overview [#-overview]

Alphabetical glossary of core Django terms for models, views, templates, auth, and the request/response cycle.

## 🔧 Core concepts [#-core-concepts]

| Term               | Definition                                                                |
| ------------------ | ------------------------------------------------------------------------- |
| Admin              | The built-in CRUD interface generated from registered models.             |
| App                | A self-contained Django package under `INSTALLED_APPS` with models/views. |
| ASGI               | Async server gateway interface used for async Django and Channels.        |
| Authentication     | Verifying who a user is, typically via sessions or tokens.                |
| CSRF               | Cross-Site Request Forgery protection built into Django forms/middleware. |
| Field              | A model attribute that maps to a database column and validation rules.    |
| Form               | A class that validates and cleans submitted input data.                   |
| Management command | A CLI task run with `python manage.py <command>`.                         |
| Manager            | The interface attached to models for building QuerySets (`objects`).      |
| Middleware         | Hooks that process requests and responses globally.                       |
| Migration          | A versioned schema change applied to the database.                        |
| Model              | A Python class mapping to a database table via the ORM.                   |
| MVT                | Model–View–Template, Django’s primary architectural pattern.              |
| ORM                | Object-relational mapper that turns model APIs into SQL.                  |
| Permission         | A flag controlling whether a user may perform an action.                  |
| Project            | The top-level Django configuration package created by `startproject`.     |
| QuerySet           | A lazy, chainable representation of a database query.                     |
| Request            | The `HttpRequest` object representing an incoming HTTP call.              |
| Serializer         | DRF component that converts models to/from JSON (and validates).          |
| Settings           | Project configuration module (`settings.py` / env-driven values).         |
| Signal             | A dispatcher that notifies receivers when certain actions occur.          |
| Static files       | CSS, JS, and images collected and served separately from media uploads.   |
| Template           | An HTML file with Django template language tags and filters.              |
| URLConf            | The URL routing table mapping paths to views.                             |
| View               | A callable that takes a request and returns a response.                   |
| WSGI               | Web Server Gateway Interface used by traditional sync Django deploys.     |

## 💡 Examples [#-examples]

**Model and QuerySet:**

```python
class Post(models.Model):
    title = models.CharField(max_length=200)
    published = models.BooleanField(default=False)

Post.objects.filter(published=True).order_by("-id")
```

**URL and view:**

```python
# urls.py
path("posts/<int:pk>/", views.post_detail, name="post_detail")

# views.py
def post_detail(request, pk):
    post = get_object_or_404(Post, pk=pk)
    return render(request, "posts/detail.html", {"post": post})
```

**Migration workflow:**

```shellscript
python manage.py makemigrations
python manage.py migrate
```

## ⚠️ Pitfalls [#️-pitfalls]

* Confusing **project** (site config) with **app** (feature package).
* Mixing **Form** validation with **Serializer** validation in DRF projects.
* Treating a **QuerySet** as immediately executed — it is lazy until evaluated.
* Equating **authentication** (who you are) with **permission** (what you may do).
* Assuming **static files** and **media uploads** are the same storage path.

## 🔗 Related [#-related]

* [models](/docs/django/models)
* [views](/docs/django/views)
* [templates](/docs/django/templates)
* [queryset](/docs/django/queryset)
* [authentication](/docs/django/authentication)
* [migrations](/docs/django/migrations)


---

# i18n (/docs/django/i18n)



# i18n [#i18n]

*Django · Reference cheat sheet*

***

## 📋 Overview [#-overview]

Internationalization (i18n) marks strings for translation; localization (l10n) formats dates, numbers, and timezones. Use `gettext` / `gettext_lazy`, `\{% translate %\}`, and `makemessages` / `compilemessages`. Activate languages via middleware, URLs, or explicit `activate()`.

## 🔧 Core concepts [#-core-concepts]

| Piece                           | Role                                   |
| ------------------------------- | -------------------------------------- |
| `gettext` / `_`                 | Eager translate                        |
| `gettext_lazy` / `gettext_noop` | Lazy / mark only                       |
| `ngettext`                      | Plurals                                |
| `LocaleMiddleware`              | Per-request language                   |
| `LANGUAGE_CODE` / `LANGUAGES`   | Defaults / available                   |
| `USE_I18N` / `USE_L10N`         | Feature flags (L10N always on in 5.0+) |
| `makemessages`                  | Build `.po`                            |
| `compilemessages`               | `.po` → `.mo`                          |

Timezone: `USE_TZ = True`, `timezone.now()`, activate user TZ in middleware.

## 💡 Examples [#-examples]

**Python:**

```python
from django.utils.translation import gettext as _
from django.utils.translation import gettext_lazy as _lazy
from django.db import models


class Article(models.Model):
    title = models.CharField(_lazy("Title"), max_length=200)


def greet(name: str) -> str:
    return _("Hello, %(name)s") % {"name": name}
```

**Template:**

```html
{% load i18n %}
{% translate "Welcome" %}
{% blocktranslate count counter=items|length %}
  One item
{% plural %}
  {{ counter }} items
{% endblocktranslate %}
<form action="{% url 'set_language' %}" method="post">
  {% csrf_token %}
  <input name="language" value="es">
  <button type="submit">ES</button>
</form>
```

**Commands / settings:**

```python
LANGUAGE_CODE = "en-us"
LANGUAGES = [("en", "English"), ("es", "Spanish")]
LOCALE_PATHS = [BASE_DIR / "locale"]
MIDDLEWARE = [
    ...
    "django.middleware.locale.LocaleMiddleware",
    ...
]
```

```shellscript
django-admin makemessages -l es
django-admin compilemessages
```

## ⚠️ Pitfalls [#️-pitfalls]

* Using `_()` at module import for model `verbose_name`—prefer `_lazy`.
* Interpolating before translating breaks message catalogs.
* Missing `LocaleMiddleware` order (after Session, before Common).
* Forgetting to compile `.mo` in deploy.
* Hardcoded date formats—use `localize` / `date` filters.

## 🔗 Related [#-related]

* [Settings](/docs/django/settings)
* [Templates](/docs/django/templates)
* [Middleware](/docs/django/middleware)
* [Models](/docs/django/models)
* [URLs](/docs/django/urls)


---

# Management commands (/docs/django/management-commands)



# Management commands [#management-commands]

*Django · Reference cheat sheet*

***

## 📋 Overview [#-overview]

Custom `manage.py` commands live under `app/management/commands/`. Subclass `BaseCommand`, implement `handle`, and optionally declare arguments. Use for one-off jobs, cron entrypoints, data backfills, and ops scripts that need Django setup.

## 🔧 Core concepts [#-core-concepts]

| Piece                         | Role                            |
| ----------------------------- | ------------------------------- |
| `BaseCommand`                 | Base class                      |
| `add_arguments`               | `argparse`-style options        |
| `handle(*args, **options)`    | Main body                       |
| `self.stdout` / `self.stderr` | Output (respects `--verbosity`) |
| `CommandError`                | Abort with non-zero exit        |
| `requires_system_checks`      | Skip/alter system checks        |
| `help`                        | Shown in `manage.py help`       |

Package layout: `management/__init__.py`, `commands/__init__.py`, `commands/my_cmd.py`.

## 💡 Examples [#-examples]

**Command with args:**

```python
# blog/management/commands/publish_drafts.py
from django.core.management.base import BaseCommand, CommandError
from django.utils import timezone

from blog.models import Article


class Command(BaseCommand):
    help = "Publish draft articles older than N days"

    def add_arguments(self, parser):
        parser.add_argument("--days", type=int, default=7)
        parser.add_argument("--dry-run", action="store_true")

    def handle(self, *args, **options):
        days = options["days"]
        cutoff = timezone.now() - timezone.timedelta(days=days)
        qs = Article.objects.filter(is_published=False, created_at__lte=cutoff)
        count = qs.count()
        if options["dry_run"]:
            self.stdout.write(f"Would publish {count} articles")
            return
        updated = qs.update(is_published=True, published_at=timezone.now())
        self.stdout.write(self.style.SUCCESS(f"Published {updated}"))
```

**Run:**

```shellscript
python manage.py publish_drafts --days 3 --dry-run
python manage.py publish_drafts --days 3
```

**Call from code:**

```python
from django.core.management import call_command

call_command("publish_drafts", days=3, verbosity=0)
```

## ⚠️ Pitfalls [#️-pitfalls]

* Missing `__init__.py` files → command not discovered.
* Doing HTTP/request work without timeouts in long jobs.
* Not wrapping multi-step DB work in `transaction.atomic()`.
* Printing secrets at high verbosity.
* Blocking the web process—run via cron/worker, not inside a view.

## 🔗 Related [#-related]

* [Models](/docs/django/models)
* [Migrations](/docs/django/migrations)
* [Transactions / ORM](/docs/django/transactions-orm)
* [Settings](/docs/django/settings)
* [Testing](/docs/django/testing)


---

# Managers (/docs/django/managers)



# Managers [#managers]

*Django · Reference cheat sheet*

***

## 📋 Overview [#-overview]

Managers are the interface for table-level ORM operations (`Model.objects`). Customize to encapsulate reusable querysets (published, for\_user, …). Prefer chaining a custom `QuerySet` class and exposing it via `as_manager()` or `from_queryset`.

## 🔧 Core concepts [#-core-concepts]

| Piece             | Role                            |
| ----------------- | ------------------------------- |
| `models.Manager`  | Default `objects`               |
| Custom `Manager`  | Extra methods / default filters |
| Custom `QuerySet` | Chainable filters               |
| `as_manager()`    | Manager from QuerySet           |
| `from_queryset`   | Manager + QuerySet methods      |
| Multiple managers | e.g. `objects`, `published`     |

Related managers on reverse FK/M2M are separate (`article.tags`).

## 💡 Examples [#-examples]

**QuerySet + manager:**

```python
from django.db import models


class ArticleQuerySet(models.QuerySet):
    def published(self):
        return self.filter(is_published=True)

    def by_author(self, user):
        return self.filter(author=user)


class Article(models.Model):
    title = models.CharField(max_length=200)
    is_published = models.BooleanField(default=False)
    author = models.ForeignKey("auth.User", on_delete=models.CASCADE)

    objects = ArticleQuerySet.as_manager()
```

```python
Article.objects.published().by_author(request.user)
```

**Manager with default filter (use carefully):**

```python
class PublishedManager(models.Manager):
    def get_queryset(self):
        return super().get_queryset().filter(is_published=True)


class Article(models.Model):
    ...
    objects = models.Manager()
    published = PublishedManager()
```

**from\_queryset:**

```python
ArticleManager = models.Manager.from_queryset(ArticleQuerySet)

class Article(models.Model):
    objects = ArticleManager()
```

## ⚠️ Pitfalls [#️-pitfalls]

* Overriding `get_queryset` on the only manager hides rows in admin—keep a full `objects`.
* Soft-delete managers that break FK integrity unexpectedly.
* Putting create/update business logic only on managers without tests.
* Forgetting related name clashes when adding managers.
* Custom managers not used by `RelatedManager`—re-declare with `base_manager_name` / `default_manager_name` in Meta when needed.

## 🔗 Related [#-related]

* [Models](/docs/django/models)
* [QuerySet](/docs/django/queryset)
* [Admin](/docs/django/admin)
* [Transactions / ORM](/docs/django/transactions-orm)
* [Testing](/docs/django/testing)


---

# Messages framework (/docs/django/messages-framework)



# Messages framework [#messages-framework]

*Django · Reference cheat sheet*

***

## 📋 Overview [#-overview]

`django.contrib.messages` stores one-time flash messages (success, error, …) for the next request, typically in the session (or cookie). Add message in a view; display in the base template. Requires `MessageMiddleware` and the messages context processor.

## 🔧 Core concepts [#-core-concepts]

| Level   | Constant / tag     |
| ------- | ------------------ |
| Debug   | `messages.DEBUG`   |
| Info    | `messages.INFO`    |
| Success | `messages.SUCCESS` |
| Warning | `messages.WARNING` |
| Error   | `messages.ERROR`   |

APIs: `messages.add_message`, shortcuts `success` / `error` / `warning` / `info` / `debug`. Storage backends: session (default), cookie, fallback.

## 💡 Examples [#-examples]

**View:**

```python
from django.contrib import messages
from django.shortcuts import redirect


def save_article(request):
    form = ArticleForm(request.POST)
    if form.is_valid():
        form.save()
        messages.success(request, "Article saved.")
        return redirect("article-list")
    messages.error(request, "Please fix the errors below.")
    return render(request, "form.html", {"form": form})
```

**Template:**

```html
{% if messages %}
  <ul class="messages">
    {% for message in messages %}
      <li class="{{ message.tags }}">{{ message }}</li>
    {% endfor %}
  </ul>
{% endif %}
```

**Settings extras:**

```python
from django.contrib.messages import constants as message_constants

MESSAGE_LEVEL = message_constants.INFO
MESSAGE_TAGS = {
    message_constants.ERROR: "danger",  # Bootstrap-friendly
}
```

## ⚠️ Pitfalls [#️-pitfalls]

* Missing middleware / context processor → messages never show.
* Consuming messages twice (iterate in view and template).
* Cookie storage size limits for long messages.
* AJAX flows need JSON/toast handling—session flashes suit full page loads.
* Setting messages then returning the same template without redirect can work, but PRG (Post/Redirect/Get) avoids resubmit issues.

## 🔗 Related [#-related]

* [Sessions](/docs/django/sessions)
* [Middleware](/docs/django/middleware)
* [Context processors](/docs/django/context-processors)
* [Views](/docs/django/views)
* [Templates](/docs/django/templates)


---

# Middleware (/docs/django/middleware)



# Middleware [#middleware]

*Django · Reference cheat sheet*

***

## 📋 Overview [#-overview]

Middleware is a hook into Django’s request/response cycle. Each class can process the request before the view, the response after, and exceptions. Order in `MIDDLEWARE` matters—security and session middleware run early; custom logic usually sits after auth.

## 🔧 Core concepts [#-core-concepts]

| Piece                       | Role                                                     |
| --------------------------- | -------------------------------------------------------- |
| `__init__(get_response)`    | Store next callable (Django 1.10+ style)                 |
| `__call__(request)`         | Process request → call `get_response` → process response |
| `process_view`              | After URL resolve, before view (optional)                |
| `process_exception`         | On view exception (optional)                             |
| `process_template_response` | For `TemplateResponse` (optional)                        |
| Short-circuit               | Return `HttpResponse` without calling `get_response`     |

Built-ins: `SecurityMiddleware`, `SessionMiddleware`, `CommonMiddleware`, `CsrfViewMiddleware`, `AuthenticationMiddleware`, `MessageMiddleware`, `XFrameOptionsMiddleware`.

## 💡 Examples [#-examples]

**Simple timing middleware:**

```python
import time
from django.utils.deprecation import MiddlewareMixin  # avoid if possible


class TimingMiddleware:
    def __init__(self, get_response):
        self.get_response = get_response

    def __call__(self, request):
        start = time.perf_counter()
        response = self.get_response(request)
        elapsed_ms = (time.perf_counter() - start) * 1000
        response["X-Request-Duration-Ms"] = f"{elapsed_ms:.1f}"
        return response
```

**Settings:**

```python
MIDDLEWARE = [
    "django.middleware.security.SecurityMiddleware",
    "django.contrib.sessions.middleware.SessionMiddleware",
    "django.middleware.common.CommonMiddleware",
    "django.middleware.csrf.CsrfViewMiddleware",
    "django.contrib.auth.middleware.AuthenticationMiddleware",
    "django.contrib.messages.middleware.MessageMiddleware",
    "django.middleware.clickjacking.XFrameOptionsMiddleware",
    "myapp.middleware.TimingMiddleware",
]
```

**Short-circuit (maintenance mode):**

```python
from django.http import HttpResponse

class MaintenanceMiddleware:
    def __init__(self, get_response):
        self.get_response = get_response

    def __call__(self, request):
        if getattr(settings, "MAINTENANCE_MODE", False):
            return HttpResponse("Down for maintenance", status=503)
        return self.get_response(request)
```

## ⚠️ Pitfalls [#️-pitfalls]

* Wrong order: CSRF after session; auth after session.
* Mutating `request` after the view has run does nothing useful for that response.
* Heavy work in middleware hits every request—cache or skip static paths.
* Prefer new-style `__call__` over deprecated `MiddlewareMixin` patterns.
* Async views need async-capable middleware on ASGI (Django 4.1+).

## 🔗 Related [#-related]

* [Settings](/docs/django/settings)
* [Views](/docs/django/views)
* [Sessions](/docs/django/sessions)
* [Security](/docs/django/security)
* [Authentication](/docs/django/authentication)
* [ASGI / Channels](/docs/django/asgi-channels)


---

# Migrations (/docs/django/migrations)



# Migrations [#migrations]

*Django · Reference cheat sheet*

***

## 📋 Overview [#-overview]

Migrations version the database schema to match models. Workflow: change models → `makemigrations` → review → `migrate`. Keep migrations in VCS; never edit applied production migrations casually—add a new one instead.

## 🔧 Core concepts [#-core-concepts]

| Command            | Role                                                           |
| ------------------ | -------------------------------------------------------------- |
| `makemigrations`   | Detect model changes, write migration files                    |
| `migrate`          | Apply / unapply migrations                                     |
| `showmigrations`   | Status per app                                                 |
| `sqlmigrate`       | Preview SQL                                                    |
| `squashmigrations` | Collapse history                                               |
| Operations         | `CreateModel`, `AddField`, `AlterField`, `RunPython`, `RunSQL` |

Dependencies graph ensures order across apps. Data migrations use `RunPython` / `RunPython.noop`.

## 💡 Examples [#-examples]

**Generate and apply:**

```shellscript
python manage.py makemigrations blog
python manage.py migrate
python manage.py showmigrations blog
python manage.py sqlmigrate blog 0003
```

**Data migration:**

```python
from django.db import migrations


def forwards(apps, schema_editor):
    Article = apps.get_model("blog", "Article")
    Article.objects.filter(slug="").update(slug="untitled")


def backwards(apps, schema_editor):
    pass


class Migration(migrations.Migration):
    dependencies = [("blog", "0002_article_slug")]

    operations = [
        migrations.RunPython(forwards, backwards),
    ]
```

**Fake / zero (careful):**

```shellscript
python manage.py migrate blog 0002
python manage.py migrate blog zero          # unapply all for app
python manage.py migrate blog 0003 --fake   # mark applied, no SQL
```

## ⚠️ Pitfalls [#️-pitfalls]

* Using real model classes in `RunPython`—always `apps.get_model`.
* Renaming models/fields without `RenameModel` / `RenameField` drops and recreates data.
* Circular dependencies between apps—split or use `SeparateDatabaseAndState`.
* Editing already-deployed migrations breaks other environments.
* Long locks on large `AlterField`—plan downtime or online schema tools.

## 🔗 Related [#-related]

* [Models](/docs/django/models)
* [Managers](/docs/django/managers)
* [QuerySet](/docs/django/queryset)
* [Settings](/docs/django/settings)
* [Testing](/docs/django/testing)


---

# Models (/docs/django/models)



# Models [#models]

*Django · Reference cheat sheet*

***

## 📋 Overview [#-overview]

Models define database tables via Django ORM. Subclass `django.db.models.Model`, declare fields and `Meta`, then `makemigrations` / `migrate`. Prefer explicit `related_name`, indexes for common filters, and `get_absolute_url` for object links.

## 🔧 Core concepts [#-core-concepts]

| Piece      | Role                                                                         |
| ---------- | ---------------------------------------------------------------------------- |
| Fields     | `CharField`, `TextField`, `ForeignKey`, `ManyToManyField`, …                 |
| Options    | `null`, `blank`, `default`, `unique`, `db_index`                             |
| Relations  | FK / M2M / O2O with `on_delete`                                              |
| Managers   | `objects` / custom `Manager` + `QuerySet`                                    |
| Meta       | `ordering`, `constraints`, `indexes`, `unique_together` → `UniqueConstraint` |
| Migrations | Schema evolution under `migrations/`                                         |

`null` is DB-level; `blank` is validation/forms-level.

## 💡 Examples [#-examples]

**Typical model:**

```python
from django.conf import settings
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(
        settings.AUTH_USER_MODEL,
        on_delete=models.CASCADE,
        related_name="articles",
    )
    is_published = models.BooleanField(default=False)
    published_at = models.DateTimeField(null=True, blank=True)
    created_at = models.DateTimeField(auto_now_add=True)
    updated_at = models.DateTimeField(auto_now=True)

    class Meta:
        ordering = ("-published_at", "-pk")
        indexes = [
            models.Index(fields=["is_published", "published_at"]),
        ]

    def __str__(self) -> str:
        return self.title

    def get_absolute_url(self):
        return reverse("article-detail", kwargs={"slug": self.slug})
```

**Query patterns:**

```python
Article.objects.filter(is_published=True).select_related("author")
Article.objects.prefetch_related("tags")
Article.objects.get(slug="hello")
```

## ⚠️ Pitfalls [#️-pitfalls]

* `CharField(null=True)` usually wrong—use `blank=True` + default `""`.
* Missing `on_delete` on `ForeignKey` is required.
* N+1 queries: use `select_related` / `prefetch_related`.
* Changing fields without migrations desyncs DB and code.
* Avoid circular imports—use string references `"app.Model"`.

## 🔗 Related [#-related]

* [Admin](/docs/django/admin)
* [Forms](/docs/django/forms)
* [Serializers](/docs/django/serializers)
* [Views](/docs/django/views)
* [Settings](/docs/django/settings)
* [REST framework](/docs/django/rest-framework)


---

# Pagination (/docs/django/pagination)



# Pagination [#pagination]

*Django · Reference cheat sheet*

***

## 📋 Overview [#-overview]

`Paginator` splits a queryset or sequence into pages. Use in function views, `ListView` (`paginate_by`), or DRF pagination classes. Prefer queryset pagination (LIMIT/OFFSET or keyset) over loading all rows into memory.

## 🔧 Core concepts [#-core-concepts]

| Piece                                            | Role          |
| ------------------------------------------------ | ------------- |
| `Paginator(object_list, per_page)`               | Build pages   |
| `page(number)`                                   | `Page` object |
| `Page.object_list`                               | Items on page |
| `has_next` / `has_previous`                      | Navigation    |
| `ListView.paginate_by`                           | CBV helper    |
| `InvalidPage` / `EmptyPage` / `PageNotAnInteger` | Errors        |

DRF: `PageNumberPagination`, `LimitOffsetPagination`, `CursorPagination`.

## 💡 Examples [#-examples]

**Function view:**

```python
from django.core.paginator import Paginator, EmptyPage, PageNotAnInteger
from django.shortcuts import render

from .models import Article


def article_list(request):
    qs = Article.objects.filter(is_published=True).order_by("-pk")
    paginator = Paginator(qs, 20)
    page_number = request.GET.get("page")
    try:
        page = paginator.page(page_number)
    except PageNotAnInteger:
        page = paginator.page(1)
    except EmptyPage:
        page = paginator.page(paginator.num_pages)
    return render(request, "blog/list.html", {"page": page})
```

**ListView:**

```python
from django.views.generic import ListView


class ArticleList(ListView):
    model = Article
    paginate_by = 20
    queryset = Article.objects.filter(is_published=True)
```

**Template:**

```html
{% for article in page %}
  <li>{{ article.title }}</li>
{% endfor %}
{% if page.has_previous %}
  <a href="?page={{ page.previous_page_number }}">Prev</a>
{% endif %}
{% if page.has_next %}
  <a href="?page={{ page.next_page_number }}">Next</a>
{% endif %}
```

## ⚠️ Pitfalls [#️-pitfalls]

* Unordered querysets → unstable pages; always `order_by`.
* Large OFFSET is slow—consider cursor/keyset pagination for deep pages.
* Counting huge tables on every request—cache `count` or approximate.
* Mixing GET filters without preserving querystring in page links.
* Evaluating full queryset before paginating.

## 🔗 Related [#-related]

* [QuerySet](/docs/django/queryset)
* [Views](/docs/django/views)
* [Templates](/docs/django/templates)
* [REST framework](/docs/django/rest-framework)
* [buildin\_views](/docs/django/buildin-views)


---

# Permissions (/docs/django/permissions)



# Permissions [#permissions]

*Django · Reference cheat sheet*

***

## 📋 Overview [#-overview]

Django auth permissions are `app_label.codename` triples (add/change/delete/view plus custom). Attach via groups or directly on users. Enforce with mixins, decorators, `user.has_perm`, or DRF permission classes. Object-level rules need custom checks or packages like django-guardian.

## 🔧 Core concepts [#-core-concepts]

| Piece                                            | Role                                    |
| ------------------------------------------------ | --------------------------------------- |
| Default perms                                    | Auto-created per model                  |
| Custom                                           | `Meta.permissions` or `Permission` rows |
| Groups                                           | Bundle permissions                      |
| `user_passes_test` / `permission_required`       | Function views                          |
| `PermissionRequiredMixin` / `LoginRequiredMixin` | CBVs                                    |
| `has_perm` / `has_module_perms`                  | Checks                                  |

Superusers pass all permission checks.

## 💡 Examples [#-examples]

**Model custom permission:**

```python
class Article(models.Model):
    ...

    class Meta:
        permissions = [
            ("publish_article", "Can publish article"),
        ]
```

**CBV:**

```python
from django.contrib.auth.mixins import LoginRequiredMixin, PermissionRequiredMixin
from django.views.generic import UpdateView

from .models import Article


class ArticleUpdate(LoginRequiredMixin, PermissionRequiredMixin, UpdateView):
    model = Article
    fields = ("title", "body")
    permission_required = "blog.change_article"
```

**Function view + check:**

```python
from django.contrib.auth.decorators import login_required, permission_required


@login_required
@permission_required("blog.publish_article", raise_exception=True)
def publish(request, pk):
    ...
```

**Programmatic:**

```python
from django.contrib.auth.models import Permission
from django.contrib.contenttypes.models import ContentType

ct = ContentType.objects.get_for_model(Article)
perm = Permission.objects.get(codename="publish_article", content_type=ct)
user.user_permissions.add(perm)
user.has_perm("blog.publish_article")
```

## ⚠️ Pitfalls [#️-pitfalls]

* Checking only authentication, not authorization.
* Caching stale `user.user_permissions` after changes in the same request—refetch or clear cache.
* Object ownership ≠ model permission—implement explicitly.
* DRF: browser session auth vs token auth need matching permission classes.
* Forgetting `raise_exception=True` → silent redirect to login on 403 cases.

## 🔗 Related [#-related]

* [Authentication](/docs/django/authentication)
* [Views](/docs/django/views)
* [REST framework](/docs/django/rest-framework)
* [Testing](/docs/django/testing)
* [Security](/docs/django/security)


---

# QuerySet (/docs/django/queryset)



# QuerySet [#queryset]

*Django · Reference cheat sheet*

***

## 📋 Overview [#-overview]

A `QuerySet` is a lazy, chainable representation of a DB query. Evaluation happens on iteration, `list()`, `bool()`, slicing with step, or terminal methods (`get`, `count`, `exists`, `aggregate`). Optimize with `select_related` / `prefetch_related` and defer unused fields.

## 🔧 Core concepts [#-core-concepts]

| API                                             | Role                                |
| ----------------------------------------------- | ----------------------------------- |
| `filter` / `exclude`                            | Narrow rows (`Q` for OR/complex)    |
| `get`                                           | Exactly one row or raise            |
| `create` / `get_or_create` / `update_or_create` | Write helpers                       |
| `annotate` / `aggregate`                        | Per-row / whole-set aggregates      |
| `order_by` / `distinct`                         | Sort / unique                       |
| `values` / `values_list`                        | Dicts / tuples instead of models    |
| `select_related`                                | JOIN for FK / O2O                   |
| `prefetch_related`                              | Separate query for M2M / reverse FK |
| `only` / `defer`                                | Column subset                       |
| `iterator`                                      | Stream large results                |

Lookups: `field__lookup` (`exact`, `iexact`, `contains`, `in`, `gt`, `isnull`, …).

## 💡 Examples [#-examples]

**Filtering and Q objects:**

```python
from django.db.models import Q, Count

Article.objects.filter(is_published=True).exclude(author=None)

Article.objects.filter(
    Q(title__icontains="django") | Q(tags__name="python")
).distinct()
```

**Related + annotate:**

```python
qs = (
    Article.objects.filter(is_published=True)
    .select_related("author")
    .prefetch_related("tags")
    .annotate(tag_count=Count("tags"))
    .order_by("-published_at")
)
```

**Updates and existence:**

```python
Article.objects.filter(pk=1).update(is_published=True)  # no signals
Article.objects.filter(slug="hello").exists()
Article.objects.values_list("id", flat=True)
```

## ⚠️ Pitfalls [#️-pitfalls]

* N+1: accessing `.author` / `.tags.all()` in a loop without prefetch.
* Evaluating the same queryset twice unexpectedly (cache after first eval).
* `get()` raises `DoesNotExist` / `MultipleObjectsReturned`.
* Chaining after slicing can be restricted; prefer filter before slice.
* `update()` / `bulk_create` skip `save()` and most signals.

## 🔗 Related [#-related]

* [Models](/docs/django/models)
* [Managers](/docs/django/managers)
* [Transactions / ORM](/docs/django/transactions-orm)
* [Pagination](/docs/django/pagination)
* [Testing](/docs/django/testing)


---

# REST framework (/docs/django/rest-framework)



# REST framework [#rest-framework]

*Django · Reference cheat sheet*

***

## 📋 Overview [#-overview]

Django REST framework (DRF) builds Web APIs on Django: serializers, views/viewsets, routers, authentication, and permissions. Use for JSON APIs consumed by SPAs and mobile apps. Pair with Django 4.2+/5.x; install `djangorestframework`.

## 🔧 Core concepts [#-core-concepts]

| Piece                  | Role                                        |
| ---------------------- | ------------------------------------------- |
| Serializers            | Validate input / render output              |
| `APIView` / generics   | Class-based API endpoints                   |
| ViewSets + Routers     | CRUD URL wiring                             |
| Authentication         | Session, Token, JWT (3rd party), etc.       |
| Permissions            | `IsAuthenticated`, `IsAdminUser`, custom    |
| Pagination / filtering | `DEFAULT_PAGINATION_CLASS`, `django-filter` |
| Browsable API          | HTML forms for debugging                    |

Settings live under `REST_FRAMEWORK = \{...\}`.

## 💡 Examples [#-examples]

**Install & settings:**

```python
INSTALLED_APPS = [
    # …
    "rest_framework",
]

REST_FRAMEWORK = {
    "DEFAULT_AUTHENTICATION_CLASSES": [
        "rest_framework.authentication.SessionAuthentication",
        "rest_framework.authentication.BasicAuthentication",
    ],
    "DEFAULT_PERMISSION_CLASSES": [
        "rest_framework.permissions.IsAuthenticatedOrReadOnly",
    ],
    "DEFAULT_PAGINATION_CLASS": "rest_framework.pagination.PageNumberPagination",
    "PAGE_SIZE": 20,
}
```

**ViewSet + router:**

```python
# api/views.py
from rest_framework import viewsets
from rest_framework.permissions import IsAuthenticatedOrReadOnly
from .models import Article
from .serializers import ArticleSerializer


class ArticleViewSet(viewsets.ModelViewSet):
    queryset = Article.objects.select_related("author").all()
    serializer_class = ArticleSerializer
    permission_classes = [IsAuthenticatedOrReadOnly]
    lookup_field = "slug"

    def perform_create(self, serializer):
        serializer.save(author=self.request.user)
```

```python
# config/urls.py
from django.urls import include, path
from rest_framework.routers import DefaultRouter
from api.views import ArticleViewSet

router = DefaultRouter()
router.register("articles", ArticleViewSet, basename="article")

urlpatterns = [
    path("api/", include(router.urls)),
    path("api-auth/", include("rest_framework.urls")),
]
```

**Function-style `@api_view`:**

```python
from rest_framework.decorators import api_view, permission_classes
from rest_framework.permissions import AllowAny
from rest_framework.response import Response


@api_view(["GET"])
@permission_classes([AllowAny])
def health(request):
    return Response({"status": "ok"})
```

## ⚠️ Pitfalls [#️-pitfalls]

* CSRF: SessionAuthentication requires CSRF on unsafe methods from browsers.
* Exposing `ModelViewSet` without permissions is a data leak.
* N+1 in serializers—use `select_related` on the queryset.
* Do not put secrets in browsable API responses.

## 🔗 Related [#-related]

* [Serializers](/docs/django/serializers)
* [Authentication](/docs/django/authentication)
* [URLs](/docs/django/urls)
* [Views](/docs/django/views)
* [Models](/docs/django/models)
* [Settings](/docs/django/settings)


---

# Security (/docs/django/security)



# Security [#security]

*Django · Reference cheat sheet*

***

## 📋 Overview [#-overview]

Django provides CSRF protection, XSS escaping, clickjacking headers, host validation, and SSL redirects. Harden production settings (`DEBUG=False`, secure cookies, HSTS). Treat user input and uploads as hostile; keep secrets out of code and VCS.

## 🔧 Core concepts [#-core-concepts]

| Control      | Mechanism                          |
| ------------ | ---------------------------------- |
| CSRF         | Token + `CsrfViewMiddleware`       |
| XSS          | Template auto-escape               |
| Clickjacking | `XFrameOptionsMiddleware`          |
| Host header  | `ALLOWED_HOSTS`                    |
| HTTPS        | `SECURE_SSL_REDIRECT`, HSTS        |
| Cookies      | `SECURE` / `HTTPONLY` / `SAMESITE` |
| SQL          | ORM parameterization               |
| Passwords    | Hashers (`PBKDF2`, Argon2)         |

`django.middleware.security.SecurityMiddleware` applies many header redirects.

## 💡 Examples [#-examples]

**Production settings checklist:**

```python
DEBUG = False
ALLOWED_HOSTS = ["www.example.com"]
SECURE_SSL_REDIRECT = True
SECURE_HSTS_SECONDS = 31536000
SECURE_HSTS_INCLUDE_SUBDOMAINS = True
SECURE_HSTS_PRELOAD = True
SESSION_COOKIE_SECURE = True
CSRF_COOKIE_SECURE = True
SECURE_CONTENT_TYPE_NOSNIFF = True
SECURE_REFERRER_POLICY = "same-origin"
X_FRAME_OPTIONS = "DENY"
CSRF_TRUSTED_ORIGINS = ["https://www.example.com"]
```

**CSRF in AJAX:**

```javascript
const csrftoken = document.querySelector("[name=csrfmiddlewaretoken]").value;
fetch("/api/item/", {
  method: "POST",
  headers: { "X-CSRFToken": csrftoken, "Content-Type": "application/json" },
  body: JSON.stringify({ title: "Hi" }),
});
```

**Safe vs unsafe:**

```html
{{ user_bio }}           {# escaped #}
{{ user_bio|safe }}      {# only if trusted #}
```

## ⚠️ Pitfalls [#️-pitfalls]

* `DEBUG=True` in production leaks traces and settings.
* Disabling CSRF for “API convenience” on cookie-auth browsers.
* `|safe` / `mark_safe` on user HTML.
* Open redirects via unvalidated `next=` parameters.
* Serving user uploads from the same origin with executable MIME types.

## 🔗 Related [#-related]

* [Settings](/docs/django/settings)
* [Middleware](/docs/django/middleware)
* [Authentication](/docs/django/authentication)
* [Permissions](/docs/django/permissions)
* [Sessions](/docs/django/sessions)
* [File uploads](/docs/django/file-uploads)


---

# Serializers (/docs/django/serializers)



# Serializers [#serializers]

*Django · Reference cheat sheet*

***

## 📋 Overview [#-overview]

DRF serializers convert between complex types (model instances, querysets) and JSON-native Python data. They validate incoming payloads like Django forms. Prefer `ModelSerializer` for CRUD; use plain `Serializer` for custom shapes and write-only flows (e.g. password change).

## 🔧 Core concepts [#-core-concepts]

| Piece                      | Role                                |
| -------------------------- | ----------------------------------- |
| `Serializer`               | Explicit fields                     |
| `ModelSerializer`          | Auto fields from a model            |
| `validated_data`           | After `is_valid()`                  |
| Nested serializers         | Embed related objects               |
| `read_only` / `write_only` | Directional fields                  |
| `SerializerMethodField`    | Computed output                     |
| Validators                 | UniqueTogether, custom `validate_*` |

Call `serializer.save()` only after successful validation (ModelSerializer).

## 💡 Examples [#-examples]

**ModelSerializer:**

```python
from rest_framework import serializers
from .models import Article


class ArticleSerializer(serializers.ModelSerializer):
    author_name = serializers.CharField(source="author.get_username", read_only=True)

    class Meta:
        model = Article
        fields = (
            "id",
            "title",
            "slug",
            "body",
            "author",
            "author_name",
            "is_published",
            "published_at",
        )
        read_only_fields = ("author", "published_at")

    def validate_title(self, value: str) -> str:
        value = value.strip()
        if len(value) < 3:
            raise serializers.ValidationError("Title too short.")
        return value
```

**Create / update in a view:**

```python
serializer = ArticleSerializer(data=request.data)
serializer.is_valid(raise_exception=True)
article = serializer.save(author=request.user)
return Response(ArticleSerializer(article).data, status=201)
```

**Nested / method field:**

```python
class TagSerializer(serializers.ModelSerializer):
    class Meta:
        model = Tag
        fields = ("id", "name")


class ArticleDetailSerializer(serializers.ModelSerializer):
    tags = TagSerializer(many=True, read_only=True)
    summary = serializers.SerializerMethodField()

    class Meta:
        model = Article
        fields = ("id", "title", "slug", "tags", "summary")

    def get_summary(self, obj: Article) -> str:
        return obj.body[:120]
```

## ⚠️ Pitfalls [#️-pitfalls]

* Returning `serializer.data` before `is_valid()` on input serializers is wrong for writes.
* Writable nested serializers need custom `create`/`update`.
* Exposing sensitive model fields via `fields = "__all__"`.
* Forgetting `many=True` for lists causes cryptic errors.

## 🔗 Related [#-related]

* [REST framework](/docs/django/rest-framework)
* [Models](/docs/django/models)
* [Forms](/docs/django/forms)
* [Views](/docs/django/views)
* [Authentication](/docs/django/authentication)
* [URLs](/docs/django/urls)


---

# Sessions (/docs/django/sessions)



# Sessions [#sessions]

*Django · Reference cheat sheet*

***

## 📋 Overview [#-overview]

Sessions persist per-visitor data across requests via a session key (usually a cookie). Server-side backends store the payload (DB, cache, file, signed cookies). Access with `request.session` like a dict. Enable `SessionMiddleware` and `django.contrib.sessions`.

## 🔧 Core concepts [#-core-concepts]

| Piece                 | Role                 |
| --------------------- | -------------------- |
| `request.session`     | Read/write mapping   |
| `session.save()`      | Force persist        |
| `flush` / `cycle_key` | Logout / rotate key  |
| `SESSION_ENGINE`      | Backend              |
| `SESSION_COOKIE_*`    | Cookie flags         |
| `set_expiry`          | Per-session lifetime |

Backends: database (default), cache, cached\_db, file, signed\_cookies.

## 💡 Examples [#-examples]

**Usage:**

```python
def cart_add(request, product_id):
    cart = request.session.get("cart", {})
    cart[str(product_id)] = cart.get(str(product_id), 0) + 1
    request.session["cart"] = cart
    request.session.modified = True  # if mutating nested structures
    return redirect("cart")
```

**Login hygiene:**

```python
from django.contrib.auth import login

def login_view(request):
    ...
    login(request, user)  # rotates session key by default
```

**Settings:**

```python
SESSION_ENGINE = "django.contrib.sessions.backends.cached_db"
SESSION_COOKIE_HTTPONLY = True
SESSION_COOKIE_SECURE = True       # HTTPS only
SESSION_COOKIE_SAMESITE = "Lax"
SESSION_COOKIE_AGE = 60 * 60 * 24 * 14
```

**Clear:**

```python
request.session.flush()       # delete data + cookie
request.session.cycle_key()   # keep data, new key
```

## ⚠️ Pitfalls [#️-pitfalls]

* Mutating nested dicts/lists without `modified = True` or reassignment.
* Storing large objects or secrets in signed-cookie sessions.
* Missing `Secure` / `HttpOnly` / `SameSite` in production.
* Session fixation—always cycle on privilege change (login does this).
* Cache-only backend: eviction loses sessions.

## 🔗 Related [#-related]

* [Middleware](/docs/django/middleware)
* [Authentication](/docs/django/authentication)
* [Caching](/docs/django/caching)
* [Messages framework](/docs/django/messages-framework)
* [Security](/docs/django/security)
* [Settings](/docs/django/settings)


---

# Settings (/docs/django/settings)



# Settings [#settings]

*Django · Reference cheat sheet*

***

## 📋 Overview [#-overview]

`settings.py` (or split modules) configures Django: apps, middleware, database, templates, static/media, auth, and third-party packages. Keep secrets out of source control—use environment variables. Django 4.2+/5.x: prefer `pathlib`, `django-environ`/`python-dotenv`, and explicit `DEFAULT_AUTO_FIELD`.

## 🔧 Core concepts [#-core-concepts]

| Setting                   | Purpose                   |
| ------------------------- | ------------------------- |
| `INSTALLED_APPS`          | Enabled applications      |
| `MIDDLEWARE`              | Request/response pipeline |
| `DATABASES`               | DB connections            |
| `TEMPLATES`               | Template engines          |
| `AUTH_USER_MODEL`         | Custom user               |
| `STATIC_*` / `MEDIA_*`    | Assets & uploads          |
| `ALLOWED_HOSTS` / `DEBUG` | Deployment safety         |
| `CACHES` / `EMAIL_*`      | Infra integrations        |

Access via `from django.conf import settings`—never import settings module circularly from apps at import time if avoidable.

## 💡 Examples [#-examples]

**Core project settings:**

```python
from pathlib import Path
import os

BASE_DIR = Path(__file__).resolve().parent.parent

SECRET_KEY = os.environ["DJANGO_SECRET_KEY"]
DEBUG = os.environ.get("DJANGO_DEBUG", "0") == "1"
ALLOWED_HOSTS = os.environ.get("DJANGO_ALLOWED_HOSTS", "localhost,127.0.0.1").split(",")

INSTALLED_APPS = [
    "django.contrib.admin",
    "django.contrib.auth",
    "django.contrib.contenttypes",
    "django.contrib.sessions",
    "django.contrib.messages",
    "django.contrib.staticfiles",
    "blog",
]

MIDDLEWARE = [
    "django.middleware.security.SecurityMiddleware",
    "django.contrib.sessions.middleware.SessionMiddleware",
    "django.middleware.common.CommonMiddleware",
    "django.middleware.csrf.CsrfViewMiddleware",
    "django.contrib.auth.middleware.AuthenticationMiddleware",
    "django.contrib.messages.middleware.MessageMiddleware",
    "django.middleware.clickjacking.XFrameOptionsMiddleware",
]

ROOT_URLCONF = "config.urls"
WSGI_APPLICATION = "config.wsgi.application"
DEFAULT_AUTO_FIELD = "django.db.models.BigAutoField"

DATABASES = {
    "default": {
        "ENGINE": "django.db.backends.postgresql",
        "NAME": os.environ.get("POSTGRES_DB", "app"),
        "USER": os.environ.get("POSTGRES_USER", "app"),
        "PASSWORD": os.environ.get("POSTGRES_PASSWORD", ""),
        "HOST": os.environ.get("POSTGRES_HOST", "127.0.0.1"),
        "PORT": os.environ.get("POSTGRES_PORT", "5432"),
    }
}

STATIC_URL = "static/"
STATIC_ROOT = BASE_DIR / "staticfiles"
MEDIA_URL = "media/"
MEDIA_ROOT = BASE_DIR / "media"
```

**Production hardening flags:**

```python
if not DEBUG:
    SECURE_SSL_REDIRECT = True
    SESSION_COOKIE_SECURE = True
    CSRF_COOKIE_SECURE = True
    SECURE_HSTS_SECONDS = 31536000
```

## ⚠️ Pitfalls [#️-pitfalls]

* `DEBUG=True` in production leaks stack traces and secrets in error pages.
* Empty `ALLOWED_HOSTS` with `DEBUG=False` refuses all hosts.
* Committing `SECRET_KEY`—rotate if exposed.
* Changing `AUTH_USER_MODEL` late requires complex migrations.

## 🔗 Related [#-related]

* [URLs](/docs/django/urls)
* [Authentication](/docs/django/authentication)
* [django-allauth](/docs/django/django-allauth)
* [REST framework](/docs/django/rest-framework)
* [Admin](/docs/django/admin)
* [Models](/docs/django/models)


---

# Signals (/docs/django/signals)



# Signals [#signals]

*Django · Reference cheat sheet*

***

## 📋 Overview [#-overview]

Signals decouple senders from receivers: when something happens (save, delete, request finished), Django notifies connected handlers. Prefer explicit calls or service functions when coupling is local; use signals for cross-app side effects (cache bust, audit log, search index).

## 🔧 Core concepts [#-core-concepts]

| Signal                                 | When                          |
| -------------------------------------- | ----------------------------- |
| `pre_save` / `post_save`               | Before / after `Model.save()` |
| `pre_delete` / `post_delete`           | Before / after delete         |
| `m2m_changed`                          | M2M set/add/remove/clear      |
| `request_started` / `request_finished` | Request lifecycle             |
| `got_request_exception`                | Unhandled exception           |

Connect with `@receiver` or `signal.connect`. Receivers live in `AppConfig.ready()` so they register once.

## 💡 Examples [#-examples]

**App config registration:**

```python
# apps.py
from django.apps import AppConfig


class BlogConfig(AppConfig):
    default_auto_field = "django.db.models.BigAutoField"
    name = "blog"

    def ready(self):
        import blog.signals  # noqa: F401
```

**post\_save receiver:**

```python
# signals.py
from django.db.models.signals import post_save
from django.dispatch import receiver
from django.conf import settings

from .models import Profile


@receiver(post_save, sender=settings.AUTH_USER_MODEL)
def create_profile(sender, instance, created, **kwargs):
    if created:
        Profile.objects.create(user=instance)
```

**Disconnect / send custom:**

```python
from django.dispatch import Signal

order_paid = Signal()  # providing_args deprecated; use kwargs

def notify(sender, **kwargs):
    print(kwargs["order_id"])

order_paid.connect(notify)
order_paid.send(sender=None, order_id=42)
```

## ⚠️ Pitfalls [#️-pitfalls]

* Importing signals only from models can miss registration—use `AppConfig.ready()`.
* `bulk_create` / `QuerySet.update` / `bulk_update` do **not** fire save signals.
* Infinite loops: receiver calling `save()` on the same instance without guards.
* Weak refs: keep a reference or use `weak=False` if the receiver is a local function.
* Overusing signals hides control flow—harder to test and reason about.

## 🔗 Related [#-related]

* [Models](/docs/django/models)
* [Managers](/docs/django/managers)
* [Transactions / ORM](/docs/django/transactions-orm)
* [Caching](/docs/django/caching)
* [Testing](/docs/django/testing)


---

# Static files (/docs/django/static-files)



# Static files [#static-files]

*Django · Reference cheat sheet*

***

## 📋 Overview [#-overview]

Static files are CSS, JS, and images not uploaded by users. In development, Django can serve them; in production, collect into `STATIC_ROOT` and serve via WhiteNoise, nginx, or a CDN. Use `\{% static %\}` and `django.contrib.staticfiles`.

## 🔧 Core concepts [#-core-concepts]

| Setting            | Role                                   |
| ------------------ | -------------------------------------- |
| `STATIC_URL`       | URL prefix (`/static/`)                |
| `STATIC_ROOT`      | Collect destination for prod           |
| `STATICFILES_DIRS` | Extra project-level dirs               |
| App `static/`      | Per-app assets                         |
| `collectstatic`    | Copy/hashed files to `STATIC_ROOT`     |
| Storages           | `ManifestStaticFilesStorage`, S3, etc. |

Finders locate files; Manifest storage adds cache-busting hashes.

## 💡 Examples [#-examples]

**Settings:**

```python
STATIC_URL = "static/"
STATIC_ROOT = BASE_DIR / "staticfiles"
STATICFILES_DIRS = [BASE_DIR / "assets"]
STORAGES = {
    "default": {"BACKEND": "django.core.files.storage.FileSystemStorage"},
    "staticfiles": {
        "BACKEND": "django.contrib.staticfiles.storage.ManifestStaticFilesStorage",
    },
}
```

**Template:**

```html
{% load static %}
<link rel="stylesheet" href="{% static 'blog/app.css' %}">
<script src="{% static 'blog/app.js' %}"></script>
```

**Collect:**

```shellscript
python manage.py collectstatic --noinput
python manage.py findstatic blog/app.css
```

**WhiteNoise (typical):**

```python
MIDDLEWARE = [
    "django.middleware.security.SecurityMiddleware",
    "whitenoise.middleware.WhiteNoiseMiddleware",
    ...
]
```

## ⚠️ Pitfalls [#️-pitfalls]

* Confusing static files with media (`MEDIA_URL` / user uploads).
* Hardcoding `/static/...` instead of `\{% static %\}`.
* Forgetting `collectstatic` in deploy pipelines.
* Putting secrets or env-specific config inside static JS—use templated bootstrapping carefully.
* App static name collisions—namespace under `static/<app_label>/`.

## 🔗 Related [#-related]

* [Templates](/docs/django/templates)
* [Settings](/docs/django/settings)
* [File uploads](/docs/django/file-uploads)
* [Security](/docs/django/security)
* [Middleware](/docs/django/middleware)


---

# Templates (/docs/django/templates)



# Templates [#templates]

*Django · Reference cheat sheet*

***

## 📋 Overview [#-overview]

Django templates render HTML (or text) with variables, tags, and filters. Prefer thin templates: pass prepared context from views; use `\{% include %\}`, `\{% extends %\}`, and custom tags for reuse. Auto-escaping is on by default.

## 🔧 Core concepts [#-core-concepts]

| Feature                             | Role                               |
| ----------------------------------- | ---------------------------------- |
| `\{\{ var \}\}`                     | Variable output (escaped)          |
| `\{\{ var\|filter \}\}`             | Transform value                    |
| `\{% tag %\}`                       | Logic: `if`, `for`, `block`, `url` |
| `\{% extends %\}` / `\{% block %\}` | Inheritance                        |
| `\{% include %\}`                   | Partial templates                  |
| `\{% csrf_token %\}`                | CSRF for POST forms                |
| `\{% static %\}`                    | Static asset URLs                  |
| `\{% url %\}`                       | Reverse named routes               |

Engines: DjangoTemplates (default) or Jinja2 via `TEMPLATES` setting.

## 💡 Examples [#-examples]

**Base + child:**

```html
{# base.html #}
<!DOCTYPE html>
<html>
<head><title>{% block title %}Site{% endblock %}</title></head>
<body>
  {% block content %}{% endblock %}
</body>
</html>
```

```html
{# article_detail.html #}
{% extends "base.html" %}
{% load static %}

{% block title %}{{ article.title }}{% endblock %}

{% block content %}
  <h1>{{ article.title|title }}</h1>
  <p>{{ article.body|linebreaks }}</p>
  <a href="{% url 'article-list' %}">Back</a>
  <img src="{% static 'blog/hero.png' %}" alt="">
{% endblock %}
```

**View:**

```python
from django.shortcuts import render, get_object_or_404
from .models import Article


def article_detail(request, slug):
    article = get_object_or_404(Article, slug=slug)
    return render(request, "blog/article_detail.html", {"article": article})
```

**Loop and conditions:**

```html
{% for item in items %}
  <li class="{% if forloop.first %}first{% endif %}">{{ item }}</li>
{% empty %}
  <li>No items</li>
{% endfor %}
```

## ⚠️ Pitfalls [#️-pitfalls]

* Business logic in templates—move to views, model methods, or custom tags.
* `|safe` / `mark_safe` on untrusted input → XSS.
* Forgetting `\{% csrf_token %\}` on POST forms.
* Hardcoded URLs instead of `\{% url %\}`.
* Missing `\{% load static %\}` before `\{% static %\}`.

## 🔗 Related [#-related]

* [Views](/docs/django/views)
* [Context processors](/docs/django/context-processors)
* [Static files](/docs/django/static-files)
* [Forms](/docs/django/forms)
* [URLs](/docs/django/urls)
* [i18n](/docs/django/i18n)


---

# Testing (/docs/django/testing)



# Testing [#testing]

*Django · Reference cheat sheet*

***

## 📋 Overview [#-overview]

Django ships `TestCase`, `SimpleTestCase`, `TransactionTestCase`, and `LiveServerTestCase` on top of `unittest`. Prefer `TestCase` for DB tests (wraps each test in a transaction). Use the test client for request/response; factories or fixtures for data.

## 🔧 Core concepts [#-core-concepts]

| Class                 | Use                             |
| --------------------- | ------------------------------- |
| `SimpleTestCase`      | No DB                           |
| `TestCase`            | DB + rollback per test          |
| `TransactionTestCase` | Real commits (signals, threads) |
| `APITestCase`         | DRF (if installed)              |
| Client                | `self.client.get/post`          |
| `override_settings`   | Temporary settings              |
| `assertNumQueries`    | Catch N+1                       |

Run: `python manage.py test` or `pytest` + `pytest-django`.

## 💡 Examples [#-examples]

**View test:**

```python
from django.test import TestCase
from django.urls import reverse
from django.contrib.auth import get_user_model

from blog.models import Article


class ArticleTests(TestCase):
    def setUp(self):
        User = get_user_model()
        self.user = User.objects.create_user("ada", password="x")
        self.article = Article.objects.create(
            title="Hi", slug="hi", body="...", author=self.user, is_published=True
        )

    def test_detail_ok(self):
        url = reverse("article-detail", kwargs={"slug": "hi"})
        response = self.client.get(url)
        self.assertEqual(response.status_code, 200)
        self.assertContains(response, "Hi")

    def test_create_requires_login(self):
        url = reverse("article-create")
        response = self.client.post(url, {"title": "X", "slug": "x", "body": "y"})
        self.assertEqual(response.status_code, 302)
```

**Query count:**

```python
def test_list_queries(self):
    with self.assertNumQueries(2):  # adjust to reality
        self.client.get(reverse("article-list"))
```

**Settings override:**

```python
from django.test import override_settings

@override_settings(EMAIL_BACKEND="django.core.mail.backends.locmem.EmailBackend")
def test_sends_mail(self):
    ...
```

## ⚠️ Pitfalls [#️-pitfalls]

* Sharing mutable state across tests without isolation.
* Hitting real email/SMS/payment APIs—mock or use locmem backends.
* `TransactionTestCase` is slower; use only when needed.
* Hardcoding absolute URLs—use `reverse`.
* Flaky time-dependent tests—freeze time or use `freezegun`.

## 🔗 Related [#-related]

* [Views](/docs/django/views)
* [Models](/docs/django/models)
* [Permissions](/docs/django/permissions)
* [Email](/docs/django/email)
* [REST framework](/docs/django/rest-framework)


---

# Transactions / ORM (/docs/django/transactions-orm)



# Transactions / ORM [#transactions--orm]

*Django · Reference cheat sheet*

***

## 📋 Overview [#-overview]

Wrap multi-step writes in transactions so they commit or roll back together. Use `transaction.atomic()` (decorator or context manager). Understand autocommit (default), savepoints, `on_commit` hooks, and isolation when concurrent updates matter.

## 🔧 Core concepts [#-core-concepts]

| API                   | Role                             |
| --------------------- | -------------------------------- |
| `atomic()`            | Transaction / savepoint          |
| `on_commit(func)`     | Run after successful commit      |
| `set_rollback(True)`  | Mark atomic block to roll back   |
| Autocommit            | Each query commits unless atomic |
| `select_for_update()` | Row locks inside atomic          |
| `Robust`              | Nested atomics create savepoints |

DB-specific: PostgreSQL supports durable transactions and richer locking.

## 💡 Examples [#-examples]

**Atomic block:**

```python
from django.db import transaction


@transaction.atomic
def transfer(from_acct, to_acct, amount):
    from_acct.balance -= amount
    from_acct.save(update_fields=["balance"])
    to_acct.balance += amount
    to_acct.save(update_fields=["balance"])
```

**on\_commit (side effects):**

```python
def create_order(data):
    with transaction.atomic():
        order = Order.objects.create(**data)
        transaction.on_commit(lambda: send_order_email(order.pk))
        return order
```

**Locking:**

```python
with transaction.atomic():
    item = (
        Product.objects.select_for_update()
        .get(pk=pk)
    )
    if item.stock < 1:
        raise InsufficientStock()
    item.stock -= 1
    item.save(update_fields=["stock"])
```

**Catch integrity errors:**

```python
from django.db import IntegrityError

try:
    with transaction.atomic():
        Tag.objects.create(slug=slug)
except IntegrityError:
    ...
```

## ⚠️ Pitfalls [#️-pitfalls]

* Long atomic blocks holding locks—keep them short.
* Catching exceptions inside `atomic` without re-raising can leave the transaction broken until exit.
* Side effects (email, HTTP) inside atomic that run even if later rollback—use `on_commit`.
* `select_for_update` outside `atomic` is an error on some backends.
* Assuming read-after-write visibility across connections without commit.

## 🔗 Related [#-related]

* [Models](/docs/django/models)
* [QuerySet](/docs/django/queryset)
* [Signals](/docs/django/signals)
* [Managers](/docs/django/managers)
* [Testing](/docs/django/testing)


---

# URLs (/docs/django/urls)



# URLs [#urls]

*Django · Reference cheat sheet*

***

## 📋 Overview [#-overview]

URL routing maps paths to views via `urlpatterns` in `urls.py`. Use `path()` / `re_path()`, name routes for `reverse()`, and `include()` to compose per-app configs. Django 4.2+/5.x: prefer `path()` converters over complex regex unless needed.

## 🔧 Core concepts [#-core-concepts]

| Piece                         | Role                                                     |
| ----------------------------- | -------------------------------------------------------- |
| `path(route, view, name=...)` | Primary routing                                          |
| Converters                    | `<int:pk>`, `<slug:slug>`, `<uuid:id>`, `<path:subpath>` |
| `include()`                   | Nest app URLconfs                                        |
| `name`                        | Reverse lookups / `\{% url %\}`                          |
| `app_name`                    | Namespacing                                              |
| `re_path`                     | Regex routes                                             |
| `static()`                    | Dev media/static serving                                 |

`ROOT_URLCONF` points at the project URLconf module.

## 💡 Examples [#-examples]

**Project + app URLs:**

```python
# config/urls.py
from django.contrib import admin
from django.urls import include, path

urlpatterns = [
    path("admin/", admin.site.urls),
    path("blog/", include("blog.urls")),
    path("accounts/", include("allauth.urls")),
    path("api/", include("api.urls")),
]
```

```python
# blog/urls.py
from django.urls import path
from . import views

app_name = "blog"

urlpatterns = [
    path("", views.ArticleListView.as_view(), name="list"),
    path("new/", views.ArticleCreateView.as_view(), name="create"),
    path("<slug:slug>/", views.ArticleDetailView.as_view(), name="detail"),
]
```

**Reverse:**

```python
from django.urls import reverse

reverse("blog:detail", kwargs={"slug": "hello"})
# "/blog/hello/"
```

```html
<a href="{% url 'blog:detail' slug=article.slug %}">{{ article.title }}</a>
```

**Dev media:**

```python
from django.conf import settings
from django.conf.urls.static import static

urlpatterns += static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)
```

## ⚠️ Pitfalls [#️-pitfalls]

* Trailing-slash mismatches (`APPEND_SLASH`) cause confusing redirects on POST.
* Duplicate `name`s without namespaces break `reverse`.
* Putting business logic in URLconfs—keep views thin but logic out of urls.
* Forgetting `.as_view()` for CBVs.

## 🔗 Related [#-related]

* [Views](/docs/django/views)
* [Built-in class-based views](/docs/django/buildin-views)
* [Settings](/docs/django/settings)
* [Admin](/docs/django/admin)
* [REST framework](/docs/django/rest-framework)
* [django-allauth](/docs/django/django-allauth)


---

# Validators (/docs/django/validators)



# Validators [#validators]

*Django · Reference cheat sheet*

***

## 📋 Overview [#-overview]

Validators are callables that raise `ValidationError` when a value is invalid. Attach to model fields (`validators=[...]`), form fields, or call `full_clean()`. Built-ins cover email, URL, regex, min/max value/length, and file extensions.

## 🔧 Core concepts [#-core-concepts]

| Validator                                   | Checks            |
| ------------------------------------------- | ----------------- |
| `EmailValidator`                            | Email shape       |
| `URLValidator`                              | URL               |
| `RegexValidator`                            | Pattern           |
| `MinValueValidator` / `MaxValueValidator`   | Numeric bounds    |
| `MinLengthValidator` / `MaxLengthValidator` | Length            |
| `FileExtensionValidator`                    | Upload extensions |
| `validate_slug` / `validate_ipv4_address`   | Common formats    |
| `ProhibitNullCharactersValidator`           | No `\0`           |

Model `clean()` / `clean_<field>()` for cross-field rules. Forms run field + form cleaning automatically; `Model.save()` does **not** call validators unless you `full_clean()`.

## 💡 Examples [#-examples]

**Field validators:**

```python
from django.core.validators import MinValueValidator, RegexValidator
from django.db import models


slug_re = RegexValidator(r"^[a-z0-9-]+$", "Lowercase letters, numbers, hyphens.")


class Product(models.Model):
    sku = models.CharField(max_length=32, validators=[slug_re])
    price = models.DecimalField(
        max_digits=8,
        decimal_places=2,
        validators=[MinValueValidator(0)],
    )
```

**Custom validator:**

```python
from django.core.exceptions import ValidationError


def validate_even(value: int) -> None:
    if value % 2 != 0:
        raise ValidationError("%(value)s is not even", params={"value": value})
```

**Model.clean:**

```python
class Event(models.Model):
    starts_at = models.DateTimeField()
    ends_at = models.DateTimeField()

    def clean(self):
        super().clean()
        if self.ends_at <= self.starts_at:
            raise ValidationError({"ends_at": "Must be after start."})
```

## ⚠️ Pitfalls [#️-pitfalls]

* Relying on DB constraints alone—validators are Python-level.
* Skipping `full_clean()` on programmatic `save()`.
* Regex DoS with pathological patterns on user input.
* Duplicating the same rules on model and form inconsistently.
* `blank=True` vs validators—empty values may skip some validators.

## 🔗 Related [#-related]

* [Models](/docs/django/models)
* [Forms](/docs/django/forms)
* [File uploads](/docs/django/file-uploads)
* [Serializers](/docs/django/serializers)
* [Testing](/docs/django/testing)


---

# Views (/docs/django/views)



# Views [#views]

*Django · Reference cheat sheet*

***

## 📋 Overview [#-overview]

Views receive an `HttpRequest` and return an `HttpResponse` (or raise). Use function-based views (FBVs) for simple flows and class-based views (CBVs) for reusable patterns. Keep views thin: validate input, call services/ORM, return response or redirect.

## 🔧 Core concepts [#-core-concepts]

| Style      | Notes                                                  |
| ---------- | ------------------------------------------------------ |
| FBV        | `def view(request, **kwargs)`                          |
| CBV        | `MyView.as_view()` in URLconf                          |
| Shortcuts  | `render`, `redirect`, `get_object_or_404`              |
| Decorators | `login_required`, `require_POST`, `csrf_exempt` (rare) |
| Mixins     | Auth/permission for CBVs                               |
| Errors     | `Http404`, `PermissionDenied`, custom handlers         |

Request attributes: `method`, `GET`, `POST`, `user`, `session`, `FILES`, `headers`.

## 💡 Examples [#-examples]

**Function-based view:**

```python
from django.contrib.auth.decorators import login_required
from django.shortcuts import get_object_or_404, redirect, render
from .forms import ArticleForm
from .models import Article


def article_detail(request, slug: str):
    article = get_object_or_404(Article, slug=slug, is_published=True)
    return render(request, "blog/article_detail.html", {"article": article})


@login_required
def article_edit(request, slug: str):
    article = get_object_or_404(Article, slug=slug, author=request.user)
    if request.method == "POST":
        form = ArticleForm(request.POST, instance=article)
        if form.is_valid():
            form.save()
            return redirect(article)
    else:
        form = ArticleForm(instance=article)
    return render(request, "blog/article_form.html", {"form": form})
```

**JSON response without DRF:**

```python
import json
from django.http import JsonResponse
from django.views.decorators.http import require_GET


@require_GET
def health(request):
    return JsonResponse({"status": "ok"})
```

**CBV sketch:**

```python
from django.views import View
from django.http import HttpResponse


class PingView(View):
    def get(self, request, *args, **kwargs):
        return HttpResponse("pong")
```

## ⚠️ Pitfalls [#️-pitfalls]

* Returning unsanitized user HTML → XSS; use templates auto-escape.
* Long-running work in a request blocks workers—use tasks/queues.
* Catching all exceptions and returning 200 hides failures.
* Mutating data on GET violates HTTP semantics—use POST/PUT/PATCH/DELETE.

## 🔗 Related [#-related]

* [Built-in class-based views](/docs/django/buildin-views)
* [URLs](/docs/django/urls)
* [Forms](/docs/django/forms)
* [Authentication](/docs/django/authentication)
* [REST framework](/docs/django/rest-framework)
* [Models](/docs/django/models)

