Skills DirectorySkills Directory
SkillsLearnSecurityCategoriesDocsCommunityBlog
Sign InSubmit Skill
Skills Directory

Security-tested agent skills for Claude, coding agents, and AI workflows.

Directory

  • Browse Skills
  • All Skills A–Z
  • Claude Skills
  • Claude Code Skills
  • Agent Skills
  • Categories
  • Authors
  • Submit a Skill

Learn

  • Learn Hub
  • Install Claude Skills
  • Write SKILL.md
  • Skills vs MCP
  • Directories Compared

Security

  • Security
  • Methodology
  • Secure Claude Skills
  • Security Badges

Company

  • About
  • Community
  • Blog
  • API Docs
  • Advertise

2026 Skills Directory. All rights reserved.

ProTermsPrivacyRefunds
Back to skills

Django Conventions

ASecurity

Django web framework conventions: app layout, settings split (base/local/production), URLconf with app namespacing, CBV and DRF ViewSets, DRF serializers and permissions, form validation, signals, Django admin registration, and middleware. Activated automatically by django-plugin/stack.md. Works alongside python-foundation:python-conventions and django-plugin:django-orm-patterns. Use this skill to: - Structure a Django project with multiple apps and correct URLconf hierarchy. - Write CBVs an...

35 stars
0 votes
0 copies
0 views
Added 9/22/2026
developmentpythongodjangotestingapidatabasebackend

Works with

api

Security Analysis

A100/100

Scanned 9/22/2026

Install to Claude Code

$npx -y skills add AratKruglik/claude-sdlc --skill django-conventions --agent claude-code

Installs into .claude/skills of the current project.

Are you the author of Django Conventions?

Add the live security badge to your README — it updates automatically with every re-scan.

