Skip to content
Back to skills

Django Best Practices

ASecurity

Django patterns for production-ready Python web applications

  • 43 stars
  • 0 votes
  • 0 copies
  • 3 views
  • Added September 9, 2026
databasespythongosqlexpressdjangotestingapidatabasebackendci/cd

Works with

  • cursor
  • cli
  • api

Security analysis

A100/100

Scanned September 9, 2026

npx -y skills add baekenough/oh-my-customcode --skill django-best-practices --agent claude-code

Installs into .claude/skills of the current project.

Are you the author of Django Best Practices?

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

Security grade badge for Django Best Practices
[![Security: A — Skills Directory](https://www.skillsdirectory.com/api/skills/baekenough-django-best-practices/badge)](https://www.skillsdirectory.com/skills/baekenough-django-best-practices)

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-best-practices
description: Django patterns for production-ready Python web applications
scope: core
user-invocable: false
---

## Purpose

Apply Django 6.0.4 patterns for building production-ready, secure, and maintainable Python web applications.

## Rules

### 1. Project Structure

```yaml
structure:
  settings_split: true
  layout: "config/{settings/{base,development,production}.py,urls.py,wsgi.py} + apps/{core/,users/,<feature>/} + templates/ + static/ + requirements/{base,development,production}.txt"

app_module_contents:
  models.py: Database models
  views.py: Request handlers
  urls.py: URL patterns (with app_name)
  serializers.py: DRF serializers (if API)
  forms.py: Django forms
  admin.py: Admin customization
  services.py: Business logic layer
  managers.py: Custom model managers
  signals.py: Django signals (use sparingly)
  tests/: Test suite (mirror app structure)
```

Reference: guides/django-best-practices/README.md

### 2. Models Best Practices

```yaml
custom_user_model:
  rule: ALWAYS create a custom User model, even if identical to default
  location: apps/users/models.py
  reason: Impossible to swap default User model mid-project
  pattern: "Extend AbstractUser, set AUTH_USER_MODEL in settings"

primary_key:
  default: BigAutoField
  settings: DEFAULT_AUTO_FIELD = 'django.db.models.BigAutoField'

model_meta:
  required:
    - __str__: human-readable representation
    - Meta.ordering: consistent default ordering
    - Meta.verbose_name: singular display name
    - Meta.verbose_name_plural: plural display name

query_optimization:
  foreign_key: select_related()    # Single SQL JOIN
  many_to_many: prefetch_related() # Separate query + Python join
  partial_fields: only() / defer() # Load subset of fields
  aggregations: F() and Q() objects for complex expressions
  bulk_ops:
    create: bulk_create(objs, batch_size=1000)
    update: bulk_update(objs, fields, batch_size=1000)

indexing:
  - Frequently filtered fields: db_index=True
  - Frequently ordered fields: Meta.ordering fields
  - Multi-column: Meta.indexes with models.Index
  - Unique together: Meta.unique_together or UniqueConstraint

constraints:
  use: Meta.constraints for database-level enforcement
  types: "UniqueConstraint, CheckConstraint"

soft_delete:
  pattern: is_active = models.BooleanField(default=True)
  manager: Override default manager to filter is_active=True

custom_managers:
  rule: Use managers for reusable querysets
```

Reference: guides/django-best-practices/README.md

### 3. Views Best Practices

```yaml
cbv_vs_fbv:
  cbv: Standard CRUD, predictable patterns (ListView, DetailView, etc.)
  fbv: Complex custom logic, non-standard workflows

thin_views:
  rule: Keep views thin — delegate business logic to services/models

shortcuts:
  - get_object_or_404(Model, pk=pk): Returns 404 instead of 500
  - get_list_or_404(Model, **kwargs): 404 if empty list

mixins:
  authentication: LoginRequiredMixin
  permissions: PermissionRequiredMixin
  user_pass: UserPassesTestMixin

status_codes:
  200: OK (default for success)
  201: Created (after successful creation)
  302: Redirect (after POST success — PRG pattern)
  400: Bad Request (validation error)
  403: Forbidden (permission denied)
  404: Not Found
  405: Method Not Allowed
```

### 4. URL Patterns

```yaml
namespacing:
  app_name: Required in every app's urls.py
  usage: "reverse('app_name:url_name') or {% url 'app_name:url_name' %}"

syntax:
  prefer: path() over re_path() for clarity
  use_re_path: Only for complex regex patterns

naming:
  rule: Name ALL URL patterns
  convention: "{resource}-{action} (e.g., article-list, article-detail)"

inclusion:
  root_urls: Use include() for app-level URLs
```

Reference: guides/django-best-practices/README.md

### 5. Forms & Validation

```yaml
model_forms:
  rule: Use ModelForm when form maps to a model
  fields: "Explicitly list fields (never use fields = '__all__')"

validation:
  field_level: clean_<field>() method
  cross_field: clean() method
  built_in: Use Django validators (MaxValueValidator, RegexValidator, etc.)
```

Reference: guides/django-best-practices/README.md

### 6. Security

```yaml
environment:
  SECRET_KEY: Never hardcode — read from environment variable
  DEBUG: False in production (MUST)
  ALLOWED_HOSTS: Explicitly set in production (MUST)

https:
  SECURE_SSL_REDIRECT: true
  SESSION_COOKIE_SECURE: true
  CSRF_COOKIE_SECURE: true
  SECURE_HSTS_SECONDS: 3600  # Start small, increase to 31536000
  SECURE_HSTS_INCLUDE_SUBDOMAINS: true

clickjacking:
  X_FRAME_OPTIONS: DENY

content_type:
  SECURE_CONTENT_TYPE_NOSNIFF: true

csrf:
  rule: Enabled by default via CsrfViewMiddleware — do NOT disable

sql_injection:
  rule: Use ORM, avoid raw SQL; if needed, use parameterized queries

xss:
  rule: Templates auto-escape by default — never use |safe with user content

deployment_check:
  command: python manage.py check --deploy
  run: Before every production deployment
```

### 7. Testing

```yaml
framework:
  preferred: pytest-django (over unittest)
  config: pytest.ini or pyproject.toml with [tool.pytest.ini_options]

test_classes:
  database: TestCase (wraps each test in transaction)
  no_database: SimpleTestCase (faster)
  live_server: LiveServerTestCase (for Selenium)

test_data:
  preferred: factory_boy or model_bakery
  avoid: fixtures (hard to maintain, slow)

request_testing:
  Client: Full request/response cycle (preferred for views)
  RequestFactory: Faster, no middleware (for unit testing views)

settings_override:
  decorator: '@override_settings(...)'

coverage:
  target: 80%+
  exclude: migrations, admin, settings

structure:
  mirror_app: tests/test_models.py, tests/test_views.py, tests/test_forms.py
```

Reference: guides/django-best-practices/README.md

### 8. Performance

```yaml
n_plus_1_prevention:
  check: Use django-debug-toolbar in development
  fix_fk: select_related('author', 'category')
  fix_m2m: prefetch_related('tags', 'comments')
  complex: Prefetch object with custom queryset

partial_loading:
  only: "only('id', 'title', 'created_at') — Load only these fields"
  defer: "defer('body', 'metadata') — Load all except these"
  values: "values('id', 'title') — Returns dicts (no ORM overhead)"
  values_list: "values_list('id', flat=True) — Returns flat list"

caching:
  backend: Redis (preferred), Memcached
  view_cache: "'@cache_page(60 * 15)' decorator"
  template_cache: "'{% cache 500 sidebar %}' template tag"
  low_level: cache.get/set/delete for fine-grained control

pagination:
  list_views: Always paginate large querysets
  page_size: Set reasonable default (20-50 items)
  drf: PageNumberPagination or CursorPagination

bulk_operations:
  create: "bulk_create(articles, batch_size=1000)"
  update: "bulk_update(articles, ['status'], batch_size=1000)"
  avoid: Loops calling .save() on many objects
```

### 9. Django REST Framework (DRF)

```yaml
serializers:
  standard_crud: ModelSerializer
  read_only: Use SerializerMethodField for computed values
  write_validation: validate_<field>() and validate() methods

viewsets:
  standard: ModelViewSet for full CRUD
  custom: ViewSet with explicit action methods
  routers: DefaultRouter or SimpleRouter for URL generation

authentication:
  jwt: djangorestframework-simplejwt (recommended)
  token: DRF built-in TokenAuthentication
  session: SessionAuthentication (for browser clients)

permissions:
  global: DEFAULT_PERMISSION_CLASSES in settings
  per_view: permission_classes attribute on ViewSet
  object_level: has_object_permission() in custom permission class

versioning:
  method: NamespaceVersioning or URLPathVersioning

throttling:
  anonymous: AnonRateThrottle
  authenticated: UserRateThrottle
  custom: Extend BaseThrottle

pagination:
  global: DEFAULT_PAGINATION_CLASS in settings
  types: PageNumberPagination (simple), CursorPagination (large datasets)
```

Reference: guides/django-best-practices/README.md

### 10. Deployment

```yaml
wsgi_asgi:
  wsgi: gunicorn (4 workers per CPU core)
  asgi: uvicorn with gunicorn workers (for async/WebSocket)

static_files:
  development: Django's staticfiles
  production: whitenoise middleware OR CDN (S3 + CloudFront)
  command: python manage.py collectstatic --noinput

database:
  development: SQLite (acceptable)
  production: PostgreSQL (MUST — never SQLite in production)
  connection_pooling: Use pgBouncer or django-db-connection-pool

migrations:
  deploy: Run as part of CI/CD pipeline before server restart
  zero_downtime: Use additive migrations (add nullable columns, backfill, then add constraint)

logging:
  config: LOGGING dict in settings
  handler: File handler in production, console in development
  level: WARNING in production, DEBUG in development

environment_variables:
  tool: python-decouple or django-environ
  never: Hardcode secrets in settings files

health_check:
  endpoint: /health/ returning 200 OK
  checks: Database connection, cache connection, disk space
```

## Application

When writing Django code:

1. **Always** create a custom User model before any other models
2. **Always** split settings into base/development/production
3. **Prefer** CBVs for standard CRUD, FBVs for custom logic
4. **Use** select_related/prefetch_related to prevent N+1 queries
5. **Apply** the security checklist for every production deployment
6. **Test** with pytest-django and factory_boy
7. **Never** use `fields = '__all__'` in ModelForms or ModelSerializer
8. **Run** `python manage.py check --deploy` before shipping

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…

Newsletter

Get the weekly list of safe new skills, and the ones we pulled.