如何修复Django同步视图转异步时的两类报错问题
Django异步APIView错误修复方案
错误1:SynchronousOnlyOperation: You cannot call this from an async context - use a thread or sync_to_async.
问题根源
在异步函数get_available_riders中直接调用了同步的Django ORM操作:
Rider.objects.filter()queryset.exists()queryset.first()
这类同步IO操作不能在异步上下文直接执行,必须用asgiref.sync.sync_to_async包裹后调用。而你错误地给异步Firestore的stream()方法套了sync_to_async——实际上riderexpert_db是firestore.AsyncClient,它的stream()本身就是异步方法,不需要额外包裹。
错误2:TypeError: object Response can't be used in 'await' expression
问题根源
在get_available_riders的异常捕获中,直接返回了handle_google_maps_api_error(e)的结果(一个Response对象)。但get_available_riders是异步函数,上层get方法用await调用它时,会尝试等待这个返回值,而Response不是可等待对象,因此触发错误。
正确做法是在get_available_riders中重新抛出异常,让上层get方法统一捕获并处理,而非直接返回Response。
完整修复代码
from asgiref.sync import sync_to_async from rest_framework.views import APIView from rest_framework.response import Response from rest_framework.permissions import IsAuthenticated import firestore from googlemaps import Client as GoogleMapsClient, ApiError from django.conf import settings import logging logger = logging.getLogger(__name__) riderexpert_db = firestore.AsyncClient(project="riderexpert", database="riderexpert-db") class GetAvailableRidersView(APIView): permission_classes = [IsAuthenticated] SEARCH_RADIUS_METERS = 10 async def get_google_maps_client(self): """Initialize and return the asynchronous Google Maps API client.""" return GoogleMapsClient(key=settings.GOOGLE_MAPS_API_KEY) async def validate_parameters(self, order_location, item_capacity, is_fragile): """Validate input parameters.""" try: item_capacity = float(item_capacity) is_fragile = str_to_bool(is_fragile) except (ValueError, TypeError): return False, "Invalid or missing parameters" if not all( [ order_location is not None and isinstance(order_location, str), item_capacity is not None and isinstance(item_capacity, (int, float)), is_fragile is not None and isinstance(is_fragile, bool), ] ): return False, "Invalid or missing parameters" return True, "" def handle_google_maps_api_error(self, e): """Handle Google Maps API errors.""" return Response( {"status": "error", "message": f"Google Maps API error: {str(e)}"}, status=400, ) def handle_internal_error(self, e): """Handle internal server errors.""" logger.error(str(e)) return Response( {"status": "error", "message": "Error processing the request"}, status=500 ) async def get(self, request, *args, **kwargs): order_location = request.GET.get("origin") item_capacity = request.GET.get("item_capacity") is_fragile = request.GET.get("is_fragile") # Handle Missing or Invalid Parameters is_valid, validation_message = await self.validate_parameters( order_location, item_capacity, is_fragile ) if not is_valid: return Response( {"status": "error", "message": validation_message}, status=400 ) try: # Initialize asynchronous Google Maps API client gmaps = await self.get_google_maps_client() # Optimize Database Queries available_riders = await self.get_available_riders( gmaps, order_location, item_capacity, is_fragile ) return Response({"status": "success", "riders": available_riders}) except ApiError as e: logger.error(f"Google Maps API error: {str(e)}") return self.handle_google_maps_api_error(e) except Exception as e: return self.handle_internal_error(e) finally: # Close the asynchronous Firestore client await riderexpert_db.close() async def get_available_riders(self, gmaps, order_location, item_capacity, is_fragile): try: # 异步Firestore操作,无需sync_to_async riders_collection = await riderexpert_db.collection("riders").stream() riders_within_radius = [] for firestore_rider in riders_collection: firestore_data = firestore_rider.to_dict() email = firestore_data.get("email") # 用sync_to_async包裹同步ORM操作,合并exists()与first()判断 django_rider = await sync_to_async( lambda: Rider.objects.filter( user__email=email, is_available=True, fragile_item_allowed=is_fragile, min_capacity__lte=item_capacity, max_capacity__gte=item_capacity, ).first() )() if django_rider: rider_location = ( firestore_data.get("current_latitude"), firestore_data.get("current_longitude"), ) distance_matrix_result = await gmaps.distance_matrix( origins=order_location, destinations=rider_location, mode="driving", units="metric", ) distance = distance_matrix_result["rows"][0]["elements"][0]["distance"] duration = distance_matrix_result["rows"][0]["elements"][0]["duration"]["text"] if distance["value"] <= self.SEARCH_RADIUS_METERS * 1000: rider_data = { "rider": RiderSerializer(django_rider).data, "distance": distance, "duration": duration, } riders_within_radius.append(rider_data) return riders_within_radius except ApiError as e: # 重新抛出异常,交给上层get方法统一处理 raise e
关键修改点
- 移除Firestore的
sync_to_async包裹:异步客户端的stream()方法直接用await调用即可。 - 用
sync_to_async包裹ORM操作:将Rider.objects.filter().first()用sync_to_async包裹,同时合并exists()判断,减少一次数据库查询。 - 调整异常处理逻辑:在
get_available_riders中不再直接返回Response,而是重新抛出异常,让上层get方法的捕获逻辑统一处理。
内容的提问来源于stack exchange,提问作者Abdulmujeeb Ahmad
相关产品推荐
相关产品推荐

