improving-drf-endpoints
Compare original and translation side by side
🇺🇸
Original
English🇨🇳
Translation
ChineseImproving DRF Endpoints
优化DRF端点
Overview
概述
Serializer fields are the source of truth for PostHog's entire type pipeline:
text
Django serializer → drf-spectacular → OpenAPI JSON → Orval → Zod schemas → MCP toolsEvery , every field type, every annotation flows downstream.
A missing means an agent guessing at parameters.
A bare means in the generated Zod schema.
Getting the serializer right means every consumer — frontend types, MCP tools, API docs — gets correct types and descriptions automatically.
help_text@extend_schemahelp_textListField()z.unknown()Serializer字段是PostHog整个类型流水线的唯一可信源:
text
Django serializer → drf-spectacular → OpenAPI JSON → Orval → Zod schemas → MCP tools每个、每个字段类型、每个注解都会向下流转。缺失会导致Agent需要猜测参数含义;未指定类型的会在生成的Zod Schema中变成。确保Serializer的正确性,就能让所有下游消费者——前端类型、MCP工具、API文档——自动获得正确的类型与描述。
help_text@extend_schemahelp_textListField()z.unknown()When to use
适用场景
- Editing or reviewing any file that defines a or
SerializerViewSet - Fixing OpenAPI spec warnings or generated type issues
- Preparing an endpoint for MCP tool exposure
- Code review of API changes
- 编辑或评审任何定义了或
Serializer的文件ViewSet - 修复OpenAPI规范警告或生成的类型问题
- 为MCP工具暴露准备端点
- API变更的代码评审
Audit checklist
审计检查清单
Triage: check the generated output first
优先级排序:先检查生成的输出结果
Before diving into Python, look at the committed generated types to see what's broken.
Find the generated files for the endpoint's product:
- Core API:
frontend/src/generated/core/ - Product APIs:
products/<product>/frontend/generated/
Each has two files:
- — TypeScript interfaces derived from serializers. Search for the serializer name and look for
api.schemas.tstypes (bareunknown/ListField), missing JSDoc descriptions (missingJSONField), or overly generichelp_textshapes.Record<string, unknown> - — API client functions. Check if the endpoint's operation exists at all — if missing, the viewset method likely lacks
api.ts.@extend_schema
This tells you exactly which fields and endpoints to prioritize.
在深入Python代码之前,先查看已提交的生成类型,确认存在哪些问题。找到对应产品端点的生成文件:
- 核心API:
frontend/src/generated/core/ - 产品API:
products/<product>/frontend/generated/
每个目录下有两个关键文件:
- —— 从Serializer派生的TypeScript接口。搜索Serializer名称,查看是否存在
api.schemas.ts类型(来自未指定子类型的unknown/ListField)、缺失JSDoc描述(对应缺失的JSONField),或过于宽泛的help_text结构。Record<string, unknown> - —— API客户端函数。检查端点的操作是否存在——如果缺失,说明ViewSet方法可能未添加
api.ts注解。@extend_schema
这能帮你明确需要优先处理的字段和端点。
Serializer fields
Serializer字段
Work through this list for every serializer and viewset you touch.
- Every field has — describes purpose, format, constraints, valid values
help_text - No bare or
ListField()— always specifyDictField()with a typed serializer or fieldchild= - No bare — create a custom field class with
JSONField()@extend_schema_field(TypedSchema) - has
SerializerMethodFieldon its@extend_schema_fieldmethodget_* - has explicit
ChoiceFieldwith all valid values listedchoices= - Read vs write serializers are separate when input shape differs from output
- Every success response is backed by a serializer — returning raw dicts or untyped lists means no generated types downstream
See serializer-fields.md for patterns and examples.
处理每个Serializer和ViewSet时,逐一核对以下清单:
- 所有字段都包含—— 描述字段用途、格式、约束条件及有效值
help_text - 禁止使用未指定类型的或
ListField()—— 必须通过DictField()指定类型化的Serializer或字段child= - 禁止使用未指定类型的—— 创建自定义字段类并添加
JSONField()注解@extend_schema_field(TypedSchema) - 的
SerializerMethodField方法需添加get_*注解@extend_schema_field - 需显式指定
ChoiceField参数,列出所有有效值choices= - 当输入与输出结构不同时,需将读、写Serializer分离
- 所有成功响应都需由Serializer提供支持 —— 返回原始字典或未类型化列表会导致下游无法生成类型
查看serializer-fields.md获取模式示例。
Viewset and action annotations
ViewSet与Action注解
- Every custom has
@actionor@extend_schema— without it, drf-spectacular discovers zero parameters@validated_request - Plain methods have schema annotations —
ViewSetwithModelViewSetis auto-discovered; plainserializer_classis notViewSet - is on the actual method (
@extend_schema,get,post,create), not on a helper or the class itselflist - Error responses are typed — use , not
OpenApiResponse(response=ErrorSerializer)OpenApiTypes.OBJECT - List endpoints declare pagination — reset with on custom actions that don't paginate
pagination_class=None - Prefer over manual
@validated_request+serializer.is_valid()— it handles both in one decorator@extend_schema - ViewSets outside need
products/— ViewSets in@extend_schema(tags=["<product>"])are auto-tagged via module path, but ViewSets inproducts/<name>/backend/orposthog/api/are not. Without the tag, the MCP scaffold and frontend type generator can't route the endpoint to the right productee/
Streaming endpoints: For SSE or streaming responses, use to document the request schema even though the response can't be fully typed.
@extend_schema(request=InputSerializer, responses={(200, "text/event-stream"): OpenApiTypes.STR})See viewset-annotations.md for patterns and examples.
- 所有自定义都需添加
@action或@extend_schema注解 —— 缺少注解的话,drf-spectacular无法发现任何参数@validated_request - 普通方法需添加Schema注解 —— 带有
ViewSet的serializer_class会被自动发现,但普通ModelViewSet不会ViewSet - 注解需添加在实际方法上(如
@extend_schema、get、post、create),而非辅助方法或类本身list - 错误响应需指定类型 —— 使用,而非
OpenApiResponse(response=ErrorSerializer)OpenApiTypes.OBJECT - 列表端点需声明分页配置 —— 对于不需要分页的自定义Action,需设置
pagination_class=None - 优先使用而非手动调用
@validated_request+serializer.is_valid()—— 该注解可同时处理验证与Schema生成@extend_schema - 目录外的ViewSet需添加
products/注解 ——@extend_schema(tags=["<product>"])下的ViewSet会通过模块路径自动打标签,但products/<name>/backend/或posthog/api/下的ViewSet不会。缺少标签会导致MCP脚手架与前端类型生成器无法将端点路由到正确产品ee/
流式端点: 对于SSE或流式响应,使用来记录请求Schema,尽管响应无法完全类型化。
@extend_schema(request=InputSerializer, responses={(200, "text/event-stream"): OpenApiTypes.STR})查看viewset-annotations.md获取模式示例。
Facade products (DataclassSerializer)
门面产品(DataclassSerializer)
For products using the facade pattern (e.g., ) with wrapping frozen dataclasses from :
visual_reviewDataclassSerializercontracts.py- Field types are auto-derived from the dataclass — fewer typing issues by design
- Focus on (dataclass fields don't carry it; add it on the serializer field overrides)
help_text - is already the standard pattern — verify response serializers are declared
@validated_request - tags and descriptions still need to be set on viewset methods
@extend_schema
对于使用门面模式的产品(如),其包装了来自的冻结数据类:
visual_reviewDataclassSerializercontracts.py- 字段类型会自动从数据类派生——设计上减少了类型问题
- 重点关注****(数据类字段不携带该属性,需在Serializer字段重写时添加)
help_text - ****已是标准模式——需验证响应Serializer是否已声明
@validated_request - 标签与描述仍需添加在ViewSet方法上
@extend_schema
Decision flowchart
决策流程图
dot
digraph audit {
rankdir=TB
node [shape=diamond fontsize=10]
edge [fontsize=9]
start [label="Serializer or\nViewSet file?" shape=box]
is_model [label="ModelViewSet with\nserializer_class?"]
is_plain [label="Plain ViewSet or\ncustom @action?"]
is_facade [label="DataclassSerializer\n(facade product)?"]
check_fields [label="Check fields:\nhelp_text, ListField,\nJSONField, ChoiceField" shape=box]
add_schema [label="Add @validated_request\nor @extend_schema to\nevery method" shape=box]
check_help [label="Focus on help_text\nand response declarations" shape=box]
check_responses [label="Check response types,\npagination, error schemas" shape=box]
start -> is_model
is_model -> check_fields [label="yes"]
is_model -> is_plain [label="no"]
is_plain -> add_schema [label="yes"]
is_plain -> is_facade [label="no"]
is_facade -> check_help [label="yes"]
check_fields -> check_responses
add_schema -> check_fields
check_help -> check_responses
}dot
digraph audit {
rankdir=TB
node [shape=diamond fontsize=10]
edge [fontsize=9]
start [label="Serializer or\nViewSet file?" shape=box]
is_model [label="ModelViewSet with\nserializer_class?"]
is_plain [label="Plain ViewSet or\ncustom @action?"]
is_facade [label="DataclassSerializer\n(facade product)?"]
check_fields [label="Check fields:\nhelp_text, ListField,\nJSONField, ChoiceField" shape=box]
add_schema [label="Add @validated_request\nor @extend_schema to\nevery method" shape=box]
check_help [label="Focus on help_text\nand response declarations" shape=box]
check_responses [label="Check response types,\npagination, error schemas" shape=box]
start -> is_model
is_model -> check_fields [label="yes"]
is_model -> is_plain [label="no"]
is_plain -> add_schema [label="yes"]
is_plain -> is_facade [label="no"]
is_facade -> check_help [label="yes"]
check_fields -> check_responses
add_schema -> check_fields
check_help -> check_responses
}Quick reference
快速参考
See quick-reference-table.md for a scannable "I see X, do Y" lookup.
See common-anti-patterns.md for before/after code pairs.
查看quick-reference-table.md获取可快速查阅的“遇到X,执行Y”对照表。
查看common-anti-patterns.md获取代码正反例对比。
Canonical examples in the codebase
代码库中的标准示例
- JSONField + @extend_schema_field:
posthog/api/alert.py - @validated_request:
products/tasks/backend/api.py - help_text + typed responses:
products/llm_analytics/backend/api/evaluation_summary.py - Facade product:
products/visual_review/backend/presentation/views.py
- JSONField + @extend_schema_field:
posthog/api/alert.py - @validated_request:
products/tasks/backend/api.py - help_text + 类型化响应:
products/llm_analytics/backend/api/evaluation_summary.py - 门面产品:
products/visual_review/backend/presentation/views.py
Related
相关资源
- Downstream: After fixing serializers, use the skill to scaffold MCP tools
implementing-mcp-tools - Pipeline docs:
docs/published/handbook/engineering/type-system.md - Mixins: (
posthog/api/mixins.pysource)@validated_request - drf-spectacular config: (
posthog/settings/web.py)SPECTACULAR_SETTINGS
- 下游操作: 修复Serializer后,可使用技能搭建MCP工具
implementing-mcp-tools - 流水线文档:
docs/published/handbook/engineering/type-system.md - Mixin: (
posthog/api/mixins.py的源码)@validated_request - drf-spectacular配置: (
posthog/settings/web.py)SPECTACULAR_SETTINGS