django-drf by prowler-cloud/prowler
npx skills add https://github.com/prowler-cloud/prowler --skill django-drffilterset_class(而非 filterset_fields)BaseWriteSerializer)get_queryset() 中使用 select_related/prefetch_related 以避免 N+1 问题get_queryset() 中处理 swagger_fake_view 以生成模式广告位招租
在这里展示您的产品或服务
触达数万 AI 开发者,精准高效
SerializerMethodField 的 OpenAPI 文档使用 @extend_schema_fieldtrailing_slash=False)注意:
swagger_fake_view是 drf-spectacular 特有的,用于生成 OpenAPI 模式。
实现新端点时,按顺序检查这些模式:
---|---|---|---
1 | 模型 | api/models.py | UUID 主键,inserted_at/updated_at,JSONAPIMeta.resource_name
2 | 视图集 | api/base_views.py,api/v1/views.py | 继承 BaseRLSViewSet,get_queryset() 需预防 N+1
3 | 序列化器 | api/v1/serializers.py | 分离 读取/创建/更新/包含,继承 BaseWriteSerializer
4 | 过滤器 | api/filters.py | 使用 filterset_class,继承基础过滤器类
5 | 权限 | api/base_views.py | required_permissions,set_required_permissions()
6 | 分页 | api/pagination.py | 自定义分页类(如果需要)
7 | URL 路由 | api/v1/urls.py | trailing_slash=False,kebab-case 路径
8 | OpenAPI 模式 | api/v1/views.py | 使用 drf-spectacular 的 @extend_schema_view
9 | 测试 | api/tests/test_views.py | JSON:API 内容类型,fixture 模式
完整文件路径 : 参见 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 → BaseProviderFilter
通过 Scan 外键关联 → BaseScanProviderFilter
无 provider 关联 → FilterSet
受 RLS 保护的模型 → BaseRLSViewSet(最常见)
租户操作 → BaseTenantViewset
用户操作 → BaseUserViewset
无需 RLS → BaseViewSet(罕见)
单字模型 → 复数小写 (Provider → providers)
多字模型 → 复数小写 kebab (ProviderGroup → provider-groups)
连接/关联模型 → 父子模式 (UserRoleRelationship → user-roles)
聚合/概览 → 描述性 kebab 复数 (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= 的稀疏字段)
class ProviderIncludeSerializer(RLSSerializer):
class Meta:
model = Provider
fields = ["id", "alias"] # 最少字段
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,
}
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
始终结合 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 内省视图集以生成 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 的模型"""
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 的模型(Scan 外键关联到 Provider)"""
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
# 单值包含
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": "至少需要一个日期过滤器",
"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)
def get_filterset_class(self):
if self.action in ["latest", "metadata_latest"]:
return LatestFindingFilter
return FindingFilter
class Meta:
model = Finding
filter_overrides = {
FindingDeltaEnumField: {"filter_class": CharFilter},
StatusEnumField: {"filter_class": CharFilter},
SeverityEnumField: {"filter_class": CharFilter},
}
对于具有昂贵连接的大型查询集:
class PaginateByPkMixin:
def paginate_by_pk(self, request, base_queryset, manager,
select_related=None, prefetch_related=None):
# 1. 仅获取主键(廉价)
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. 重新排序以保持数据库顺序
queryset = sorted(queryset, key=lambda obj: page.index(obj.id))
return self.get_paginated_response(self.get_serializer(queryset, many=True).data)
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>Serializer | ProviderSerializer |
| 序列化器(创建) | <Model>CreateSerializer | ProviderCreateSerializer |
| 序列化器(更新) | <Model>UpdateSerializer | ProviderUpdateSerializer |
| 序列化器(包含) | <Model>IncludeSerializer | ProviderIncludeSerializer |
| 过滤器 | <Model>Filter | ProviderFilter |
| 视图集 | <Model>ViewSet | ProviderViewSet |
from drf_spectacular.utils import extend_schema, extend_schema_view
@extend_schema_view(
list=extend_schema(tags=["Provider"], summary="列出所有提供者"),
retrieve=extend_schema(tags=["Provider"], summary="检索提供者"),
create=extend_schema(tags=["Provider"], summary="创建提供者"),
)
@extend_schema(tags=["Provider"])
class ProviderViewSet(BaseRLSViewSet):
pass
完整示例 : 参见 assets/security_patterns.py
| 模式 | 要点 |
|---|---|
| 输入验证 | 使用 validate_<field>() 进行清理,validate() 用于跨字段验证 |
| 防止批量赋值 | 始终 使用显式的 fields 列表,切勿 使用 __all__ 或 exclude |
| 对象级权限 | 实现 has_object_permission() 进行所有权检查 |
| 速率限制 | 配置 DEFAULT_THROTTLE_RATES,对敏感端点使用每视图限制 |
| 防止信息泄露 | 通用错误消息,对未授权返回 404 而非 403(防止枚举) |
| SQL 注入 | 始终 使用 ORM 参数化,切勿 在原始 SQL 中使用字符串插值 |
# 序列化器中的输入验证
def validate_uid(self, value):
value = value.strip().lower()
if not re.match(r'^[a-z0-9-]+$', value):
raise serializers.ValidationError("无效格式")
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("未找到资源") # 通用,不包含内部 ID
# 开发
cd api && poetry run python src/backend/manage.py runserver
cd api && poetry run python src/backend/manage.py shell
# 数据库
cd api && poetry run python src/backend/manage.py makemigrations
cd api && poetry run python src/backend/manage.py migrate
# 测试
cd api && poetry run pytest -x --tb=short
cd api && poetry run make lint
前提条件: 安装 Context7 MCP 服务器以获取最新的文档查找。
在实现或调试时,通过 mcp_context7_query-docs 查询这些库:
| 库 | Context7 ID | 用途 |
|---|---|---|
| Django | /websites/djangoproject_en_5_2 | 模型,ORM,迁移 |
| DRF | /websites/django-rest-framework | 视图集,序列化器,权限 |
| drf-spectacular | /tfranzel/drf-spectacular | OpenAPI 模式,@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。
每周安装量
142
仓库
GitHub 星标
13.4K
首次出现
2026年1月21日
安全审计
安装于
opencode134
codex130
gemini-cli129
github-copilot128
cursor116
amp110
filterset_class for complex filtering (not filterset_fields)BaseWriteSerializer)select_related/prefetch_related in get_queryset() to avoid N+1swagger_fake_view in get_queryset() for schema generation@extend_schema_field for OpenAPI docs on SerializerMethodFieldtrailing_slash=False)Note:
swagger_fake_viewis specific to drf-spectacular for OpenAPI schema generation.
When implementing a new endpoint, review these patterns in order:
---|---|---|---
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 | | | Use , inherit base filter classes
5 | | | ,
6 | | | Custom pagination class if needed
7 | | | , kebab-case paths
8 | | | with drf-spectacular
9 | | | JSON:API content type, fixture patterns
Full file paths : See references/file-locations.md
GET list/retrieve → <Model>Serializer
POST create → <Model>CreateSerializer
PATCH update → <Model>UpdateSerializer
?include=... → <Model>IncludeSerializer
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
Direct FK to Provider → BaseProviderFilter
FK via Scan → BaseScanProviderFilter
No provider relation → FilterSet
RLS-protected model → BaseRLSViewSet (most common)
Tenant operations → BaseTenantViewset
User operations → BaseUserViewset
No RLS required → BaseViewSet (rare)
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)
# 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
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,
}
class ScanSerializer(RLSSerializer):
included_serializers = {
"provider": "api.v1.serializers.ProviderIncludeSerializer",
}
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
Always combine swagger_fake_view check with select_related/prefetch_related:
def get_queryset(self):
# REQUIRED: Return empty queryset for OpenAPI schema generation
if getattr(self, "swagger_fake_view", False):
return Provider.objects.none()
# N+1 prevention: eager load relationships
return Provider.objects.select_related(
"tenant",
).prefetch_related(
"provider_groups",
Prefetch("tags", queryset=ProviderTag.objects.filter(tenant_id=self.request.tenant_id)),
)
Why swagger_fake_view? drf-spectacular introspects ViewSets to generate OpenAPI schemas. Without this check, it executes real queries and can fail without request context.
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 = [] # Read-only = no permission
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
# Detail action (operates on single object)
@action(detail=True, methods=["post"], url_name="connection")
def connection(self, request, pk=None):
instance = self.get_object()
# Process instance...
# List action (operates on collection)
@action(detail=False, methods=["get"], url_name="metadata")
def metadata(self, request):
queryset = self.filter_queryset(self.get_queryset())
# Aggregate over queryset...
class BaseProviderFilter(FilterSet):
"""For models with direct FK to Provider"""
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):
"""For models with FK to Scan (Scan has FK to Provider)"""
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
# Single value contains
region = CharFilter(method="filter_region")
def filter_region(self, queryset, name, value):
return queryset.filter(resource_regions__contains=[value])
# Multi-value overlap
region__in = CharInFilter(field_name="resource_regions", lookup_expr="overlap")
def filter_queryset(self, queryset):
# Require date filter for performance
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",
}])
# Validate max range
if date_range > settings.FINDINGS_MAX_DAYS_IN_RANGE:
raise ValidationError(...)
return super().filter_queryset(queryset)
def get_filterset_class(self):
if self.action in ["latest", "metadata_latest"]:
return LatestFindingFilter
return FindingFilter
class Meta:
model = Finding
filter_overrides = {
FindingDeltaEnumField: {"filter_class": CharFilter},
StatusEnumField: {"filter_class": CharFilter},
SeverityEnumField: {"filter_class": CharFilter},
}
For large querysets with expensive joins:
class PaginateByPkMixin:
def paginate_by_pk(self, request, base_queryset, manager,
select_related=None, prefetch_related=None):
# 1. Get PKs only (cheap)
pk_list = base_queryset.values_list("id", flat=True)
page = self.paginate_queryset(pk_list)
# 2. Fetch full objects for just the page
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. Re-sort to preserve DB ordering
queryset = sorted(queryset, key=lambda obj: page.index(obj.id))
return self.get_paginated_response(self.get_serializer(queryset, many=True).data)
def get_tags(self, obj):
# Use prefetched tags if available
if hasattr(obj, "prefetched_tags"):
return {tag.key: tag.value for tag in obj.prefetched_tags}
# Fallback (causes N+1 if not prefetched)
return obj.get_tags(self.context.get("tenant_id"))
| Entity | Pattern | Example |
|---|---|---|
| Serializer (read) | <Model>Serializer | ProviderSerializer |
| Serializer (create) | <Model>CreateSerializer | ProviderCreateSerializer |
| Serializer (update) | <Model>UpdateSerializer | ProviderUpdateSerializer |
| Serializer (include) |
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
Full examples : See assets/security_patterns.py
| Pattern | Key Points |
|---|---|
| Input Validation | Use validate_<field>() for sanitization, validate() for cross-field |
| Prevent Mass Assignment | ALWAYS use explicit fields list, NEVER __all__ or exclude |
| Object-Level Permissions | Implement has_object_permission() for ownership checks |
| Rate Limiting | Configure , use per-view throttles for sensitive endpoints |
# Input validation in serializer
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
# Explicit fields (prevent mass assignment)
class Meta:
fields = ["name", "email"] # GOOD: whitelist
read_only_fields = ["id", "inserted_at"] # System fields
# Object permission
class IsOwnerOrReadOnly(BasePermission):
def has_object_permission(self, request, view, obj):
if request.method in SAFE_METHODS:
return True
return obj.owner == request.user
# Throttling for sensitive endpoints
class BurstRateThrottle(UserRateThrottle):
rate = "10/minute"
# Safe error messages (prevent enumeration)
def get_object(self):
try:
return super().get_object()
except Http404:
raise NotFound("Resource not found") # Generic, no internal IDs
# Development
cd api && poetry run python src/backend/manage.py runserver
cd api && poetry run python src/backend/manage.py shell
# Database
cd api && poetry run python src/backend/manage.py makemigrations
cd api && poetry run python src/backend/manage.py migrate
# Testing
cd api && poetry run pytest -x --tb=short
cd api && poetry run make lint
Prerequisite: Install Context7 MCP server for up-to-date documentation lookup.
When implementing or debugging, query these libraries via mcp_context7_query-docs:
| Library | Context7 ID | Use For |
|---|---|---|
| Django | /websites/djangoproject_en_5_2 | Models, ORM, migrations |
| DRF | /websites/django-rest-framework | ViewSets, serializers, permissions |
| drf-spectacular | /tfranzel/drf-spectacular | OpenAPI schema, @extend_schema |
Example queries:
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")
Note: Use
mcp_context7_resolve-library-idfirst if you need to find the correct library ID.
Weekly Installs
142
Repository
GitHub Stars
13.4K
First Seen
Jan 21, 2026
Security Audits
Gen Agent Trust HubPassSocketPassSnykPass
Installed on
opencode134
codex130
gemini-cli129
github-copilot128
cursor116
amp110
Lark CLI IM 即时消息管理工具:机器人/用户身份操作聊天、消息、文件下载
34,600 周安装
浏览器自动化指南:使用 browse CLI 与 Claude 实现网页交互自动化
971 周安装
OKX x402支付技能:区块链支付授权与凭证生成工具
971 周安装
技术文档编写指南:为不同受众撰写清晰可维护的API、README、架构文档
990 周安装
Railway CLI 部署指南:使用 railway up 命令快速部署代码到 Railway 平台
942 周安装
Flutter Platform Views 实现指南:Android/iOS/macOS原生视图与Web嵌入教程
945 周安装
Flutter插件生成器:快速创建标准/FFI/联合插件,支持多平台原生开发
946 周安装
api/filters.pyfilterset_classapi/base_views.pyrequired_permissionsset_required_permissions()api/pagination.pyapi/v1/urls.pytrailing_slash=Falseapi/v1/views.py@extend_schema_viewapi/tests/test_views.py<Model>IncludeSerializerProviderIncludeSerializer |
| Filter | <Model>Filter | ProviderFilter |
| ViewSet | <Model>ViewSet | ProviderViewSet |
DEFAULT_THROTTLE_RATES| Prevent Info Disclosure | Generic error messages, return 404 not 403 for unauthorized (prevents enumeration) |
| SQL Injection | ALWAYS use ORM parameterization, NEVER string interpolation in raw SQL |