Agent Skills by ALSEL
Anthropic Claudeソフトウェア開発⭐ リポ 0品質スコア 50/100

django-drf

Django REST Frameworkのパターンを提供します。ViewSet・シリアライザー・ルーター・パーミッション・フィルターセットなど、汎用的なDRF APIを実装する際にトリガーされます。Prowler API固有の機能(RLS/RBAC/プロバイダー)には、prowler-apiスキルも併用してください。

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.

SKILL.md 本文

重要なパターン

  • シリアライザーは常に操作ごとに分離する: Read / Create / Update / Include
  • 複雑なフィルタリングには常に filterset_class を使用する (filterset_fields ではなく)
  • 書き込みシリアライザーで常に不明なフィールドを検証する (BaseWriteSerializer を継承)
  • get_queryset() で常に select_related/prefetch_related を使用して N+1 を回避する
  • スキーマ生成のために常に get_queryset()swagger_fake_view を処理する
  • SerializerMethodField の OpenAPI ドキュメント用に常に @extend_schema_field を使用する
  • シリアライザーにビジネスロジックを絶対に置かない - services/utils を使用する
  • 自動増分 PK を絶対に使用しない - UUIDv4 または UUIDv7 を使用する
  • URL で末尾スラッシュを絶対に使用しない (trailing_slash=False)

注: swagger_fake_view は OpenAPI スキーマ生成のための drf-spectacular に固有です。


実装チェックリスト

新しいエンドポイントを実装する場合、これらのパターンを順序に従って確認してください:

#パターンリファレンス主要ポイント
1Modelsapi/models.pyUUID PK、inserted_at/updated_atJSONAPIMeta.resource_name
2ViewSetsapi/base_views.pyapi/v1/views.pyBaseRLSViewSet を継承、N+1 防止を伴う get_queryset()
3Serializersapi/v1/serializers.pyRead/Create/Update/Include を分離、BaseWriteSerializer を継承
4Filtersapi/filters.pyfilterset_class を使用、基本フィルタークラスを継承
5Permissionsapi/base_views.pyrequired_permissionsset_required_permissions()
6Paginationapi/pagination.py必要に応じてカスタムページング クラス
7URL Routingapi/v1/urls.pytrailing_slash=False、ケバブケース パス
8OpenAPI Schemaapi/v1/views.pydrf-spectacular を使用した @extend_schema_view
9Testsapi/tests/test_views.pyJSON:API コンテンツ タイプ、フィクスチャ パターン

完全なファイル パス: references/file-locations.md を参照してください


デシジョン ツリー

どのシリアライザーを選ぶか?

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

どの基本シリアライザーを選ぶか?

読み取り専用シリアライザー    → BaseModelSerializerV1
tenant_id での作成            → RLSSerializer + BaseWriteSerializer (作成時に tenant_id を自動注入)
検証を伴う更新                → BaseWriteSerializer (tenant_id は既にオブジェクトに存在)
非モデル データ               → BaseSerializerV1

どのフィルター基本クラスを選ぶか?

Provider への直接 FK  → BaseProviderFilter
Scan 経由の FK        → BaseScanProviderFilter
Provider 関連なし     → FilterSet

どの基本 ViewSet を選ぶか?

RLS保護モデル    → BaseRLSViewSet (最も一般的)
テナント操作     → BaseTenantViewset
ユーザー操作     → BaseUserViewset
RLS不要          → BaseViewSet (稀)

リソース名の形式は?

単語モデル       → 複数形小文字           (Provider → providers)
複合単語モデル   → 複数形小文字ケバブ     (ProviderGroup → provider-groups)
Through/join     → 親子パターン           (UserRoleRelationship → user-roles)
集約/概要        → 説明的なケバブ複数形   (ComplianceOverview → compliance-overviews)

シリアライザー パターン

基本クラス階層

# 読み取りシリアライザー (最も一般的)
class ProviderSerializer(RLSSerializer):
    class Meta:
        model = Provider
        fields = ["id", "provider", "uid", "alias", "connected", "inserted_at"]

# 書き込みシリアライザー (不明なフィールドを検証)
class ProviderCreateSerializer(RLSSerializer, BaseWriteSerializer):
    class Meta:
        model = Provider
        fields = ["provider", "uid", "alias"]

# Include シリアライザー (?include= 用の疎なフィールド)
class ProviderIncludeSerializer(RLSSerializer):
    class Meta:
        model = Provider
        fields = ["id", "alias"]  # 最小限のフィールド

SerializerMethodField と 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,
        }

