Source code for duck.backend.django.views
"""
Module containing the default view to handle all Django requests through
redirecting them to the appropriate Duck view to produce a response.
"""
from typing import Callable
from functools import wraps
from django.http.request import HttpRequest as DjangoHttpRequest
from django.http.response import HttpResponse as DjangoHttpResponse
from duck.http.response import BaseResponse
from duck.backend.django.utils import (
django_to_duck_request,
duck_to_django_response,
)
from duck.contrib.sync import convert_to_sync_if_needed
from duck.contrib.websockets import WebSocketView, View
from duck.exceptions.all import RouteNotFoundError, ExpectingNoResponse
from duck.routes import RouteRegistry
from duck.meta import Meta
from duck.logging import logger
from duck.shortcuts import (not_found404, to_response,)
[docs]
@request_meta_update
def duck_django_view(
request: DjangoHttpRequest,
) -> DjangoHttpResponse:
"""
Django default view to handle requests meant for urlpatterns registered within Duck.
Args:
request (DjangoHttpRequest): The Django HTTP request.
Returns:
DjangoHttpResponse: The corresponding Django HTTP response.
"""
return _duck_django_view(request)
[docs]
def _duck_django_view(
request: DjangoHttpRequest,
) -> DjangoHttpResponse:
"""
Django default view to handle requests meant for urlpatterns registered within Duck.
Args:
request (DjangoHttpRequest): The Django HTTP request.
Returns:
DjangoHttpResponse: The corresponding Django HTTP response.
"""
# Convert Django request to Duck request
duck_request = django_to_duck_request(request)
request.duck_request = duck_request
# Retrieve route details from Duck app's route registry
try:
route_info = RouteRegistry.fetch_route_info_by_url(duck_request.path)
url = route_info["url"]
view_kwargs = route_info["handler_kwargs"]
# Call the Duck view with the necessary parameters
view_callable = route_info["handler"]
if type(view_callable) == type and issubclass(view_callable, WebSocketView):
raise TypeError(f"A websocket View cannot be accessed at Django side. Add `{duck_request.path}` to DUCK_EXPLICIT_URLS in settings.py.")
if (type(view_callable) == type and issubclass(view_callable, View)):
view = view_callable(duck_request, **view_kwargs)
if view.strictly_async():
# View needs to be run asynchonously.
raise TypeError("View is set to be strictly asynchronous and cannot be accessed at Django side.")
else:
# Convert the run method to sync if not.
run = convert_to_sync_if_needed(view.run)
duck_response = run()
else:
view_callable = convert_to_sync_if_needed(view_callable)
duck_response = view_callable(request=duck_request, **view_kwargs)
except RouteNotFoundError:
duck_response = not_found404(request=duck_request)
except Exception as e:
if isinstance(e, ExpectingNoResponse):
error_msg = f'ExpectingNoResponse exception have no effect on Django side": {str(e)}'
else:
error_msg = f'Error invoking response view for URL "{duck_request.path}": {str(e)}'
logger.log_raw(
error_msg,
level=logger.ERROR,
custom_color=logger.Fore.YELLOW,
)
raise e
try:
duck_response = to_response(duck_response)
except TypeError as e:
raise TypeError(f"View for URL '{url}' returned object of unallowed type.") from e
# Convert Duck Response to DjangoHttpResponse
django_response = duck_to_django_response(duck_response)
return django_response