graphene-file-upload icon indicating copy to clipboard operation
graphene-file-upload copied to clipboard

Is it possible to use graphene.List(Upload) to upload multiple files?

Open emilt27 opened this issue 7 years ago • 5 comments
trafficstars

The question in title ^^. screen shot 2018-09-07 at 12 29 28 pm

emilt27 avatar Sep 07 '18 09:09 emilt27

I've figured out why it does not work. I have the following:

class AddItem(graphene.relay.ClientIDMutation):
    class Input:
        files = graphene.List(Upload)
        expires = graphene.Date(required=True)

When I try to execute this mutation with wrong expires value, it tries to return the error with serialized payloads. Here is a line where it tries to serialize: https://github.com/graphql-python/graphql-core/blob/master/graphql/execution/values.py#L73

emilt27 avatar Sep 07 '18 13:09 emilt27

Oops man, when solving the problem of the field "expires" the upload with multiple files is working well? I am evaluating this library. I wanted to know if it is stable considering that my application will use a lot of image uploads.

marlonpatrick avatar Jun 12 '20 14:06 marlonpatrick

Do we have a solution for uploading multiple files together ?

sreerajkksd avatar Sep 16 '20 11:09 sreerajkksd

I spend sometime on this and this is definitely working fine. I read https://medium.com/@dilipkumar/graphql-and-file-upload-using-react-and-node-js-c1d629e1b86b to understand the payload structure and just did what they asked me to do.

In summary, we have to put the following keys and values in form-data

operations:{"query": "mutation uploadFiles($files: [Upload]) {uploadFiles(files: $files) { success }", "variables": { "files": [null,null] }}
map:{ "0": ["variables.files.0"], "1": ["variables.files.1"]}

Additionally 0 key should contain the content of the first file and 1 should contain the content of the second file.

sreerajkksd avatar Sep 17 '20 06:09 sreerajkksd

Here's how to create a test for Django, combining all the bits of information from above + some figuring out:

# test_upload.py

import random

import pytest
from django.core.files.uploadedfile import SimpleUploadedFile

def get_bytes(size: int) -> bytes:
    return bytes(bytearray(random.getrandbits(8) for _ in range(size)))

UPLOAD_DOCUMENTS = """mutation uploadDocuments($documents: [Upload]!) {
 uploadDocuments(documents: $documents) {
  	success
	} 
}"""

def test_upload():
    # Note the keys: variablename dot index_number
    documents = {
        "documents.0": SimpleUploadedFile(
            name="scanned contract.png",
            content=get_bytes(1024),
            content_type="image/png",
        ),
        "documents.1": SimpleUploadedFile(
            name="Invoice.pdf",
            content=get_bytes(2048),
            content_type="application/pdf",
        ),
    }

    response = file_graphql_query(
        UPLOAD_DOCUMENTS, files=documents, variables={"documents": [None, None]}
    )
    assert response.json()["data"]["uploadDocuments"]["success"] is True

# schema.py

class uploadDocuments(graphene.Mutation):
    success = graphene.Boolean()
    class Arguments:
        documents = graphene.NonNull(graphene.List(Upload))

    @classmethod
    def mutate(
        cls,
        root,
        info: td.ResolveInfo,
        documents: t.List[SimpleUploadedFile],
    ):
        if isinstance(documents[0], SimpleUploadedFile) and isinstance(
            documents[1], SimpleUploadedFile
        ):
            return cls(success=True)

        return cls(success=False)

melvyn-apryl avatar Jan 17 '22 21:01 melvyn-apryl