Десериализовать вложенные поля в зефире

Я использую API, который возвращает что-то вроде:

{'name': 'foo', 'start': {'date': '2016-06-19', 'time': '18:00'}}

И я хочу снять десериализацию с помощью зефира, чтобы получить только имя и дату начала, поэтому желаемый результат будет следующим:

{'name': 'foo', 'date': '2016-06-19'}

Но я не нашел способа получить дату, вот что я попробовал:

from marshmallow import Schema, fields, pprint

event = {'name': 'foo', 'start': {'date': '2016-06-19', 'time': '18:00'}}
class EventSchema(Schema):
    name = fields.Str()
    date = fields.Str(load_from='start.date')


schema = EventSchema()
result = schema.load(event)
pprint(result.data)

4 ответа

Решение

Вам нужно будет создать NestedSchema для вложенного словаря, и перезаписать ваши родительские схемы load метод для добавления вложенного поля к родителю. Укажите only атрибут так Nested поле не выбирает все свои элементы:

class DateTimeSchema(Schema):
    date = fields.Str()
    time = fields.Str()


class EventSchema(Schema):
    name = fields.Str()
    date = fields.Nested(DateTimeSchema, load_from='start', only='date')

    def load(self, *args, special=None):
        _partial = super(EventSchema, self).load(*args)

        # Move special field from Nest to Parent
        if special is not None and special in _partial.data:
            _partial.data[special]  = _partial.data[special].get(special)
        return _partial

И настройте свой экземпляр схемы следующим образом:

event = {'name': 'foo', 'start': {'date': '2016-06-19', 'time': '18:00'}}

schema, special_field = EventSchema(), 'date'
result = schema.load(event, special=special_field)
pprint(result.data)
# {'name': 'foo', 'date': '2016-06-19'}

Вы всегда можете подстроиться под свой вкус.

То, что вы описываете, может быть достигнуто путем преобразования * ваших входных данных на этапе предварительной обработки *. Хотя принятый ответ выглядит так, как будто он это сделает, в Marshmallow есть встроенные декораторы, которые позволяют вам сделать это так, как мне кажется, еще яснее:

from marshmallow import Schema, pre_load, fields, pprint

event = {'name': 'foo', 'start': {'date': '2016-06-19', 'time': '18:00'}}
expected = {'name': 'foo', 'date': '2016-06-19'}


class EventSchema(Schema):
    name = fields.Str()
    date = fields.Str(load_from='start.date')

    @pre_load
    def move_date(self, data):
        """This will alter the data passed to ``load()`` before Marshmallow
        attempts deserialization.
        """
        start = data.pop('start')
        data['date'] = start['date']
        return data

schema = EventSchema()
result = schema.load(event)
pprint(result.data)

assert result.data == expected

* Преобразование и предварительная обработка являются условиями искусства в области моделирования объектов и обработки данных. Я выделил их, потому что зная, что они могут помочь людям, которые успешно читают этот вопрос, Google найдет ответы на похожие вопросы.

Marshmallow 3 имеет Pluck:

class DateTimeSchema(Schema):
    date = fields.Str()
    time = fields.Str()

class EventSchema(Schema):
    name = fields.Str()
    date = fields.Pluck(DateTimeSchema, 'date')

документация для fields.Pluck()

Видя, насколько сложны предыдущие ответы, я хочу предложить очень простое решение (используя fields.Function):

      from marshmallow import Schema, fields

class EventSchema(Schema):
    name = fields.Str()
    date = fields.Function(data_key='start',
                           deserialize=lambda start: start['date'])

schema = EventSchema()
event = {'name': 'foo', 'start': {'date': '2016-06-19', 'time': '18:00'}}
result = schema.load(event)
print(result)

Результат ожидаемый: {'name': 'foo', 'date': '2016-06-19'}.

Это работает на версии 3 marshmallow.

Другие вопросы по тегам