Как я могу вставить записи, которые имеют словари и списки в Flask Eve?

Я использую Flask-Eve для предоставления API для своих данных. Я хотел бы вставить свои записи, используя Eve, чтобы получить атрибут _created и другие атрибуты, добавленные Eve.

Два моих поля — это слова, а одно — список. Когда я пытаюсь вставить это Еве, кажется, что структура становится плоской, теряется часть информации. Попытка рассказать Еве об элементах dict и list выдает мне ошибку при POST, говоря, что эти поля должны быть dicts и lists, но они уже есть! Пожалуйста, может кто-нибудь помочь мне и сказать мне, что я делаю неправильно?

Моя конфа в Еве выглядела так:

'myendpoint': { 'allow_unknown': True,
                   'schema': { 'JobTitle': { 'type':       'string',
                                             'required':       True,
                                             'empty':         False,
                                             'minlength':         3,
                                             'maxlength':        99 },
                                'JobDescription': { 'type': 'string',
                                                    'required':       True,
                                                    'empty':         False,
                                                    'minlength':        32,
                                                    'maxlength':        102400 },
                           },
               },

Но когда я POST следующую структуру, используя запросы:

{
        "_id" : ObjectId("56e840686dbf9a5fe069220e"),
        "Salary" : {
                "OtherPay" : "On Application"
        },
        "ContactPhone" : "xx",
        "JobTypeCodeList" : [
                "Public Sector",
                "Other"
        ],
        "CompanyName" : "Scc",
        "url" : "xx",
        "JobTitle" : "xxx",
        "WebAdID" : "TA7494725_1_1",
        "JobDescription" : "xxxx",
        "JobLocation" : {
                "DisplayCity" : "BRIDGWATER",
                "City" : "BRIDGWATER",
                "StateProvince" : "Somerset",
                "Country" : "UK",
                "PostalCode" : "TA6"
        },
        "CustomField1" : "Permanent",
        "CustomField3" : "FTJOBUKNCSG",
        "WebAdManagerEmail" : "xxxx",
        "JobType" : "Full",
        "ProductID" : "JCPRI0UK"
}

Строка поста выглядит так:

resp = requests.post(url, data = job)

Он становится «сплющенным» и теряет информацию из диктов и списка:

{
        "_id" : ObjectId("56e83f5a6dbf9a6395ea559d"),
        "Salary" : "OtherPay",
        "_updated" : ISODate("2016-03-15T16:59:06Z"),
        "ContactPhone" : "xx",
        "JobTypeCodeList" : "Public Sector",
        "CompanyName" : "Scc",
        "url" : "xxx",
        "JobTitle" : "xx",
        "WebAdID" : "TA7494725_1_1",
        "JobDescription" : "xxx",
        "JobLocation" : "DisplayCity",
        "CustomField1" : "Permanent",
        "_created" : ISODate("2016-03-15T16:59:06Z"),
        "CustomField3" : "FTJOBUKNCSG",
        "_etag" : "55d8d394141652f5dc2892a900aa450403a63d10",
        "JobType" : "Full",
        "ProductID" : "JCPRI0UK"
}

Я попытался обновить свою схему, чтобы сказать, что некоторые из них являются диктовками и списками:

     'JobTypeCodeList': { 'type': 'list'},
     'Salary':          { 'type': 'dict'},
     'JobLocation':     { 'type': 'dict'},

Но затем, когда я POST в новой записи, я получаю сообщение об ошибке

 {u'Salary': u'must be of dict type', u'JobTypeCodeList': u'must be of list type', u'JobLocation': u'must be of dict type'},

Я проверил перед POST, что type(job.Salary) == dict и т. д., поэтому я не уверен, как это решить. Хотя я могу отправить запись непосредственно в MongoDB, минуя Eve, я бы предпочел использовать Eve, если это возможно.


person Jeremy Jones    schedule 15.03.2016    source источник


Ответы (1)


На случай, если это будет полезно кому-то еще, я решил обойти эту проблему, опубликовав плоскую структуру в Eve, а затем используя события on_insert и on_update для циклического перебора ключей и создания объектов (и списков) из них.

