Files
chunyu_project/docs/ADRF_CONVERSION_GUIDE.md
T

75 lines
5.0 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# ADRF 异步化转换规范(chunyu_project 视图层)
## 目标
将 DRF 同步视图迁移到 ADRF(Asynchronous Django REST Framework)+ Django 5.x 异步 ORM,实现从视图到数据库的全链路非阻塞。
## 核心规则
### 1. 导入替换
- `from rest_framework.views import APIView` → `from adrf.views import APIView`
- `from rest_framework import generics` / `rest_framework.generics.XXX` → `from adrf import generics`(或 `from adrf.generics import ListCreateAPIView` 等)
- `from rest_framework.viewsets import ModelViewSet/ReadOnlyModelViewSet` → `from adrf.viewsets import ModelViewSet, ReadOnlyModelViewSet`
- `from rest_framework.mixins import ...` → `from adrf import mixins`
- 保留:`Response`, `status`, `permissions`, `filters`, `pagination`, `serializers`, `django_filters`, `drf_yasg` 的导入不变。
- `get_object_or_404` → `from adrf.generics import aget_object_or_404`(在 async 上下文中使用)
### 2. 处理器异步化
- 视图类中的 HTTP 方法处理器改为 `async def`(get/post/put/patch/delete/list/retrieve/create/update/destroy 及自定义 action)。
- ADRF 特性:类内任意一个处理器是 async,dispatch 即走事件循环;残留的同步处理器会被自动 `sync_to_async` 兜底(不会崩,但会占线程池——尽量全部转完)。
### 3. 同步 ORM → 异步 ORM(Django 5.x,逐个替换,不可遗漏)
| 同步 | 异步 |
|---|---|
| `Model.objects.get(...)` | `await Model.objects.aget(...)` |
| `filter(...).first()` | `await Model.objects.filter(...).afirst()` |
| `filter(...).exists()` | `await Model.objects.filter(...).aexists()` |
| `filter(...).count()` | `await Model.objects.filter(...).acount()` |
| `Model.objects.create(...)` | `await Model.objects.acreate(...)` |
| `obj.save()` | `await obj.asave()` |
| `obj.delete()` | `await obj.adelete()` |
| `queryset.update(...)` | `await queryset.aupdate(...)` |
| `for x in queryset:` | `async for x in queryset:` |
| `list(queryset)` / 切片后遍历 | `results = [x async for x in queryset[start:end]]` |
| `get_object_or_404(...)` | `await aget_object_or_404(...)` |
| `len(queryset)`(避免) | `acount()` |
注意:
- **切片**:`queryset[0:10]` 本身是惰性的,切片可以保留;但取值/遍历必须 async。
- **values()/values_list()**:`[x async for x in Model.objects.filter(...).values(...)]` 合法。
- **select_related/prefetch_related 链**:保留,遍历时 async。
- **聚合**:`await Model.objects.aggregate(...)`(Django 5.x 支持 aggregate 的异步版本是 `Model.objects.acount` 等;`aggregate()` 无原生异步 —— 用 `await sync_to_async(Model.objects.aggregate)(...)`)。
- **事务** `transaction.atomic()`:在 async 上下文中改为 `await sync_to_async(fn)()` 整体包裹,或使用 `transaction.atomic` 的异步支持 `async with transaction.acreate_agent`——最稳妥是 `await sync_to_async(sync_business_fn)()`。
- **Q 对象/复杂查询**:构造部分是同步的,保留。
### 4. 外部 HTTP 调用
- `import requests` + `requests.get/post(...)` → 改用 `aiohttp`(已安装):
```python
import aiohttp
async with aiohttp.ClientSession(timeout=aiohttp.ClientTimeout(total=10)) as session:
async with session.get(url, params=..., headers=...) as resp:
data = await resp.json()
```
- 文件上传(如百度语音/图片识别)用 `aiohttp.FormData()`。
- 若改写风险过大(复杂 multipart),可用 `await sync_to_async(requests.post)(...)` 兜底,但要加注释 `# TODO: aiohttp 化`。
### 5. 必须保持不变
- 所有 `@swagger_auto_schema` 装饰器原样保留(drf-yasg 只附加元数据,与 async 兼容)。
- 所有 permission_classes、authentication_classes、pagination/filter 配置不变。
- URL 路由文件(urls.py)不改 —— as_view() 不变。
- 返回的 JSON 结构、状态码、错误信息完全不变(前端依赖这些契约)。
- Celery `.delay()` 调用保留(可在 async 中直接调用;如需保险用 `await sync_to_async(task.delay)(...)`)。
- `request.data` / `request.query_params` / `request.user` 用法不变(ADRF 的 AsyncRequest 兼容)。
### 6. 缓存操作
- `cache.get/set` 是同步网络 I/O → `await sync_to_async(cache.get)(key)` 或保持 django-redis 同步调用并用 sync_to_async 包裹。
- 简单做法:`from asgiref.sync import sync_to_async`,然后 `await sync_to_async(cache.set)(key, val, ttl)`。
### 7. 文件/验证
- 每改完一个文件执行:`python -m py_compile <file>` 确保语法正确。
- 不要运行服务器/测试(由主会话统一验证)。
- 不确定某个 ORM 调用是否有异步版本时,用 `sync_to_async` 包裹并加 `# TODO: async ORM` 注释 —— 宁可兜底也不要留下裸同步 ORM 调用(会抛 SynchronousOnlyOperation)。
### 8. 输出要求
- 逐文件报告:改了哪些处理器、哪些 ORM 调用、哪些外部 HTTP、是否有 sync_to_async 兜底点。
- 列出任何你不敢改的复杂点(如嵌套事务、信号)。