Как декодировать форму редактора таблиц данных в фляге?

У меня есть приложение фляги, которое получает запрос от редактора таблиц данных. После получения на сервере, request.form выглядит как (например)

ImmutableMultiDict([('data[59282][gender]', u'M'), ('data[59282][hometown]', u''), 
('data[59282][disposition]', u''), ('data[59282][id]', u'59282'),
('data[59282][resultname]', u'Joe Doe'), ('data[59282][confirm]', 'true'), 
('data[59282][age]', u'27'), ('data[59282][place]', u'3'), ('action', u'remove'), 
('data[59282][runnerid]', u''), ('data[59282][time]', u'29:49'),
('data[59282][club]', u'')])

Я думаю использовать что-то похожее на этот ужасный код для его декодирования. Есть ли способ лучше?

from collections import defaultdict

# request.form comes in multidict [('data[id][field]',value), ...]
# so we need to exec this string to turn into python data structure
data = defaultdict(lambda: {})   # default is empty dict

# need to define text for each field to be received in data[id][field]
age = 'age'
club = 'club'
confirm = 'confirm'
disposition = 'disposition'
gender = 'gender'
hometown = 'hometown'
id = 'id'
place = 'place'
resultname = 'resultname'
runnerid = 'runnerid'
time = 'time'

# fill in data[id][field] = value
for formkey in request.form.keys():
    exec '{} = {}'.format(d,repr(request.form[formkey]))

2 ответа

Решение

Я выбрал способ более безопасный, чем использование exec:

from collections import defaultdict

def get_request_data(form):
    '''
    return dict list with data from request.form

    :param form: MultiDict from `request.form`
    :rtype: {id1: {field1:val1, ...}, ...} [fieldn and valn are strings]
    '''

    # request.form comes in multidict [('data[id][field]',value), ...]

    # fill in id field automatically
    data = defaultdict(lambda: {})

    # fill in data[id][field] = value
    for formkey in form.keys():
        if formkey == 'action': continue
        datapart,idpart,fieldpart = formkey.split('[')
        if datapart != 'data': raise ParameterError, "invalid input in request: {}".format(formkey)

        idvalue = int(idpart[0:-1])
        fieldname = fieldpart[0:-1]

        data[idvalue][fieldname] = form[formkey]

    # return decoded result
    return data

На этот вопрос принят ответ, и он немного староват, но DataTable модуль кажется довольно популярным в сообществе jQuery, я думаю, что этот подход может быть полезен для кого-то еще. Я только что написал простую функцию синтаксического анализа на основе регулярного выражения и dpath Модуль, хотя это не совсем надежный модуль. Фрагмент может быть не очень простым из-за фрагмента, основанного на исключении, но это был только один способ предотвратить dpath от попыток разрешить строки как целочисленные индексы, которые я нашел.

import re, dpath.util

rxsKey = r'(?P<key>[^\W\[\]]+)'
rxsEntry = r'(?P<primaryKey>[^\W]+)(?P<secondaryKeys>(\[' \
         + rxsKey \
         + r'\])*)\W*'

rxKey = re.compile(rxsKey)
rxEntry = re.compile(rxsEntry)

def form2dict( frmDct ):
    res = {}
    for k, v in frmDct.iteritems():
        m = rxEntry.match( k )
        if not m: continue
        mdct = m.groupdict()
        if not 'secondaryKeys' in mdct.keys():
            res[mdct['primaryKey']] = v
        else:
            fullPath = [mdct['primaryKey']]
            for sk in re.finditer( rxKey, mdct['secondaryKeys'] ):
                k = sk.groupdict()['key']
                try:
                    dpath.util.get(res, fullPath)
                except KeyError:
                    dpath.util.new(res, fullPath, [] if k.isdigit() else {})
                fullPath.append(int(k) if k.isdigit() else k)
            dpath.util.new(res, fullPath, v)
    return res

Практическое использование основано на родной колбе request.form.to_dict() метод:

    # ... somewhere in a view code
    pars = form2dict(request.form.to_dict())

Структура вывода включает как словарь, так и списки, как и следовало ожидать. Например:

# A little test:
rs = jQDT_form2dict( {
        'columns[2][search][regex]' : False,
        'columns[2][search][value]' : None,
        'columns[2][search][regex]' : False,
    } )

генерирует:

{
    "columns": [
        null, 
        null, 
        {
            "search": {
                "regex": false, 
                "value": null
            }
        }
    ]
}

Обновление: чтобы обрабатывать списки как словари (более эффективно), можно упростить этот фрагмент с помощью следующего блока в else часть if пункт:

    # ...
    else:
        fullPathStr = mdct['primaryKey']
        for sk in re.finditer( rxKey, mdct['secondaryKeys'] ):
            fullPathStr += '/' + sk.groupdict()['key']
        dpath.util.new(res, fullPathStr, v)
Другие вопросы по тегам