Skip to content
Security
Skill

/django-drf

Django REST Framework patterns. Trigger: When implementing generic DRF APIs (ViewSets, serializers, routers, permissions, filtersets). For Prowler API specifics (RLS/RBAC/Providers), also use prowler-api.

From plugin
prowler
15k39 skills1 MCP

Claude Code

$ npx -y skills add prowler-cloud/prowler --skill django-drf --agent claude-code

SKILL.md

django-drf.SKILL.md
name: django-drf
description: >
  Django REST Framework patterns.
  Trigger: When implementing generic DRF APIs (ViewSets, serializers, routers, permissions, filtersets). For Prowler API specifics (RLS/RBAC/Providers), also use prowler-api.
license: Apache-2.0
metadata:
  author: prowler-cloud
  version: "1.3.0"
  scope: [root, api]
  auto_invoke:
    - "Creating ViewSets, serializers, or filters in api/"
    - "Implementing JSON:API endpoints"
    - "Adding DRF pagination or permissions"
allowed-tools: Read, Edit, Write, Glob, Grep, Bash, WebFetch, WebSearch, Task

Critical Patterns

  • ALWAYS separate serializers by operation: Read / Create / Update / Include
  • ALWAYS use `filterset_class` for complex filtering (not `filterset_fields`)
  • ALWAYS validate unknown fields in write serializers (inherit `BaseWriteSerializer`)
  • ALWAYS use `select_related`/`prefetch_related` in `get_queryset()` to avoid N+1
  • ALWAYS handle `swagger_fake_view` in `get_queryset()` for schema generation
  • ALWAYS use `@extend_schema_field` for OpenAPI docs on `SerializerMethodField`
  • NEVER put business logic in serializers - use services/utils
  • NEVER use auto-increment PKs - use UUIDv4 or UUIDv7
  • NEVER use trailing slashes in URLs (`trailing_slash=False`)

> **Note:** `swagger_fake_view` is specific to **drf-spectacular** for OpenAPI schema generation.

---

Implementation Checklist

When implementing a new endpoint, review these patterns in order:

| # | Pattern | Reference | Key Points | |---|---------|-----------|------------| | 1 | **Models** | `api/models.py` | UUID PK, `inserted_at`/`updated_at`, `JSONAPIMeta.resource_name` | | 2 | **ViewSets** | `api/base_views.py`, `api/v1/views.py` | Inherit `BaseRLSViewSet`, `get_queryset()` with N+1 prevention | | 3 | **Serializers** | `api/v1/serializers.py` | Separate Read/Create/Update/Include, inherit `BaseWriteSerializer` | | 4 | **Filters** | `api/filters.py` | Use `filterset_class`, inherit base filter classes | | 5 | **Permissions** | `api/base_views.py`, `api/rbac/permissions.py` | `required_permissions`, `set_required_permissions()`, `HasPermissions` | | 6 | **Pagination** | `REST_FRAMEWORK` default `JsonApiPageNumberPagination`; `api/pagination.py` | Custom class only if needed; `PaginateByPkMixin` in `api/v1/mixins.py` for heavy joins | | 7 | **URL Routing** | `api/v1/urls.py` | `trailing_slash=False`, kebab-case paths | | 8 | **OpenAPI Schema** | `api/v1/views.py` | `@extend_schema_view` with drf-spectacular | | 9 | **Tests** | `api/tests/test_views.py` | JSON:API content type, fixture patterns (see `prowler-test-api`) | | 10 | **Changelog** | `api/changelog.d/` | towncrier fragment (see `prowler-changelog`) |

> **Full file paths**: See [references/file-locations.md](references/file-locations.md)

---

Decision Trees

Which Serializer?

GET list/retrieve → <Model>Serializer
POST create       → <Model>CreateSerializer
PATCH update      → <Model>UpdateSerializer
?include=...      → <Model>IncludeSerializer

Which Base Serializer?

Read-only serializer   → BaseModelSerializerV1
Create with tenant_id  → RLSSerializer + BaseWriteSerializer (auto-injects tenant_id on create)
Update with validation → BaseWriteSerializer (tenant_id already exists on object)
Non-model data         → BaseSerializerV1

Which Filter Base?

