This commit is contained in:
2026-05-26 21:25:26 -06:00
parent 8731012f47
commit 57c0572fd9
38 changed files with 1507 additions and 476 deletions

View File

@@ -1,7 +1,10 @@
import asyncio
import functools
import inspect
import json
import logging
from asgiref.sync import sync_to_async
from django.http import HttpResponse, QueryDict
from django.utils.decorators import method_decorator
from django.views.decorators.csrf import csrf_exempt
@@ -162,6 +165,77 @@ class CSRFExemptAPIView(APIView):
return super(CSRFExemptAPIView, self).dispatch(request, *args, **kwargs)
class AsyncAPIView(APIView):
view_is_async = True
def __init_subclass__(cls, **kwargs):
super().__init_subclass__(**kwargs)
cls.view_is_async = True
async def dispatch(self, request, *args, **kwargs):
if self.request_parsers:
try:
request.data = self._get_request_data(self.request)
except ValueError as e:
return self.error(err="invalid-request", msg=str(e))
try:
handler = getattr(self, request.method.lower(), self.http_method_not_allowed)
response = handler(request, *args, **kwargs)
if asyncio.iscoroutine(response):
response = await response
return response
except APIError as e:
ret = {"msg": e.msg}
if e.err:
ret["err"] = e.err
return self.error(**ret)
except Exception as e:
logger.exception(e)
return self.server_error()
def serialize_data(self, object_serializer, data, **kwargs):
return object_serializer(data, **kwargs).data
async def async_serialize_data(self, object_serializer, data, **kwargs):
return await sync_to_async(
self.serialize_data,
thread_sensitive=True,
)(object_serializer, data, **kwargs)
async def async_paginate_data(self, request, query_set, object_serializer=None):
try:
limit = int(request.GET.get("limit", "10"))
except ValueError:
limit = 10
if limit < 0 or limit > 250:
limit = 10
try:
offset = int(request.GET.get("offset", "0"))
except ValueError:
offset = 0
if offset < 0:
offset = 0
count, results = await asyncio.gather(
query_set.acount(),
sync_to_async(lambda: list(query_set[offset:offset + limit]), thread_sensitive=True)(),
)
if object_serializer:
results = await self.async_serialize_data(
object_serializer,
results,
many=True,
context={"request": request},
)
data = {"results": results, "total": count}
return data
class CSRFExemptAsyncAPIView(AsyncAPIView):
@method_decorator(csrf_exempt)
async def dispatch(self, request, *args, **kwargs):
return await super().dispatch(request, *args, **kwargs)
def validate_serializer(serializer):
"""
@validate_serializer(TestSerializer)
@@ -169,6 +243,20 @@ def validate_serializer(serializer):
return self.success(request.data)
"""
def validate(view_method):
if inspect.iscoroutinefunction(view_method):
@functools.wraps(view_method)
async def async_handle(*args, **kwargs):
self = args[0]
request = args[1]
s = serializer(data=request.data)
if s.is_valid():
request.data = s.data
request.serializer = s
return await view_method(*args, **kwargs)
else:
return self.invalid_serializer(s)
return async_handle
@functools.wraps(view_method)
def handle(*args, **kwargs):
self = args[0]
@@ -180,7 +268,6 @@ def validate_serializer(serializer):
return view_method(*args, **kwargs)
else:
return self.invalid_serializer(s)
return handle
return validate

14
utils/async_helpers.py Normal file
View File

@@ -0,0 +1,14 @@
from asgiref.sync import sync_to_async
from django.core.cache import cache
async def async_cache_get(key, default=None):
return await sync_to_async(cache.get, thread_sensitive=True)(key, default)
async def async_cache_set(key, value, timeout=None):
return await sync_to_async(cache.set, thread_sensitive=True)(key, value, timeout)
async def async_cache_delete(key):
return await sync_to_async(cache.delete, thread_sensitive=True)(key)

View File

@@ -4,5 +4,5 @@ from .views import SimditorFileUploadAPIView, SimditorImageUploadAPIView
urlpatterns = [
path("upload_image", SimditorImageUploadAPIView.as_view()),
path("upload_file", SimditorFileUploadAPIView.as_view()),
path("upload_file", SimditorFileUploadAPIView.as_view()), # DEPRECATED: 前端未调用
]

View File

@@ -46,6 +46,7 @@ class SimditorImageUploadAPIView(CSRFExemptAPIView):
"file_path": f"{settings.UPLOAD_PREFIX}/{img_name}"})
# DEPRECATED: 前端未调用 (2026-05-26)
class SimditorFileUploadAPIView(CSRFExemptAPIView):
request_parsers = ()