Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Put Doesn't Seem to Work Properly with Form Data #1059

Open
max-muoto opened this issue Jan 22, 2024 · 2 comments
Open

Put Doesn't Seem to Work Properly with Form Data #1059

max-muoto opened this issue Jan 22, 2024 · 2 comments

Comments

@max-muoto
Copy link
Contributor

Describe the bug

Versions:

  • Python version: 3.11
  • Django version: 4.2
  • Django-Ninja version: 1.0.1
  • Pydantic version: 2.5.2

I have a simple endpoint set up, that uses a a ninja.File, and accepts additional form data with ninja.Form:

class MyEnum(enum.IntEnum)
   ...

class Paylod(ninja.Schema):
    option_1: MyEnum
    option_2: bool


@api.put(
    "/{str:param}/my-endpoint",
)
def my_endpoint(
    request: http.HttpRequest,
    param: str,
    file: api_framework.File[files.UploadedFile],
    options: api_framework.Form[MetricsImportOptions],
) -> None:
...

When trying to make this a PUT, I face the following error,

  "detail": [
    {
      "type": "missing",
      "loc": [
        "file",
        "file"
      ],
      "msg": "Field required"
    },
    {
      "type": "missing",
      "loc": [
        "form",
        "option_1"
      ],
      "msg": "Field required"
    },
    {
      "type": "missing",
      "loc": [
        "form",
        "option_2"
      ],
      "msg": "Field required"
    }
  ]
}

However, with everything the same, a POST works just fine with the data being properly validated as correct. I'm not sure if this is easily fixable, so feel free to close this out if it's not a priority. I saw this similar issue with Laravel, which it seems was never resolved: laravel/framework#13457

However, this might be a violation of this RFC: https://www.w3.org/Protocols/rfc2616/rfc2616-sec9.html#sec9.6. It seems unclear to me whether the HTTP spec formally allows for this.

@baseplate-admin
Copy link
Contributor

This is a known issue.

How to resolve?

  1. Create a middleware that patches the PUT files.
from collections.abc import Callable
from typing import Any

from asgiref.sync import iscoroutinefunction, sync_to_async
from django.http import HttpRequest
from django.utils.decorators import sync_and_async_middleware


@sync_and_async_middleware
def process_put_patch(get_response: Callable) -> Callable:
    async def async_middleware(request: HttpRequest) -> Any:
        if (
            request.method in ("PUT", "PATCH")
            and request.content_type != "application/json"
        ):
            initial_method = request.method
            request.method = "POST"
            request.META["REQUEST_METHOD"] = "POST"
            await sync_to_async(request._load_post_and_files)()
            request.META["REQUEST_METHOD"] = initial_method
            request.method = initial_method

        return await get_response(request)

    def sync_middleware(request: HttpRequest) -> Any:
        if (
            request.method in ("PUT", "PATCH")
            and request.content_type != "application/json"
        ):
            initial_method = request.method
            request.method = "POST"
            request.META["REQUEST_METHOD"] = "POST"
            request._load_post_and_files()
            request.META["REQUEST_METHOD"] = initial_method
            request.method = initial_method

        return get_response(request)

    return async_middleware if iscoroutinefunction(get_response) else sync_middleware
    
  1. Use a package like ninja-put-patch-file-upload-middleware ( maintained by me )

@max-muoto
Copy link
Contributor Author

Thanks, appreciate this! Curious, is there a reason #397 is being blocked @vitalik?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants