diff --git a/ninja/compatibility/files.py b/ninja/compatibility/files.py index 9e28db8d7..e6bef0f66 100644 --- a/ninja/compatibility/files.py +++ b/ninja/compatibility/files.py @@ -28,13 +28,21 @@ def fix_request_files_middleware(get_response: Any) -> Any: populated for POST requests. https://code.djangoproject.com/ticket/12635 """ + + def should_fix_request_files(request: HttpRequest) -> bool: + return ( + request.method in FIX_METHODS + and request.content_type != "application/json" + and ( + ninja_settings.FIX_REQUEST_FILES_URLS is None + or ninja_settings.FIX_REQUEST_FILES_URLS.search(request.path) + ) + ) + if iscoroutinefunction(get_response): async def async_middleware(request: HttpRequest) -> Any: - if ( - request.method in FIX_METHODS - and request.content_type != "application/json" - ): + if should_fix_request_files(request): initial_method = request.method request.method = "POST" request.META["REQUEST_METHOD"] = "POST" @@ -48,10 +56,7 @@ async def async_middleware(request: HttpRequest) -> Any: else: def sync_middleware(request: HttpRequest) -> Any: - if ( - request.method in FIX_METHODS - and request.content_type != "application/json" - ): + if should_fix_request_files(request): initial_method = request.method request.method = "POST" request.META["REQUEST_METHOD"] = "POST" diff --git a/ninja/conf.py b/ninja/conf.py index 1584a5ce0..51a4ba137 100644 --- a/ninja/conf.py +++ b/ninja/conf.py @@ -1,3 +1,4 @@ +import re from math import inf from typing import Dict, Optional, Set @@ -29,6 +30,9 @@ class Settings(BaseModel): FIX_REQUEST_FILES_METHODS: Set[str] = Field( {"PUT", "PATCH", "DELETE"}, alias="NINJA_FIX_REQUEST_FILES_METHODS" ) + FIX_REQUEST_FILES_URLS: Optional[re.Pattern] = Field( + None, alias="NINJA_FIX_REQUEST_FILES_URLS" + ) settings = Settings.model_validate(django_settings) diff --git a/tests/test_files.py b/tests/test_files.py index 5b948057b..d5450e094 100644 --- a/tests/test_files.py +++ b/tests/test_files.py @@ -1,10 +1,15 @@ +import importlib +import re from typing import List import pytest from django.core.files.uploadedfile import SimpleUploadedFile +from django.test import override_settings +from django.test.client import MULTIPART_CONTENT from django.utils.datastructures import MultiValueDict from ninja import File, NinjaAPI, UploadedFile +from ninja.compatibility.files import fix_request_files_middleware from ninja.errors import ConfigError from ninja.testing import TestClient @@ -144,3 +149,23 @@ def test_files_fix_middleware(): @api.patch("/file1") def patch_with_file(request, file: UploadedFile): return {"name": file.name} + + +@override_settings(NINJA_FIX_REQUEST_FILES_URLS=re.compile(r"^/file\d+")) +def test_files_fix_middleware_urls(rf): + def get_response(request): + assert request.FILES == {} + + from ninja import conf + from ninja.compatibility import files + + importlib.reload(conf) + importlib.reload(files) + + file = SimpleUploadedFile("test.txt", b"data123") + post_data = rf._encode_data({"file": file}, MULTIPART_CONTENT) + request = rf.generic( + "PATCH", "/not-patched", post_data, content_type=MULTIPART_CONTENT + ) + + fix_request_files_middleware(get_response)(request)