title: How do I solve this AttributeError with Request's multi-part form-data POST?
body: <p>I'm trying to post files to my <code>graphql</code> endpoint @ scaphold.io.  I normally write <code>Javascript</code>, but I have to translate this particular operation into <code>Python</code>. </p>
  
  <p>The command to do so with <code>curl</code> looks like this (<a href="https://scaphold.io/docs/?shell#uploading-files" rel="nofollow noreferrer">from their docs</a>): </p>
  
  <pre><code>curl -v https://us-west-2.api.scaphold.io/graphql/scaphold-graphql \
    -H "Content-Type:multipart/form-data" \
    -F 'query=mutation CreateFile($input: CreateFileInput!) { createFile(input: $input) { changedFile { id name blobMimeType blobUrl user { id username } } } }' \
    -F 'variables={ "input": { "name": "Profile Picture", "userId": "VXNlcjoxMA==", "blobFieldName": "myBlobField" } };type=application/json' \
    -F myBlobField=@mark-zuckerberg.jpg
  </code></pre>
  
  <p>Where the <code>blobFieldName</code> prop in <code>variables</code> matches the <code>Form</code> field name that holds the file to upload.</p>
  
  <p>Using <code>Requests</code>, I've gotten this far:</p>
  
  <pre><code>import requests
  from requests_toolbelt import MultipartEncoder
  
  url = 'https://us-west-2.api.scaphold.io/graphql/scaphold-graphql'
  multipart_data = MultipartEncoder(
      fields={
          "query":"mutation CreateFile($input: CreateFileInput!) { createFile(input: $input) { changedFile { id name blobMimeType blobUrl user { id username } } } }",
          "variables": { "input": { "name": "Profile Picture", "userId": "VXNlcjoxMA==", "blobFieldName": "myBlobField" } },
          "type":'application/json',
          "myBlobField": ('example.jpg', open('example.jpg', 'rb'), 'image/jpeg' )
          }      
  )
  req_headers = {'Content-Type':multipart_data.content_type, 'Authorization':'Bearer myreallylongkey'}
  
  r = requests.post(url, data=multipart_data, headers=req_headers)
  </code></pre>
  
  <p>Unfortunately this is met with the <code>AttributeError</code>: </p>
  
  <pre><code>Traceback (most recent call last):
    File "test-gql.py", line 38, in &lt;module&gt;
      "myBlobField": ('example.jpg', open('example.jpg', 'rb'), 'image/jpeg' )
    File "/home/bmp/code/wayhome/python-phash/requests_toolbelt/multipart/encoder.py", line 119, in __init__
      self._prepare_parts()
    File "/home/bmp/code/wayhome/python-phash/requests_toolbelt/multipart/encoder.py", line 240, in _prepare_parts
      self.parts = [Part.from_field(f, enc) for f in self._iter_fields()]
    File "/home/bmp/code/wayhome/python-phash/requests_toolbelt/multipart/encoder.py", line 488, in from_field
      body = coerce_data(field.data, encoding)
    File "/home/bmp/code/wayhome/python-phash/requests_toolbelt/multipart/encoder.py", line 466, in coerce_data
      return CustomBytesIO(data, encoding)
    File "/home/bmp/code/wayhome/python-phash/requests_toolbelt/multipart/encoder.py", line 529, in __init__
      buffer = encode_with(buffer, encoding)
    File "/home/bmp/code/wayhome/python-phash/requests_toolbelt/multipart/encoder.py", line 410, in encode_with
      return string.encode(encoding)
  AttributeError: 'dict' object has no attribute 'encode'
  </code></pre>
  
  <p>I'm afraid I'm not Pythonic enough to grok this error, but I've eliminated a few suspects:</p>
  
  <ul>
  <li>Translating a simple <code>query</code> from <code>curl</code> to <code>Python</code> works fine, so I know it isn't permissions, etc. </li>
  <li>Using a different file, of the <code>plain/text</code> flavor, fails w/ the same error</li>
  <li>Using a tuple-of-tuples instead of a <code>dict</code> for <code>fields</code> (<a href="https://stackoverflow.com/a/35974071">as described here</a>)has no effect</li>
  </ul>