インクルード シリアライザー (JSON:API)

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

機密データのマスキング

def to_representation(self, instance):
    data = super().to_representation(instance)
    # デフォルトでマスク、明示的なリクエスト時のみ公開
    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 パターン

get_queryset() と N+1 防止

常に swagger_fake_view チェックと select_related/prefetch_related を組み合わせる:

def get_queryset(self):
    # 必須: OpenAPI スキーマ生成のための空のクエリセットを返す
    if getattr(self, "swagger_fake_view", False):
        return Provider.objects.none()

    # N+1 防止: 関連を先読みする
    return Provider.objects.select_related(
        "tenant",
    ).prefetch_related(
        "provider_groups",
        Prefetch("tags", queryset=ProviderTag.objects.filter(tenant_id=self.request.tenant_id)),
    )

swagger_fake_view とは? drf-spectacular は ViewSet を内省して OpenAPI スキーマを生成します。このチェックがないと、リクエスト コンテキストなしで実際のクエリを実行し、失敗することができます。

アクション固有のシリアライザー

def get_serializer_class(self):
    if self.action == "create":
        return ProviderCreateSerializer
    elif self.action == "partial_update":
        return ProviderUpdateSerializer
    elif self.action in ["connection", "destroy"]:
        return TaskSerializer
    return ProviderSerializer

アクション単位での動的パーミッション

class ProviderViewSet(BaseRLSViewSet):
    required_permissions = [Permissions.MANAGE_PROVIDERS]

    def set_required_permissions(self):
        if self.action in ["list", "retrieve"]:
            self.required_permissions = []  # 読み取り専用 = パーミッション不要
        else:
            self.required_permissions = [Permissions.MANAGE_PROVIDERS]

キャッシュ デコレーター

from django.utils.decorators import method_decorator
from django.views.decorators.cache import cache_control

CACHE_DECORATOR = cache_control(
    max_age=django_settings.CACHE_MAX_AGE,
    stale_while_revalidate=django_settings.CACHE_STALE_WHILE_REVALIDATE,
)

@method_decorator(CACHE_DECORATOR, name="list")
@method_decorator(CACHE_DECORATOR, name="retrieve")
class ProviderViewSet(BaseRLSViewSet):
    pass

カスタム アクション

# 詳細アクション (単一オブジェクトで動作)
@action(detail=True, methods=["post"], url_name="connection")
def connection(self, request, pk=None):
    instance = self.get_object()
    # インスタンスを処理...

# リスト アクション (コレクション上で動作)
@action(detail=False, methods=["get"], url_name="metadata")
def metadata(self, request):
    queryset = self.filter_queryset(self.get_queryset())
    # クエリセット上で集約...

フィルター パターン

基本フィルター クラス

class BaseProviderFilter(FilterSet):
    """Provider への直接 FK を持つモデル用"""
    provider_id = UUIDFilter(field_name="provider__id", lookup_expr="exact")
    provider_id__in = UUIDInFilter(field_name="provider__id", lookup_expr="in")
    provider_type = ChoiceFilter(field_name="provider__provider", choices=Provider.ProviderChoices.choices)

class BaseScanProviderFilter(FilterSet):
    """Scan への FK を持つモデル用 (Scan は Provider への FK を持つ)"""
    provider_id = UUIDFilter(field_name="scan__provider__id", lookup_expr="exact")

カスタム複数値フィルター

class UUIDInFilter(BaseInFilter, UUIDFilter):
    pass

class CharInFilter(BaseInFilter, CharFilter):
    pass

class ChoiceInFilter(BaseInFilter, ChoiceFilter):
    pass

ArrayField フィルタリング

# 単一値を含む
region = CharFilter(method="filter_region")

def filter_region(self, queryset, name, value):
    return queryset.filter(resource_regions__contains=[value])

# 複数値の重複
region__in = CharInFilter(field_name="resource_regions", lookup_expr="overlap")

日付範囲検証

def filter_queryset(self, queryset):
    # パフォーマンスのため日付フィルターを必須にする
    if not (date_filters_provided):
        raise ValidationError([{
            "detail": "At least one date filter is required",
            "status": 400,
            "source": {"pointer": "/data/attributes/inserted_at"},
            "code": "required",
        }])

    # 最大範囲を検証
    if date_range > settings.FINDINGS_MAX_DAYS_IN_RANGE:
        raise ValidationError(...)

    return super().filter_queryset(queryset)

動的 FilterSet 選択

def get_filterset_class(self):
    if self.action in ["latest", "metadata_latest"]:
        return LatestFindingFilter
    return FindingFilter

