Skip to content
Back to skills

Django

ASecurity

Builds, debugs, and hardens Django apps: models, the ORM, views, templates, forms, the admin, DRF APIs, and deployment. Use when a page fires one query per row (N+1, select_related, prefetch_related, annotate double-counting); when makemigrations conflicts, a migration locks a live table, or InconsistentMigrationHistory blocks a deploy; on 403 CSRF verification failed, DEBUG=False turning every request into 400 DisallowedHost or a blank 500, or SECURE_SSL_REDIRECT looping behind a proxy; on S...

  • 17 stars
  • 0 votes
  • 0 copies
  • 3 views
  • Added September 6, 2026
businesspythonrustgoshellsqlexpressfastapidjangoflaskrails

Works with

  • cursor
  • cli
  • api

Security analysis

A100/100

Pro scans all 20 files and shows the line behind each finding

Scanned September 6, 2026

npx -y skills add clawic/skills --skill django --agent claude-code

Installs into .claude/skills of the current project.

Are you the author of Django?

Add the live security badge to your README. It updates with every re-scan.

Security grade badge for Django
[![Security: A โ€” Skills Directory](https://www.skillsdirectory.com/api/skills/clawic-django/badge)](https://www.skillsdirectory.com/skills/clawic-django)

More formats (shields.io, HTML) on the badges page. Keep it an A: scan every change in CI with Pro.

Download with Pro
SKILL.md
---
name: Django
slug: django
version: 1.0.3
description: 'Builds, debugs, and hardens Django apps: models, the ORM, views, templates, forms, the admin, DRF APIs, and deployment. Use when a page fires one query per row (N+1, select_related, prefetch_related, annotate double-counting); when makemigrations conflicts, a migration locks a live table, or InconsistentMigrationHistory blocks a deploy; on 403 CSRF verification failed, DEBUG=False turning every request into 400 DisallowedHost or a blank 500, or SECURE_SSL_REDIRECT looping behind a proxy; on SynchronousOnlyOperation, AppRegistryNotReady, or NoReverseMatch; when a background task runs before its transaction commits, signals fire on rows that roll back, or update() skips auto_now; when the admin times out on a big table, collectstatic breaks static files, or workers exhaust database connections; when writing serializers, formsets, a custom user model, permissions, or assertNumQueries tests; or upgrading Django across a deprecation. Not for plain Python, FastAPI or Flask services, or engine-level SQL tuning.'
homepage: https://clawic.com/skills/django
changelog: Display name shown correctly
metadata:
  clawdbot:
    emoji: ๐ŸŒฟ
    requires:
      bins:
      - python3
    os:
    - linux
    - darwin
    - win32
    displayName: Django
    configPaths:
    - ~/Clawic/data/django/
    - ~/django/
    - ~/clawic/django/
  openclaw:
    requires:
      config:
      - ~/Clawic/data/django/
      - ~/django/
      - ~/clawic/django/
---

User preferences and memory live in `~/Clawic/data/django/` (see `setup.md` on first use, `memory-template.md` for the file format). If you have data at an old location (`~/django/` or `~/clawic/django/`), move it to `~/Clawic/data/django/`, and say in one line that you moved it and from where.

## When To Use

- Writing or reviewing Django models, migrations, views, forms, templates, admin classes, or DRF serializers
- A page is slow or the query count grows with the number of rows on screen
- A migration will not generate, will not apply, conflicts after a merge, or would lock a production table
- An exception that is Django's and not Python's: `SynchronousOnlyOperation`, `TransactionManagementError`, `AppRegistryNotReady`, `NoReverseMatch`, `DisallowedHost`, `ImproperlyConfigured`
- Hardening a project for production: settings split, `check --deploy`, static and media, sessions, permissions, upload limits
- Background jobs, async views, Channels, caching, or a test suite that is slow or order-dependent
- Not for plain Python semantics, packaging, or asyncio internals, and not for engine-level SQL tuning (see Related Skills)

## Quick Reference

| Situation | Play |
|---|---|
| Query count grows with rows on the page | `select_related` for forward FK/O2O, `prefetch_related` for reverse FK/M2M (Core Rules 1-2, โ†’ `orm.md`) |
| `Sum`/`Count` inflated after `annotate` | Two joins multiply rows โ€” `Count("x", distinct=True)` or a `Subquery` (โ†’ `orm.md`) |
| Rows come back duplicated after filtering on a related model | Chained `.filter().filter()` joins twice; one `.filter(a=..., b=...)` requires the same related row (โ†’ `orm.md`) |
| `makemigrations` reports "No changes detected" | App missing from `INSTALLED_APPS`, or models defined outside an imported module (โ†’ `migrations.md`) |
| Two migration leaves after a merge | `makemigrations --merge`; never renumber files by hand (โ†’ `migrations.md`) |
| The migration must run on a live table | Expand โ†’ backfill in batches โ†’ contract, each in its own migration (Core Rules 6, โ†’ `migrations.md`) |
| 403 "CSRF verification failed" | Missing `{% csrf_token %}`, or `CSRF_TRUSTED_ORIGINS` entries without a scheme behind a proxy (โ†’ `security.md`) |
| 400 on every request once `DEBUG=False` | `ALLOWED_HOSTS` (โ†’ `settings.md`) |
| 500 with an empty response and nothing in the logs | `DEBUG` off with no `LOGGING` config โ€” the exception exists, nothing writes it down (โ†’ `settings.md`) |
| Redirect loop behind a load balancer | `SECURE_SSL_REDIRECT` without `SECURE_PROXY_SSL_HEADER` (โ†’ `deployment.md`) |
| `SynchronousOnlyOperation` | ORM touched from an async context โ€” `sync_to_async` or the `a`-prefixed ORM methods (โ†’ `async.md`) |
| Task fails with `DoesNotExist`, then succeeds on retry | Queued inside `atomic()` and picked up before COMMIT โ€” `transaction.on_commit` (Core Rules 5, โ†’ `tasks.md`) |
| Admin change page hangs or times out | A ForeignKey rendered as a `<select>` of every row โ€” `autocomplete_fields`, `list_select_related` (โ†’ `admin.md`) |
| Static files 404, or the manifest raises after deploy | `collectstatic`, `STATIC_ROOT`, and hashed-name references (โ†’ `deployment.md`) |
| Tests pass alone and fail as a suite | Mutated `setUpTestData` objects, or a setting read at import time (โ†’ `testing.md`) |
| A DRF endpoint issues N+1 or leaks a field | `SerializerMethodField` touching a relation; `fields = "__all__"` (โ†’ `drf.md`) |
| Login, permissions, or a custom user model | `auth.md` โ€” and set `AUTH_USER_MODEL` before the first `migrate` (Core Rules 8) |
| Starting a project, or deciding where a new app goes | `startproject config .`, domain-shaped apps, and a label chosen once โ€” it is baked into every table name (โ†’ `layout.md`) |
| Bumping the Django version, or `RemovedInDjangoXXWarning` in the test output | Clear deprecations on the current version with `python -Wa manage.py test`, then move one feature release at a time (โ†’ `upgrade.md`) |
| Text must render in the user's language, or dates in their format | `gettext_lazy` at import time, `{% blocktranslate %}` in templates, and `compilemessages` โ€” Django reads `.mo`, never `.po` (โ†’ `i18n.md`) |
| Anything else | Reproduce in `manage.py shell`, switch the `django.db.backends` logger to DEBUG, and read the SQL Django actually emitted before changing any code (โ†’ `debug.md`) |

Depth on demand, by phase:

- **Start** โ€” `layout.md` project skeleton, app boundaries, labels, where non-app code goes
- **Diagnose** โ€” `debug.md` symptom to cause in minutes ยท `commands.md` the `manage.py` toolkit and what each command really does
- **Model the data** โ€” `models.md` fields, relations, constraints, managers, signals ยท `migrations.md` generating, merging, squashing, online schema change ยท `orm.md` querysets, joins, aggregation, transactions, locking
- **Serve requests** โ€” `views.md` view classes, URLs, middleware, requests and responses ยท `forms.md` validation, formsets, file uploads ยท `templates.md` escaping, context, custom tags ยท `auth.md` users, sessions, permissions, password flows ยท `admin.md` the admin at real data volume ยท `drf.md` serializers, viewsets, permissions, pagination ยท `i18n.md` translation, locale switching, formats, timezones
- **Make it fast** โ€” `performance.md` query budgets, caching layers, profiling ยท `async.md` async views, ASGI, Channels ยท `tasks.md` background jobs, on_commit, retries, email
- **Ship it** โ€” `settings.md` settings layout, env config, logging, timezone ยท `deployment.md` WSGI/ASGI, workers, static and media, release sequence ยท `security.md` the Django-specific attack surface ยท `testing.md` fast, isolated, honest tests ยท `upgrade.md` release cadence, deprecations, LTS windows

## Core Rules

1. **Give every list view a query budget and assert it.** Budget = 1 query for the page + 1 per `prefetch_related` + 0 for `select_related` (it joins into the page query) + 1 for the count if you paginate. A paginated 50-row page of orders with `select_related("customer")` and `prefetch_related("items")` is 1 + 1 + 0 + 1 = 3 queries; the unoptimized version of the same page is 1 + 50 + 50 + 1 = 102. Check it with `assertNumQueries(3)` in a test, not by eye โ€” the regression arrives inside someone else's template change.
2. **`select_related` joins, `prefetch_related` runs a second query.** Forward `ForeignKey`/`OneToOneField` โ†’ `select_related` (SQL JOIN, one query). Reverse FK and `ManyToManyField` โ†’ `prefetch_related` (one extra query, joined in Python). Passing an M2M to `select_related` raises `FieldError`; passing a forward FK to `prefetch_related` works but buys an extra round trip for nothing.
3. **Queryset-level writes bypass the model.** `update()`, `delete()`, `bulk_create()`, `bulk_update()` never call `Model.save()`, never fire `pre_save`/`post_save`, never touch `auto_now`, and never run validators. That is exactly why they are fast. When you use them, set the timestamp yourself: `.update(status="done", updated_at=timezone.now())`.
4. **Counters use `F()`, not read-modify-write.** `obj.n += 1; obj.save()` reads a stale value and loses every concurrent increment; `Model.objects.filter(pk=pk).update(n=F("n") + 1)` is a single atomic `UPDATE ... SET n = n + 1`. After an `F()` write the in-memory attribute holds an expression object, not a number โ€” `refresh_from_db()` before reading it.
5. **Side effects belong in `transaction.on_commit`.** Anything outside the database โ€” a queued task, an email, a webhook, a cache invalidation โ€” fires only after COMMIT. Queued inside `atomic()`, a worker can pick the job up before the row is visible: the symptom is a task failing with `DoesNotExist` for an object you just created, and passing on retry.
6. **A schema change on a live table is three deploys, not one.** Expand (add the nullable column or new table, ship code that tolerates both shapes) โ†’ backfill in batches with a resume key โ†’ contract (set NOT NULL, drop the old column) once nothing reads the old shape. One migration that adds a NOT NULL column to a large table rewrites it under a lock, and every request queues behind that lock.
7. **Catch database errors outside the `atomic()` block.** After any statement raises inside a transaction, the connection is poisoned: every later query raises `TransactionManagementError` until rollback. To continue after an expected `IntegrityError`, wrap just the risky statement in its own nested `with transaction.atomic():` โ€” the nesting is a savepoint, and only the savepoint rolls back.
8. **Set `AUTH_USER_MODEL` before the first `migrate`.** Start every project with `class User(AbstractUser): pass` even if it stays empty. Swapping the user model after tables exist means rewriting every FK to `auth.User` and, in practice, rebuilding migration history โ€” Django offers no supported path for it.
9. **Reference models by string; never import them at module import time.** `ForeignKey("shop.Order")` and `settings.AUTH_USER_MODEL` break import cycles. `get_user_model()` or a queryset at module level raises `AppRegistryNotReady`; put it inside the function, or in `AppConfig.ready()` for signal registration only.

## Exception To Cause

Django raises its own exception types before Python's. The type names the subsystem.

| Exception | What it actually means | First move |
|---|---|---|
| `SynchronousOnlyOperation` | An ORM call reached an async context | Wrap in `sync_to_async(...)`, or use `aget`/`acreate`/`async for` (Django >=4.1) (โ†’ `async.md`) |
| `TransactionManagementError` | A query ran after an error inside `atomic()`, or `select_for_update()` ran outside a transaction | Rule 7; for locking, open an `atomic()` block first |
| `AppRegistryNotReady` | Models or `get_user_model()` touched during import | Rule 9 โ€” move it into a function or `AppConfig.ready()` |
| `ImproperlyConfigured` | Settings used before `django.setup()`, or a required setting missing or empty | The message tail names the setting; standalone scripts need `django.setup()` before importing any app code |
| `DisallowedHost` | The `Host` header is not in `ALLOWED_HOSTS` | Add the host; behind a proxy also check `USE_X_FORWARDED_HOST` (โ†’ `settings.md`) |
| `NoReverseMatch` | A `{% url %}`/`reverse()` name, namespace, or argument count is wrong | Check `app_name` plus the pattern's converters โ€” a `<int:pk>` route rejects a string silently (โ†’ `views.md`) |
| `TemplateDoesNotExist` | Loader order, not a missing file, most of the time | The debug page lists every path tried; check `APP_DIRS` and `DIRS` (โ†’ `templates.md`) |
| `FieldError` | An invalid lookup, or `only()`/`defer()` conflicting with `select_related` | The message lists the valid choices; re-read the `__` lookup chain |
| `RelatedObjectDoesNotExist` | A nullable FK that is NULL, or a reverse OneToOne with no row | `getattr(obj, "profile", None)`; the class also catches as `Model.DoesNotExist` |
| `MultipleObjectsReturned` | `get()` matched more than one row โ€” a uniqueness constraint is missing | Add the `UniqueConstraint`, then decide whether the caller wanted `filter().first()` |
| `SuspiciousFileOperation` | A generated path escaped the storage root | Never build `upload_to` or a storage name from raw user input (โ†’ `security.md`) |
| `InconsistentMigrationHistory` | A migration is recorded as applied before a dependency it needs | Usually a late user-model swap or a re-pointed FK; repair the graph, do not `--fake` blindly (โ†’ `migrations.md`) |
| `OperationalError: database is locked` | SQLite with concurrent writers | SQLite serializes writes; raise `timeout` in `DATABASES["default"]["OPTIONS"]`, or move to Postgres for anything concurrent |

## HTTP Symptoms

| Response | Usual cause |
|---|---|
| 400 on everything after `DEBUG=False` | `ALLOWED_HOSTS` empty or missing this host |
| 403 "CSRF verification failed" | No `{% csrf_token %}`; a cross-origin POST needing `CSRF_TRUSTED_ORIGINS` entries with the scheme (`https://app.example.com`, required since Django >=4.0); or `CSRF_COOKIE_SECURE` on a plain-HTTP origin |
| 404 on a URL that exists | Trailing-slash mismatch, `include()` ordering, or a path converter rejecting the value |
| 301 loop | `SECURE_SSL_REDIRECT` behind a TLS-terminating proxy with no `SECURE_PROXY_SSL_HEADER` |
| 302 to `/accounts/login/` from an API client | `LoginRequiredMixin` on an endpoint that should answer 401/403 โ€” use DRF permissions instead (โ†’ `drf.md`) |
| A POST arrives as a GET with no data | `APPEND_SLASH`: Django answers a slash-less POST with a 301 and the body is dropped. Post to the exact URL |
| 500, blank body, nothing logged | `DEBUG=False` with default logging โ€” Django mails `ADMINS` and writes nothing else (โ†’ `settings.md`) |
| 502/504 under load, fine when idle | Worker saturation, or a request longer than the proxy timeout (โ†’ `deployment.md`) |
| Users randomly logged out | `SECRET_KEY` differs between instances, or was rotated without `SECRET_KEY_FALLBACKS` (Django >=4.1) |

## Settings Defaults That Bite

Exact Django defaults that produce confusing failures. All are overridable in settings.

| Setting | Default | What the default costs you |
|---|---|---|
| `DATA_UPLOAD_MAX_MEMORY_SIZE` | 2621440 bytes (2.5 MB) | A non-file POST body above it raises `RequestDataTooBig` โ€” hits large JSON payloads and long text fields |
| `DATA_UPLOAD_MAX_NUMBER_FIELDS` | 1000 | `TooManyFieldsSent` on large formsets. A formset posts `forms ร— fields_per_form + 4` management inputs, so 1000 caps you near 200 forms of 5 fields |
| `FILE_UPLOAD_MAX_MEMORY_SIZE` | 2621440 bytes (2.5 MB) | Below it an upload is an in-memory object with no `temporary_file_path()`; above it, a temp file on disk. Code that assumes one shape breaks on the other |
| `CONN_MAX_AGE` | 0 | A fresh TCP connect plus auth handshake on every single request |
| `CACHES["default"]["TIMEOUT"]` | 300 seconds | Anything cached without an explicit timeout expires in five minutes |
| `LocMemCache` `MAX_ENTRIES` | 300, with `CULL_FREQUENCY` 3 | At 300 keys it evicts one third at random โ€” and each worker process holds its own copy, which is why hit rates look impossible (โ†’ `performance.md`) |
| `SESSION_COOKIE_AGE` | 1209600 seconds (14 days) | Sessions live two weeks and the `django_session` table grows forever unless `clearsessions` runs on a schedule |
| `PASSWORD_RESET_TIMEOUT` | 259200 seconds (3 days) | Reset links stay valid for three days |
| Formset `max_num` | 1000, with `absolute_max` = `max_num + 1000` | A crafted POST can force Django to build up to `absolute_max` forms before validation runs |
| `DEFAULT_AUTO_FIELD` | unset โ†’ `models.W042` | Every app gets a 32-bit `AutoField` and the system check nags; set `BigAutoField` project-wide |
| `DEBUG` | `False` | Right for production, and the one default people expect backwards: with `DEBUG=True` Django appends every query to `connection.queries` forever, so a long-running dev process grows without bound |

## Configuration

User-dependent variables. Defaults apply until the user states a preference; store them in `~/Clawic/data/django/config.yaml`.

| Variable | Type | Default | Effect |
|---|---|---|---|
| django_version | number (4.2-6.x) | 5.2 | Which `Django >=X.Y` gated advice applies when the project's version is unknown, and which deprecations to flag |
| database | postgres \| mysql \| sqlite \| oracle | postgres | Switches ORM and migration advice: `select_for_update` options, server-side cursors, JSON lookups, whether `__date` needs loaded timezone tables |
| api_layer | none \| drf \| ninja \| plain-json | drf | Which request/response idiom generated endpoints use, and whether `drf.md` guidance applies at all |
| settings_layout | single \| split-by-env \| env-vars | split-by-env | Where a new setting is written and how secrets are read (โ†’ `settings.md`) |
| project_layout | flat \| apps-package | flat | Where a new app is created and which dotted names appear in `INSTALLED_APPS` and `AppConfig.name` (โ†’ `layout.md`) |
| task_queue | none \| celery \| rq \| django-tasks | celery | Shape of background-job examples; with `none`, work is inlined behind `transaction.on_commit` instead (โ†’ `tasks.md`) |
| test_runner | django \| pytest-django | django | Whether tests are emitted as `TestCase` classes or pytest functions with fixtures (โ†’ `testing.md`) |
| deploy_target | gunicorn-wsgi \| uvicorn-asgi \| paas \| serverless | gunicorn-wsgi | Worker-count formula, static-file strategy, and whether long-lived database connections are safe (โ†’ `deployment.md`) |
| destructive_confirm | bool | true | `migrate --fake`, `flush`, `sqlflush`, reverse migrations and drop-column operations are emitted for review instead of run |

Preference areas โ€” customizable dimensions; a stated preference is recorded in `config.yaml` and applied from then on:

- **Tooling** โ€” dependency manager and venv layout, debug toolbar vs profiler, `django-filter`/`factory_boy`/`allauth` and friends, migration linting in CI
- **Thresholds** โ€” query budget per view, default page size, cache TTLs, backfill batch size, the slow-request threshold worth reporting
- **Conventions** โ€” fat models vs a service layer, URL and view naming, `related_name` style, serializer naming, app naming style
- **Platform** โ€” database engine and version, cache and broker backends, media storage backend, hosting target, Python version floor
- **Risk posture** โ€” whether migrations may be applied directly, whether raw SQL is allowed, how hard to push back on `fields = "__all__"` and `@csrf_exempt`
- **Output format** โ€” whole files vs diffs, how much explanation ships with generated code, type hints and docstrings
- **Work order** โ€” test-first vs code-first, whether a migration review gate precedes merge, when `check --deploy` runs
- **Integrations** โ€” auth provider and SSO, email and payment providers, error tracking, broker choice, object storage
- **Restrictions** โ€” banned packages, LTS-only policy, PII fields that must never be logged, compliance regimes requiring audit trails
- **Cadence** โ€” dependency and security upgrade rhythm, LTS upgrade window, session and log cleanup schedules

## Output Gates

Before emitting models, a migration, a view, or a serializer:

- Does every view that lists related data declare its query budget, with `select_related`/`prefetch_related` to match (Rule 1)?
- Does the migration touch a live table, and if so, is it split expand โ†’ backfill โ†’ contract (Rule 6)?
- Is every external side effect wrapped in `transaction.on_commit` (Rule 5)?
- Do new foreign keys and frequently filtered columns get an index in the same migration?
- Are `ModelForm` and `ModelSerializer` field lists explicit, never `"__all__"`?
- Does every object fetched by an ID from the request also filter on ownership or permission (โ†’ `security.md`)?
- Are user-supplied strings rendered without `|safe`/`mark_safe`, and JSON handed to scripts through `{{ data|json_script:"id" }}`?
- Timestamps via `timezone.now()` / `timezone.localdate()`, never `datetime.now()` / `date.today()`?

## Traps

| Trap | Why it fails | Do instead |
|---|---|---|
| Assuming `Model.save()` validates | `save()` never calls `full_clean()`: `choices`, validators and most `max_length` checks are form-layer only | Enforce in the database with `Meta.constraints`, or call `full_clean()` explicitly |
| `null=True` on a text field | Two empty states (`""` and `NULL`) that every query then has to handle | `blank=True` alone; keep `null=True` for non-text columns |
| `Meta.ordering` on a busy model | Every query inherits the sort โ€” and in `values().annotate()` the ordering column silently joins the `GROUP BY`, changing your aggregate | Order at the queryset; `.order_by()` with no arguments clears an inherited sort |
| `exclude(field=None)` to find NULLs | Compiles to `NOT (field = NULL)`, which drops NULL rows instead of selecting them | `filter(field__isnull=True)` |
| `queryset.delete()` over millions of rows | Django loads the objects to cascade and fire signals in Python | Delete in primary-key batches, or move the cascade into the database and own it there |
| `get_object_or_404(Order, pk=pk)` in a user-facing view | Any authenticated user can read any ID | Scope the lookup: `get_object_or_404(Order, pk=pk, user=request.user)` |
| `fields = "__all__"` on a ModelForm or ModelSerializer | Every future field becomes exposed and writable the day it is added | List fields explicitly and let that list be the review surface |
| `@login_required` on a class-based view | The decorator wraps the class object, not the request handler | `LoginRequiredMixin` first in the bases, or `method_decorator` on `dispatch` |
| Signals carrying business logic | They fire from anywhere, are invisible at the call site, and never run for `update()`/`bulk_create()` | An explicit service function; keep signals for cross-app decoupling you actually need |
| `datetime.now()` in models or views | Naive local time; with `USE_TZ=True` (the default in Django >=5.0) you get a `RuntimeWarning` and drifted comparisons | `timezone.now()`, and `timezone.localdate()` for "today" |
| `.raw()` or `.extra()` built with f-strings | String interpolation is SQL injection regardless of the ORM around it | Bind parameters: `.raw("... WHERE id = %s", [pk])` |
| Reading `request.body` twice | The stream is consumed; the second read returns `b""` | Read once into a local, or use `request.POST` for form encodings |
| Leaving sessions to grow | The `django_session` table has no automatic cleanup | `manage.py clearsessions` on a schedule, or a cache-backed session engine |

## Where Experts Disagree

- **Fat models vs a service layer.** Model methods keep behavior next to the data and make the shell powerful; a service layer keeps transactions, side effects and orchestration in one readable place. The testable boundary: anything that spans two aggregates or touches the outside world (payments, email, tasks) belongs in a service, because that is precisely what has to be wrapped in `atomic()` and `on_commit`.
- **Signals.** One camp bans them as action at a distance; the other keeps them for genuine cross-app decoupling. Both agree they are the wrong tool inside a single app, and both concede they never fire for queryset-level writes โ€” so a signal can never be the only enforcement of an invariant.
- **DRF vs plain views for JSON.** DRF earns its weight when you need content negotiation, browsable docs, permissions and pagination as policy; for a handful of endpoints it is a large surface to reason about. Boundary: a public API or more than a few endpoints โ†’ DRF or Ninja; three internal endpoints โ†’ `JsonResponse` with explicit validation.
- **Async Django.** Async views pay off for I/O fan-out (several outbound HTTP calls per request); they buy little where the request time is ORM queries, since that path still crosses a thread. Adopt per view, not per project (โ†’ `async.md`).
- **UUID vs bigint primary keys.** UUIDs stop enumeration and let clients mint IDs offline; random v4 fragments the index and widens every foreign key. Common ground: exposing a sequential ID is only a problem when authorization is missing โ€” the check protects the row, not the shape of the key.

## Related Skills

More Clawic skills, get them at https://clawic.com/skills/django (install if the user confirms):

- `py` โ€” Python itself: imports, packaging, typing, asyncio internals, pytest mechanics
- `pg` โ€” PostgreSQL underneath the ORM: EXPLAIN plans, index design, vacuum, locks, connection pooling
- `rest-api` โ€” API design decisions above the framework: versioning, contracts, error shapes
- `fastapi` โ€” when the service is async-first and needs no ORM, admin, or templates
- `auth` โ€” protocol-level identity: OAuth flows, SSO, MFA, passwordless

## Feedback

- If useful, star it: https://clawic.com/skills/django
- Latest version: https://clawic.com/skills/django

Part of [Clawic](https://clawic.com), the verified skill library. Get this skill: https://clawic.com/skills/django.

Files in this skill

  • SKILL.md25.1 KB
  • _meta.json388 B
  • admin.md6.2 KB
  • async.md6.3 KB
  • auth.md8 KB
  • commands.md6.2 KB
  • debug.md8.2 KB
  • deployment.md7.7 KB
  • drf.md7.5 KB
  • forms.md7.4 KB
  • i18n.md8.3 KB
  • layout.md7.2 KB
  • memory-template.md915 B
  • migrations.md8 KB
  • models.md8.9 KB
  • orm.md9.4 KB
  • performance.md6.9 KB
  • security.md8 KB
  • settings.md7.5 KB
  • setup.md2.8 KB

Attribution

Is this your skill, or is something wrong with this listing? Request removal or report an issue. Author removals are honored within 72 hours.

Comments

Loading commentsโ€ฆ