【问题标题】:apollo-upload-client and graphene-djangoapollo-upload-client 和 graphene-django
【发布时间】:2018-07-20 16:49:14
【问题描述】:

我对使用apollo-upload-clientgraphene-django 有疑问。 Here 我发现 apollo-upload-clientoperations 添加到 formData。但是heregraphene-django 只是试图获取query 参数。问题是,应该在哪里以及如何修复它?

【问题讨论】:

    标签: file-upload apollo-client graphene-python


    【解决方案1】:

    如果您指的是具有类似标题的数据(从 Chrome 工具查看 HTTP 时):

    Content-Disposition: form-data; name="operations"

    和类似的数据

    {"operationName":"MyMutation","variables":{"myData"....}, "query":"mutation MyMutation"...},

    graphene-python 库对此进行解释并将其组装成一个查询,插入变量并从查询中删除文件数据。如果您使用的是 Django,则在编写突变时可以在info.context.FILES 中找到所有上传的文件。

    【讨论】:

      【解决方案2】:

      这是我支持最新 apollo-upload-client (8.1) 的解决方案。当我从 apollo-upload-client 5.x 升级到 8.x 时,我最近不得不重新访问我的 Django 代码。希望这会有所帮助。

      抱歉,我使用的是较旧的 graphene-django,但希望您可以将突变语法更新到最新版本。

      上传标量类型(基本上是直通):

      class Upload(Scalar):
          '''A file upload'''
      
          @staticmethod
          def serialize(value):
              raise Exception('File upload cannot be serialized')
      
          @staticmethod
          def parse_literal(node):
              raise Exception('No such thing as a file upload literal')
      
          @staticmethod
          def parse_value(value):
              return value
      

      我的上传突变:

      class UploadImage(relay.ClientIDMutation):
          class Input:
              image = graphene.Field(Upload, required=True)
      
          success = graphene.Field(graphene.Boolean)
      
          @classmethod
          def mutate_and_get_payload(cls, input, context, info):
              with NamedTemporaryFile(delete=False) as tmp:
                  for chunk in input['image'].chunks():
                      tmp.write(chunk)
                  image_file = tmp.name
              # do something with image_file
              return UploadImage(success=True)
      

      繁重的工作发生在自定义 GraphQL 视图中。基本上它将文件对象注入到变量映射中的适当位置。

      def maybe_int(s):
          try:
              return int(s)
          except ValueError:
              return s
      
      class CustomGraphqlView(GraphQLView):
          def parse_request_json(self, json_string):
              try:
                  request_json = json.loads(json_string)
                  if self.batch:
                      assert isinstance(request_json,
                                        list), ('Batch requests should receive a list, but received {}.').format(
                                            repr(request_json))
                      assert len(request_json) > 0, ('Received an empty list in the batch request.')
                  else:
                      assert isinstance(request_json, dict), ('The received data is not a valid JSON query.')
                  return request_json
              except AssertionError as e:
                  raise HttpError(HttpResponseBadRequest(str(e)))
              except BaseException:
                  logger.exception('Invalid JSON')
                  raise HttpError(HttpResponseBadRequest('POST body sent invalid JSON.'))
      
          def parse_body(self, request):
              content_type = self.get_content_type(request)
      
              if content_type == 'application/graphql':
                  return {'query': request.body.decode()}
      
              elif content_type == 'application/json':
                  return self.parse_request_json(request.body.decode('utf-8'))
              elif content_type in ['application/x-www-form-urlencoded', 'multipart/form-data']:
                  operations_json = request.POST.get('operations')
                  map_json = request.POST.get('map')
                  if operations_json and map_json:
                      operations = self.parse_request_json(operations_json)
                      map = self.parse_request_json(map_json)
                      for file_id, f in request.FILES.items():
                          for name in map[file_id]:
                              segments = [maybe_int(s) for s in name.split('.')]
                              cur = operations
                              while len(segments) > 1:
                                  cur = cur[segments.pop(0)]
                              cur[segments.pop(0)] = f
                      logger.info('parse_body %s', operations)
                      return operations
                  else:
                      return request.POST
      
              return {}
      

      【讨论】:

        猜你喜欢
        • 2019-04-20
        • 2018-07-18
        • 2018-09-05
        • 2021-01-15
        • 2021-12-14
        • 2020-01-08
        • 2021-06-27
        • 2018-08-10
        • 2021-08-22
        相关资源
        最近更新 更多