Direct FK to Provider  → BaseProviderFilter
FK via Scan           → BaseScanProviderFilter
No provider relation  → FilterSet

Which Base ViewSet?

RLS-protected model  → BaseRLSViewSet (most common)
Tenant operations    → BaseTenantViewset
User operations      → BaseUserViewset
No RLS required      → BaseViewSet (rare)

Resource Name Format?

Single word model     → plural lowercase           (Provider → providers)
Multi-word model      → plural lowercase kebab     (ProviderGroup → provider-groups)
Through/join model    → parent-child pattern       (UserRoleRelationship → user-roles)
Aggregation/overview  → descriptive kebab plural   (ComplianceOverview → compliance-overviews)

---

Serializer Patterns

Base Class Hierarchy

# Read serializer (most common)
class ProviderSerializer(RLSSerializer):
    class Meta:
        model = Provider
        fields = ["id", "provider", "uid", "alias", "connected", "inserted_at"]

# Write serializer (validates unknown fields)
class ProviderCreateSerializer(RLSSerializer, BaseWriteSerializer):
    class Meta:
        model = Provider
        fields = ["provider", "uid", "alias"]

# Include serializer (sparse fields for ?include=)
class ProviderIncludeSerializer(RLSSerializer):
    class Meta:
        model = Provider
        fields = ["id", "alias"]  # Minimal fields

SerializerMethodField with OpenAPI

from drf_spectacular.utils import extend_schema_field

class ProviderSerializer(RLSSerializer):
    connection = serializers.SerializerMethodField(read_only=True)

    @extend_schema_field({
        "type": "object",
        "properties": {
            "connected": {"type": "boolean"},
            "last_checked_at": {"type": "string", "format": "date-time"},
        },
    })
    def get_connection(self, obj):
        return {
            "connected": obj.connected,
            "last_checked_at": obj.connection_last_checked_at,
        }

Included Serializers (JSON:API)

class ScanSerializer(RLSSerializer):
    included_serializers = {
        "provider": "api.v1.serializers.ProviderIncludeSerializer",
    }

Sensitive Data Masking

def to_representation(self, instance):
    data = super().to_representation(instance)
    # Mask by default, expose only on explicit request
    fields_param = self.context.get("request").query_params.get("fields[my-model]", "")
    if "api_key" in fields_param:
        data["api_key"] = instance.api_key_decoded
    else:
        data["api_key"] = "****" if instance.api_key else None
    return data

---

ViewSet Patt

Read more
@prowler-cloud
Ships withprowler

Prowler is the world’s most widely used Open-Source Cloud Security Platform that automates security and compliance across any cloud environment.

15k39 skills1 MCP
Get the whole plugin

Stars on GitHub

Momentum

stars in total
15.2k
Star history of prowler-cloud/prowler on GitHub, a chart by star-history.com

Other skills on prowler.

ai-sdk-5

ai-sdk-5

Vercel AI SDK patterns for the installed major (ui/package.json: `ai` 6.x, `@ai-sdk/react` 3.x). The skill name is historical;…

gh-aw

gh-aw

Create and maintain GitHub Agentic Workflows (gh-aw) for Prowler. Trigger: When creating agentic workflows, modifying gh-aw…

jsonapi

jsonapi

Strict JSON:API v1.1 specification compliance. Trigger: When creating or modifying API endpoints, reviewing API responses, or…

nextjs-16

nextjs-16

Next.js 16 App Router patterns. Trigger: When working in Next.js App Router (app/), Server Components vs Client Components,…

More skills from other plugins.

api-security

api-security

Use for authorized security assessment of REST, GraphQL, WebSocket, or SOAP APIs, including discovery, authentication,…

@zhaoxuya520@zhaoxuya520Install Skill
nemoclaw-user-guide

nemoclaw-user-guide

Find official NemoClaw documentation for installation, configuration, operation, or troubleshooting. Also handles requested docs…

nemoclaw10d23k
@nvidia@nvidiaInstall Skill

Questions about django-drf.

Which coding agents can use django-drf?
django-drf installs in Claude Code, Codex, Cursor, Gemini CLI, GitHub Copilot, OpenCode and Windsurf with the skills CLI, using each agent's flag shown on this page. Flowy's auto-invocation, which fires a skill when your prompt matches, runs in Claude Code only.