Apollo-upload-client и графен-джанго

У меня вопрос об использовании apollo-upload-client и graphene-django. Здесь я обнаружил apollo-upload-client добавление operations в formData. Но пытается только noreferrer чтобы получить параметр query. И вопрос, где и как это исправить?


person Vladimir Chernenko    schedule 09.02.2018    source источник


Ответы (2)


Если вы имеете в виду данные с заголовком, например (при просмотре HTTP из инструментов Chrome):

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

и данные вроде

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

библиотека graphene-python интерпретирует это и собирает для вас запрос, вставляя переменные и удаляя данные файла из запроса. Если вы используете Django, вы можете найти все загруженные файлы в info.context.FILES при написании мутации.

person Mark Chackerian    schedule 12.02.2018

Вот мое решение для поддержки последней версии apollo-upload-client (8.1). Недавно мне пришлось пересмотреть свой код Django, когда я обновился с apollo-upload-client 5.x до 8.x. Надеюсь это поможет.

Извините, я использую старую версию 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 {}
person Keeth    schedule 01.09.2018