Это немного запутанно, но это помогает, и теперь, когда он на месте, его довольно прозрачно использовать. Мои объекты, добавленные в MongoDB через Eve, теперь имеют встроенные списки и хэши, но они также получают удобные атрибуты Eve, такие как _created и _updated, в то время как запросы POST и PATCH также проверяются через обычную схему Eve.

Единственная действительно неудобная вещь заключается в том, что on_insert и on_update отправляют немного разные аргументы, поэтому в приведенном ниже коде много повторений, которые я еще не рефакторил.

В качестве флагов можно использовать любые символы: я использую два символа подчеркивания для обозначения ключей/значений, которые должны в конечном итоге стать одним объектом, и два амперсанда для значений, которые должны быть разбиты на список. Структура, которую я публикую, теперь выглядит так:

    "Salary__OtherPay" : "On Application"
    "ContactPhone" : "xx",
    "JobTypeCodeList" : "Public Sector&&Other",
    "CompanyName" : "Scc",
    "url" : "xx",
    "JobTitle" : "xxx",
    "WebAdID" : "TA7494725_1_1",
    "JobDescription" : "xxxx",
    "JobLocation__DisplayCity" : "BRIDGWATER",
    "JobLocation__City" : "BRIDGWATER",
    "JobLocation__StateProvince" : "Somerset",
    "JobLocation__Country" : "UK",
    "JobLocation__PostalCode" : "TA6"
    "CustomField1" : "Permanent",
    "CustomField3" : "FTJOBUKNCSG",
    "WebAdManagerEmail" : "xxxx",
    "JobType" : "Full",
    "ProductID" : "JCPRI0UK"

И моя схема Eve была соответствующим образом обновлена, чтобы проверить значения этих новых имен ключей. Затем в бэкэнде я определил функцию ниже, которая проверяет входящие ключи/значения и преобразует их в объекты/списки, а также удаляет исходные данные __ и &&:

import re

def flat_to_complex(items=None, orig=None):

    if type(items) is dict: # inserts of new objects
        if True: # just to force indentation
            objects = {} # hash-based container for each object
            lists   = {} # hash-based container for each list

            for key,value in items.items():
                has_object_wildcard = re.search(r'^([^_]+)__', key, re.IGNORECASE)
                if bool(has_object_wildcard):
                    objects[has_object_wildcard.group(1)] = None
                elif bool(re.search(r'&&', unicode(value))):
                    lists[key] = str(value).split('&&')

            for list_name, this_list in lists.items():
                items[list_name] = this_list

            for obj_name in objects:

                this_obj = {}
                for key,value in items.items():
                    if key.startswith('{s}__'.format(s=obj_name)):
                        match = re.search(r'__(.+)$', key)
                        this_obj[match.group(1)] = value
                        del(items[key])

                objects[obj_name] = this_obj

            for obj_name, this_obj in objects.items():
                items[obj_name] = this_obj

    elif type(items) is list: # updates to existing objects
        for idx in range(len(items)):
            if type(items[idx]) is dict:
                objects = {} # hash-based container for each object
                lists   = {} # hash-based container for each list

                for key,value in items[idx].items():
                    has_object_wildcard = re.search(r'^([^_]+)__', key, re.IGNORECASE)
                    if bool(has_object_wildcard):
                        objects[has_object_wildcard.group(1)] = None
                    elif bool(re.search(r'&&', unicode(value))):
                        lists[key] = str(value).split('&&')

                for list_name, this_list in lists.items():
                    items[idx][list_name] = this_list

                for obj_name in objects:

                    this_obj = {}
                    for key,value in items[idx].items():
                        if key.startswith('{s}__'.format(s=obj_name)):
                            match = re.search(r'__(.+)$', key)
                            this_obj[match.group(1)] = value
                            del(items[idx][key])

                    objects[obj_name] = this_obj

                for obj_name, this_obj in objects.items():
                    items[idx][obj_name] = this_obj

А затем я просто говорю Еве запускать эту функцию для вставок и обновлений этой коллекции:

app.on_insert_myendpoint += flat_to_complex
app.on_update_myendpoint += flat_to_complex

Это достигает того, что мне нужно, и результирующая запись в Mongo такая же, как и в предыдущем вопросе (с атрибутами _created и _updated). Это, очевидно, не идеально, но оно работает, и с ним довольно легко работать, когда оно на месте.

person Jeremy Jones    schedule 12.05.2016