Enum フィールド オーバーライド

class Meta:
    model = Finding
    filter_overrides = {
        FindingDeltaEnumField: {"filter_class": CharFilter},
        StatusEnumField: {"filter_class": CharFilter},
        SeverityEnumField: {"filter_class": CharFilter},
    }

パフォーマンス パターン

PaginateByPkMixin

大規模なクエリセットと高コストなジョイン用:

class PaginateByPkMixin:
    def paginate_by_pk(self, request, base_queryset, manager,
                       select_related=None, prefetch_related=None):
        # 1. PK のみ取得 (低コスト)
        pk_list = base_queryset.values_list("id", flat=True)
        page = self.paginate_queryset(pk_list)

        # 2. ページ用の完全なオブジェクトのみフェッチ
        queryset = manager.filter(id__in=page)
        if select_related:
            queryset = queryset.select_related(*select_related)
        if prefetch_related:
            queryset = queryset.prefetch_related(*prefetch_related)

        # 3. DB の順序を保持するため再ソート
        queryset = sorted(queryset, key=lambda obj: page.index(obj.id))
        return self.get_paginated_response(self.get_serializer(queryset, many=True).data)

シリアライザー内での Prefetch

def get_tags(self, obj):
    # 利用可能な場合はプリフェッチされたタグを使用
    if hasattr(obj, "prefetched_tags"):
        return {tag.key: tag.value for tag in obj.prefetched_tags}
    # フォールバック (プリフェッチされていない場合 N+1 が発生)
    return obj.get_tags(self.context.get("tenant_id"))

命名規則

エンティティパターン
シリアライザー (読み取り)<Model>SerializerProviderSerializer
シリアライザー (作成)<Model>CreateSerializerProviderCreateSerializer
シリアライザー (更新)<Model>UpdateSerializerProviderUpdateSerializer
シリアライザー (include)<Model>IncludeSerializerProviderIncludeSerializer
フィルター<Model>FilterProviderFilter
ViewSet<Model>ViewSetProviderViewSet

OpenAPI ドキュメンテーション

from drf_spectacular.utils import extend_schema, extend_schema_view

@extend_schema_view(
    list=extend_schema(tags=["Provider"], summary="List all providers"),
    retrieve=extend_schema(tags=["Provider"], summary="Retrieve provider"),
    create=extend_schema(tags=["Provider"], summary="Create provider"),
)
@extend_schema(tags=["Provider"])
class ProviderViewSet(BaseRLSViewSet):
    pass

API セキュリティ パターン

完全な例: assets/security_patterns.py を参照してください

パターン主要ポイント
入力検証validate_<field>() をサニタイズに使用、validate() をフィールド間検証に使用
マスアサインメント防止常に明示的な fields リストを使用、__all__ または exclude を絶対に使用しない
オブジェクト レベルのパーミッション所有権チェックのために has_object_permission() を実装
レート制限DEFAULT_THROTTLE_RATES を構成、機密エンドポイント用にビュー単位のスロットルを使用
情報開示防止一般的なエラー メッセージ、許可されていない場合は 403 ではなく 404 を返す (列挙を防止)
SQL インジェクション常に ORM パラメータ化を使用、生 SQL で文字列補間を絶対に使用しない

クイック リファレンス

# シリアライザーでの入力検証
def validate_uid(self, value):
    value = value.strip().lower()
    if not re.match(r'^[a-z0-9-]+$', value):
        raise serializers.ValidationError("Invalid format")
    return value

# 明示的なフィールド (マスアサインメント防止)
class Meta:
    fields = ["name", "email"]  # 良い: ホワイトリスト
    read_only_fields = ["id", "inserted_at"]  # システムフィールド

# オブジェクト パーミッション
class IsOwnerOrReadOnly(BasePermission):
    def has_object_permission(self, request, view, obj):
        if request.method in SAFE_METHODS:
            return True
        return obj.owner == request.user

# 機密エンドポイント用のスロットル
class BurstRateThrottle(UserRateThrottle):
    rate = "10/minute"

# 安全なエラー メッセージ (列挙防止)
def get_object(self):
    try:
        return super().get_object()
    except Http404:
        raise NotFound("Resource not found")  # 一般的で内部 ID なし

コマンド

# 開発
cd api && uv run python src/backend/manage.py runserver
cd api && uv run python src/backend/manage.py shell

# データベース
cd api && uv run python src/backend/manage.py makemigrations
cd api && uv run python src/backend/manage.py migrate

# テスト
cd api && uv run pytest -x --tb=short
cd api && uv run make lint

