vitalik / django-ninja

💨 Fast, Async-ready, Openapi, type hints based framework for building APIs
https://django-ninja.dev
MIT License
7.02k stars 421 forks source link

[BUG] JSON payload not parsed when using upload with extra fields #1306

Open matt0x6F opened 2 hours ago

matt0x6F commented 2 hours ago

Describe the bug Ninja fails to parse the request body correctly and throws a validation error, contrary to the instructions under Upload files with extra fields.

Versions (please complete the following information):

Relevant schema

class FileMetadata(Schema):
    posts: List[int] = []
    visibility: str = "public"

API Code

@files_router.post(
    "/", response={200: FileDetails}, tags=["files"], auth=JWTAuth(permissions=StaffOnly)
)
def create_file(request: HttpRequest, metadata: FileMetadata, upload: NinjaFile[UploadedFile]):
    """
    Creates a file with or without post associations.
    """
    try:
        if metadata.visibility == "public":
            stored_name = PublicStorage().save(upload.name, upload.file)

            url = PublicStorage().url(stored_name)
        else:
            stored_name = PrivateStorage().save(upload.name, upload.file)

            url = PrivateStorage().url(stored_name)

        upload = File.objects.create(
            location=url,
            name=stored_name,
            content_type=upload.content_type,
            charset=upload.charset,
            size=upload.size,
            visibility=metadata.visibility,
        )

        if metadata:
            upload.posts.set(metadata.posts)

        return upload
    except Exception as err:
        logger.error("Error creating file", error=err)

        raise HttpError(500, "Fail to create file") from err

Request body

-----------------------------291645760626718691221248293984
Content-Disposition: form-data; name="upload"; filename="business card front.pdf"
Content-Type: application/pdf

file stuff
%%EOF

-----------------------------291645760626718691221248293984
Content-Disposition: form-data; name="metadata"; filename="blob"
Content-Type: application/json

{"posts":[2,4],"visibility":"public"}
-----------------------------291645760626718691221248293984--

Content-Type on the request is set to multipart/form-data; boundary=---------------------------291645760626718691221248293984

Response

{
    "detail": [
        {
            "type": "missing",
            "loc": [
                "body",
                "metadata"
            ],
            "msg": "Field required"
        }
    ]
}
matt0x6F commented 2 hours ago

To disambiguate some code here:

matt0x6F commented 1 hour ago

For some extra context I use openapi-generator-cli to generate my TypeScript SDK using typescript-fetch. I can supply the code around that if it helps at all.