Security grade badge for Django Conventions
[![Security: A — Skills Directory](https://www.skillsdirectory.com/api/skills/aratkruglik-django-conventions/badge)](https://www.skillsdirectory.com/skills/aratkruglik-django-conventions)

More formats (shields.io, HTML) on the badges page.

Download with Pro
Files
SKILL.md
---
name: django-conventions
description: |
  Django web framework conventions: app layout, settings split (base/local/production), URLconf with app namespacing, CBV and DRF ViewSets, DRF serializers and permissions, form validation, signals, Django admin registration, and middleware. Activated automatically by django-plugin/stack.md. Works alongside python-foundation:python-conventions and django-plugin:django-orm-patterns.

  Use this skill to:
  - Structure a Django project with multiple apps and correct URLconf hierarchy.
  - Write CBVs and DRF ViewSets with appropriate permissions and serializers.
  - Configure settings correctly for different environments.
  - Use signals for decoupled event handling between apps.
  - Register models in Django admin with useful list_display and search_fields.

  Do NOT use this skill for:
  - Django ORM model field finalization and migration patterns — see django-plugin:django-orm-patterns.
  - Python language idioms (type hints, dataclasses, enums) — see python-foundation:python-conventions.
  - Testing patterns — see python-foundation:pytest-testing.
user-invocable: false
paths: ["**/*.py", "**/templates/**"]
---

# Django Conventions

This skill encodes the conventions used across modern Django projects (Django 4.x / 5.x). Apply alongside `python-foundation:python-conventions` (language idioms) and `django-plugin:django-orm-patterns` (model/query patterns) when implementing features.

## 1. Detection

Before writing code, read:
- `manage.py` — confirms this is a Django project.
- `settings.py` or `settings/base.py` — check `DJANGO_VERSION`, `INSTALLED_APPS`, `REST_FRAMEWORK` dict (DRF present?), `AUTH_USER_MODEL`.
- The project `urls.py` — understand the existing URL hierarchy and namespace conventions.
- `requirements.txt` or `pyproject.toml` — note the exact Django version and whether DRF, `django-environ`, `django-filter`, `pytest-django`, etc. are present.

## 2. Project and app layout

Prefer a dedicated `apps/` directory for application modules and a `config/` directory for project-level config. New apps are registered in `INSTALLED_APPS` using their `AppConfig` dotted path.

```
myproject/
  manage.py
  config/
    settings/
      base.py
      local.py
      production.py
    urls.py
    wsgi.py
    asgi.py
  apps/
    users/
      migrations/
      models.py
      views.py
      serializers.py
      urls.py
      admin.py
      apps.py
      signals.py
    orders/
      migrations/
      models.py
      views.py
      serializers.py
      urls.py
      admin.py
      apps.py
```

`apps.py` for each application:

```python
from django.apps import AppConfig

class OrdersConfig(AppConfig):
    default_auto_field = 'django.db.models.BigAutoField'
    name = 'apps.orders'
    verbose_name = 'Orders'

    def ready(self) -> None:
        import apps.orders.signals  # noqa: F401 — registers signal handlers
```

## 3. Settings split

Production settings must never contain `DEBUG = True` or a hardcoded `SECRET_KEY`.

```python
# config/settings/base.py
import environ

env = environ.Env()

SECRET_KEY = env('SECRET_KEY')
DEBUG = env.bool('DEBUG', default=False)
ALLOWED_HOSTS = env.list('ALLOWED_HOSTS')

INSTALLED_APPS = [
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',
    'rest_framework',
    'corsheaders',
    'apps.users',
    'apps.orders',
]

DATABASES = {
    'default': env.db('DATABASE_URL'),
}
```

```python
# config/settings/local.py
from .base import *  # noqa: F401, F403

DEBUG = True
ALLOWED_HOSTS = ['localhost', '127.0.0.1']
```

```python
# config/settings/production.py
from .base import *  # noqa: F401, F403

SECURE_SSL_REDIRECT = True
SESSION_COOKIE_SECURE = True
CSRF_COOKIE_SECURE = True
```

## 4. URLconf

Project `urls.py` uses `include()` with app namespaces. DRF ViewSets are registered on a `DefaultRouter` in the app's `urls.py`.

```python
# config/urls.py
from django.contrib import admin
from django.urls import path, include

urlpatterns = [
    path('admin/', admin.site.urls),
    path('api/', include('apps.orders.urls', namespace='orders')),
    path('', include('apps.users.urls', namespace='users')),
]
```

```python
# apps/orders/urls.py
from django.urls import path, include
from rest_framework.routers import DefaultRouter
from . import views

app_name = 'orders'

router = DefaultRouter()
router.register(r'orders', views.OrderViewSet, basename='order')

urlpatterns = [
    path('', include(router.urls)),
    path('orders/<int:pk>/cancel/', views.OrderCancelView.as_view(), name='order-cancel'),
]
```

## 5. DRF ViewSets and serializers

### ModelViewSet

```python
from rest_framework import viewsets, permissions
from django_filters.rest_framework import DjangoFilterBackend
from .models import Order
from .serializers import OrderSerializer, OrderCreateSerializer

class OrderViewSet(viewsets.ModelViewSet):
    queryset = Order.objects.select_related('user').order_by('-created_at')
    permission_classes = [permissions.IsAuthenticated]
    filter_backends = [DjangoFilterBackend]
    filterset_fields = ['status']

    def get_serializer_class(self):
        if self.action == 'create':
            return OrderCreateSerializer
        return OrderSerializer

    def get_queryset(self):
        return super().get_queryset().filter(user=self.request.user)

    def perform_create(self, serializer):
        serializer.save(user=self.request.user)
```

### Serializers

Use separate serializers when the create/update payload differs from the list/retrieve shape.

```python
from rest_framework import serializers
from .models import Order

class OrderSerializer(serializers.ModelSerializer):
    class Meta:
        model = Order
        fields = ['id', 'status', 'product', 'qty', 'created_at']
        read_only_fields = ['id', 'status', 'created_at']


class OrderCreateSerializer(serializers.ModelSerializer):
    class Meta:
        model = Order
        fields = ['product', 'qty']

    def validate_qty(self, value: int) -> int:
        if value <= 0:
            raise serializers.ValidationError("Quantity must be positive.")
        return value

    def validate(self, attrs: dict) -> dict:
        # cross-field validation
        return attrs
```

### Nested serializer

```python
class OrderWithUserSerializer(serializers.ModelSerializer):
    user = UserSummarySerializer(read_only=True)

    class Meta:
        model = Order
        fields = ['id', 'user', 'status', 'created_at']
```

## 6. DRF permissions

Always declare `permission_classes` explicitly on every ViewSet and `APIView`. Do not rely on global defaults alone.

```python
from rest_framework.permissions import BasePermission, IsAuthenticated, IsAuthenticatedOrReadOnly

# Default: require authentication for all methods
permission_classes = [IsAuthenticated]

# Public read, authenticated write
permission_classes = [IsAuthenticatedOrReadOnly]

# Custom permission
class IsOwner(BasePermission):
    def has_object_permission(self, request, view, obj) -> bool:
        return obj.user == request.user
```

Combine permissions with a list: `permission_classes = [IsAuthenticated, IsOwner]`.

## 7. Class-based views (CBV)

Use `LoginRequiredMixin` and `PermissionRequiredMixin` for access control. Override `get_queryset()` to scope results to the requesting user. Keep views thin — delegate business logic to service functions.

```python
from django.contrib.auth.mixins import LoginRequiredMixin, PermissionRequiredMixin
from django.views.generic import ListView, CreateView
from django.urls import reverse_lazy
from .models import Order
from .forms import OrderForm

class OrderListView(LoginRequiredMixin, ListView):
    model = Order
    template_name = 'orders/list.html'
    context_object_name = 'orders'
    paginate_by = 20

    def get_queryset(self):
        return Order.objects.filter(user=self.request.user).order_by('-created_at')

    def get_context_data(self, **kwargs):
        context = super().get_context_data(**kwargs)
        context['total'] = self.get_queryset().count()
        return context


class OrderCreateView(LoginRequiredMixin, CreateView):
    model = Order
    form_class = OrderForm
    template_name = 'orders/create.html'
    success_url = reverse_lazy('orders:list')

    def form_valid(self, form):
        form.instance.user = self.request.user
        return super().form_valid(form)
```

## 8. Forms

Use `ModelForm` for model-backed forms. Validate in `clean_<field>` or `clean()` — never in the view.

```python
from django import forms
from .models import Order

class OrderForm(forms.ModelForm):
    class Meta:
        model = Order
        fields = ['product', 'qty', 'notes']
        widgets = {
            'notes': forms.Textarea(attrs={'rows': 3}),
        }

    def clean_qty(self) -> int:
        qty = self.cleaned_data['qty']
        if qty <= 0:
            raise forms.ValidationError("Quantity must be positive.")
        return qty

    def clean(self) -> dict:
        cleaned_data = super().clean()
        product = cleaned_data.get('product')
        qty = cleaned_data.get('qty')
        # cross-field validation example
        if product and qty and qty > 100:
            raise forms.ValidationError("Cannot order more than 100 units of a single product.")
        return cleaned_data
```

## 9. Signals

Register signal handlers in `signals.py` and connect them in `AppConfig.ready()`. Keep handlers thin — for heavy work, dispatch to a background task (Celery, Django Q, etc.).

```python
# apps/orders/signals.py
from django.db.models.signals import post_save
from django.dispatch import receiver
from .models import Order

@receiver(post_save, sender=Order)
def notify_on_status_change(sender, instance: Order, created: bool, **kwargs) -> None:
    if not created and instance.tracker.has_changed('status'):
        # thin: just enqueue a notification task
        send_status_notification.delay(order_id=instance.pk, new_status=instance.status)
```

```python
# apps/orders/apps.py
class OrdersConfig(AppConfig):
    name = 'apps.orders'

    def ready(self) -> None:
        import apps.orders.signals  # noqa: F401
```

Best practices:
- Never do database writes in `post_save` handlers without `update_fields` guards — it can cause infinite loops.
- Use `transaction.on_commit()` to enqueue async tasks only after the DB transaction commits.
- Prefer custom model methods or service functions over signals for logic that must be tested directly.

## 10. Django admin

Register models with `@admin.register` and provide useful `list_display`, `list_filter`, and `search_fields`.

```python
from django.contrib import admin
from .models import Order

@admin.register(Order)
class OrderAdmin(admin.ModelAdmin):
    list_display = ['id', 'user', 'product', 'status', 'qty', 'created_at']
    list_filter = ['status', 'created_at']
    search_fields = ['user__email', 'product']
    readonly_fields = ['created_at', 'updated_at']
    ordering = ['-created_at']
```

## 11. Checklist before completing development phase

- [ ] All new ViewSets and `APIView` subclasses have explicit `permission_classes`
- [ ] Serializer validation uses `validate_<field>` / `validate()` — not view bodies
- [ ] CBVs use `LoginRequiredMixin` or `permission_classes` — no unprotected views
- [ ] `get_queryset()` scopes results to `request.user` where ownership applies
- [ ] Signal handlers are thin and registered in `AppConfig.ready()`
- [ ] Settings read secrets from env — no hardcoded `SECRET_KEY` or `DATABASE_URL`
- [ ] No `makemigrations` or `migrate` called — that is django-migrations-specialist
- [ ] `python manage.py check` passes with no errors
- [ ] `ruff format .` clean

Attribution

AratKruglikAratKruglik
View sourceMore from AratKruglik →
SSkills DirectorySkills Directory

Know which skills are safe — weekly.

Best new skills + every skill we flagged as malicious. From the team that scanned 103,619.

Join free

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 (0)

No comments yet. Be the first to comment!

SSkills DirectorySkills Directory

Know which skills are safe — weekly.

Best new skills + every skill we flagged as malicious. From the team that scanned 103,619.

Join free

Related Skills

Browser Extension Developer

Use this skill when developing or maintaining browser extension code in the `browser/` directory, including Chrome/Firefox/Edge compatibility, content scripts, background scripts, or i18n updates.

284722 votes

Seo Optimizer

SEO optimization with keyword analysis, readability assessment, technical validation, content quality. Use for search rankings, blog posts, content audits, or encountering keyword density, readability scores, meta tags, schema markup errors.

2192 votes

Google Official Seo Guide

Official Google SEO guide covering search optimization, best practices, Search Console, crawling, indexing, and improving website search visibility based on official Google documentation

1862 votes

Tanstack Start

Build a full-stack TanStack Start app on Cloudflare Workers from scratch — SSR, file-based routing, server functions, D1+Drizzle, better-auth, Tailwind v4+shadcn/ui. Use whenever the user mentions TanStack Start, asks to scaffold a full-stack Cloudflare app with SSR, wants an SSR dashboard, or asks for a React 19 + Cloudflare Workers app with file-based routing and server functions — even if they don't name TanStack Start specifically. No template repo — Claude generates every file fresh per ...

9881 votes

Pentest

PTES-aligned adversarial security audit for backend, frontend, and mobile applications. Produces a CVSS-scored Hacker Report with verified PoCs and phased remediation.

5491 votes
View all in development →