From 50ae676bb25cf3ce5f4beb786c40b9f1a104c261 Mon Sep 17 00:00:00 2001 From: async-upgrade Date: Sun, 6 Sep 2026 16:27:17 +0800 Subject: [PATCH] fix: never_cache sync wrapper broke async dispatch - add async_never_cache_dispatch (wallet/login_record/tasks) --- user/views/login_record.py | 7 ++++--- user/views/tasks.py | 9 +++++---- user/views/wallet.py | 7 ++++--- utils/async_decorators.py | 32 ++++++++++++++++++++++++++++++++ 4 files changed, 45 insertions(+), 10 deletions(-) create mode 100644 utils/async_decorators.py diff --git a/user/views/login_record.py b/user/views/login_record.py index 74c1919..d160f61 100644 --- a/user/views/login_record.py +++ b/user/views/login_record.py @@ -6,6 +6,7 @@ from django.utils.decorators import method_decorator from django.views.decorators.cache import never_cache from datetime import timedelta +from utils.async_decorators import async_never_cache_dispatch from utils.response_codes import ( ResponseCode, create_standardized_response, @@ -18,7 +19,7 @@ from drf_yasg import openapi from chunyu_project.common_schemas import success_response, error_response, unauthorized_response, not_found_response -@method_decorator(never_cache, name='dispatch') +@async_never_cache_dispatch class LoginRecordListAPIView(APIView): permission_classes = [IsAuthenticated] @@ -87,7 +88,7 @@ class LoginRecordListAPIView(APIView): ) -@method_decorator(never_cache, name='dispatch') +@async_never_cache_dispatch class LoginRecordDeleteAPIView(APIView): permission_classes = [IsAuthenticated] @@ -117,7 +118,7 @@ class LoginRecordDeleteAPIView(APIView): ) -@method_decorator(never_cache, name='dispatch') +@async_never_cache_dispatch class LoginRecordClearAPIView(APIView): permission_classes = [IsAuthenticated] diff --git a/user/views/tasks.py b/user/views/tasks.py index 62d84f6..a714fb0 100644 --- a/user/views/tasks.py +++ b/user/views/tasks.py @@ -10,6 +10,7 @@ from django.views.decorators.cache import never_cache from datetime import timedelta import logging +from utils.async_decorators import async_never_cache_dispatch from utils.response_codes import ( ResponseCode, create_standardized_response, @@ -112,7 +113,7 @@ def check_level_up(user_level): return leveled_up -@method_decorator(never_cache, name='dispatch') +@async_never_cache_dispatch class TaskListAPIView(APIView): permission_classes = [IsAuthenticated] @@ -195,7 +196,7 @@ class TaskListAPIView(APIView): ) -@method_decorator(never_cache, name='dispatch') +@async_never_cache_dispatch class TaskTrackAPIView(APIView): permission_classes = [IsAuthenticated] @@ -249,7 +250,7 @@ class TaskTrackAPIView(APIView): ) -@method_decorator(never_cache, name='dispatch') +@async_never_cache_dispatch class TaskClaimAPIView(APIView): permission_classes = [IsAuthenticated] @@ -377,7 +378,7 @@ class TaskClaimAPIView(APIView): ) -@method_decorator(never_cache, name='dispatch') +@async_never_cache_dispatch class UserLevelAPIView(APIView): permission_classes = [IsAuthenticated] diff --git a/user/views/wallet.py b/user/views/wallet.py index 06404b7..4dcb88d 100644 --- a/user/views/wallet.py +++ b/user/views/wallet.py @@ -11,6 +11,7 @@ import logging from asgiref.sync import sync_to_async +from utils.async_decorators import async_never_cache_dispatch from utils.response_codes import ( ResponseCode, create_standardized_response, @@ -25,7 +26,7 @@ from chunyu_project.common_schemas import success_response, error_response, unau logger = logging.getLogger(__name__) -@method_decorator(never_cache, name='dispatch') +@async_never_cache_dispatch class WalletBalanceAPIView(APIView): permission_classes = [IsAuthenticated] @@ -222,7 +223,7 @@ async def track_checkin_task(user): logger.error(f'签到任务进度更新失败: {e}') -@method_decorator(never_cache, name='dispatch') +@async_never_cache_dispatch class CheckinStatusAPIView(APIView): permission_classes = [IsAuthenticated] @@ -277,7 +278,7 @@ class CheckinStatusAPIView(APIView): ) -@method_decorator(never_cache, name='dispatch') +@async_never_cache_dispatch class CheckinAPIView(APIView): permission_classes = [IsAuthenticated] diff --git a/utils/async_decorators.py b/utils/async_decorators.py new file mode 100644 index 0000000..44a2e0c --- /dev/null +++ b/utils/async_decorators.py @@ -0,0 +1,32 @@ +""" +异步友好的 never_cache 类装饰器 +================================ +替代 `@method_decorator(never_cache, name='dispatch')`。 + +背景:@method_decorator 生成同步包装器,在 adrf 的 async dispatch 返回的协程上 +调用 add_never_cache_headers 会抛 AttributeError('coroutine' object has no +attribute 'has_header')。本装饰器兼容同步/异步 dispatch,语义与 never_cache 一致。 + +用法(作为类装饰器): + @async_never_cache_dispatch + class WalletBalanceAPIView(APIView): + ... +""" +import asyncio + +from django.utils.cache import add_never_cache_headers + + +def async_never_cache_dispatch(cls): + orig_dispatch = cls.dispatch + + async def dispatch(self, request, *args, **kwargs): + response = orig_dispatch(self, request, *args, **kwargs) + if asyncio.iscoroutine(response): + response = await response + if response is not None and hasattr(response, "has_header"): + add_never_cache_headers(response) + return response + + cls.dispatch = dispatch + return cls