リソース

ローカル リファレンス

  • ファイル位置: references/file-locations.md を参照してください
  • JSON:API 規約: references/json-api-conventions.md を参照してください
  • セキュリティ パターン: assets/security_patterns.py を参照してください

Context7 MCP (推奨)

前提: 最新のドキュメント検索のために Context7 MCP サーバーをインストールしてください。

実装またはデバッグ時に mcp_context7_query-docs を使用してこれらのライブラリをクエリしてください:

ライブラリContext7 ID用途
Django/websites/djangoproject_en_5_2モデル、ORM、マイグレーション
DRF/websites/django-rest-frameworkViewSets、シリアライザー、パーミッション
drf-spectacular/tfranzel/drf-spectacularOpenAPI スキーマ、@extend_schema

クエリ例:

mcp_context7_query-docs(libraryId="/websites/django-rest-framework", query="ViewSet get_queryset best practices")
mcp_context7_query-docs(libraryId="/tfranzel/drf-spectacular", query="extend_schema examples for custom actions")
mcp_context7_query-docs(libraryId="/websites/djangoproject_en_5_2", query="model constraints and indexes")

注: 正しいライブラリ ID を見つける必要がある場合は、まず mcp_context7_resolve-library-id を使用してください。

外部ドキュメント

ライセンス: Apache-2.0(寛容ライセンスのため全文を引用しています) · 原本リポジトリ

詳細情報

作者
prowler-cloud
リポジトリ
prowler-cloud/prowler
ライセンス
Apache-2.0
最終更新
不明

Source: https://github.com/prowler-cloud/prowler / ライセンス: Apache-2.0

関連スキル

汎用ソフトウェア開発⭐ リポ 39,967

doubt-driven-development

重要な判断はすべて、本番環境への展開前に新しい視点から対抗的レビューを実施します。速度より正確性が重要な場合、不慣れなコードを扱う場合、本番環境・セキュリティに関わるロジック・取り消し不可の操作など影響度が高い場合、または後でバグを修正するよりも今検証する方が効率的な場合に活用してください。

by addyosmani
汎用ソフトウェア開発⭐ リポ 1,175

apprun-skills

TypeScriptを使用したAppRunアプリケーションのMVU設計に関する総合的なガイダンスが得られます。コンポーネントパターン、イベントハンドリング、状態管理(非同期ジェネレータを含む)、パラメータと保護機能を備えたルーティング・ナビゲーション、vistestを使用したテストに対応しています。AppRunコンポーネントの設計・レビュー、ルートの配線、状態フローの管理、AppRunテストの作成時に活用してください。

by yysun
OpenAIソフトウェア開発⭐ リポ 797

desloppify

コードベースのヘルスチェックと技術負債の追跡ツールです。コード品質、技術負債、デッドコード、大規模ファイル、ゴッドクラス、重複関数、コードスメル、命名規則の問題、インポートサイクル、結合度の問題についてユーザーが質問した場合に使用してください。また、ヘルススコアの確認、次の改善項目の提案、クリーンアップ計画の作成をリクエストされた際にも対応します。29言語に対応しています。

by Git-on-my-level
汎用ソフトウェア開発⭐ リポ 39,967

debugging-and-error-recovery

テストが失敗したり、ビルドが壊れたり、動作が期待と異なったり、予期しないエラーが発生したりした場合に、体系的な根本原因デバッグをガイドします。推測ではなく、根本原因を見つけて修正するための体系的なアプローチが必要な場合に使用してください。

by addyosmani
汎用ソフトウェア開発⭐ リポ 39,967

test-driven-development

テスト駆動開発により実装を進めます。ロジックの実装、バグの修正、動作の変更など、あらゆる場面で活用できます。コードが正常に動作することを証明する必要がある場合、バグ報告を受けた場合、既存機能を修正する予定がある場合に使用してください。

by addyosmani
汎用ソフトウェア開発⭐ リポ 39,967

incremental-implementation

変更を段階的に実施します。複数のファイルに影響する機能や変更を実装する場合に使用してください。大量のコードを一度に書こうとしている場合や、タスクが一度では完結できないほど大きい場合に活用します。

by addyosmani
本サイトは GitHub 上で公開されているオープンソースの SKILL.md ファイルをクロール・インデックス化したものです。 各スキルの著作権は原作者に帰属します。掲載に問題がある場合は info@alsel.co.jp または /takedown フォームよりご連絡ください。
原作者: prowler-cloud · prowler-cloud/prowler · ライセンス: Apache-2.0