Как обновить только часть json, которая хранится в базе данных со списком "json path"?

Допустим, у нас есть база данных и есть json, хранящийся как строка, которая содержит конфигурации. В приложении мы хотим обновить только определенное значение и записать обратно json в виде строки в базу данных.

В HTTP-запросе предоставляется параметр "jsonPath", который отражает путь в json-файле для перехода к объекту, который мы хотим обновить. Этот параметр предоставляется в виде списка.

Примером пути может быть что-то вроде ['input', 'keyboard', 'language'] и значение, которое мы хотим установить - 'en'

Здесь мы загружаем конфигурацию из базы данных, которая работает до сих пор:

if request.data['jsonPath']: 
    config =  json.loads(models.Config.objects.get(name__startswith='general').value

Теперь config - это обычный объект, к которому я могу получить доступ с помощью точечной нотации, например config.input.keyboard.language, и он содержит соответствующую часть в синтаксисе json.

Я могу считать значение поля со следующим синтаксисом:

config[request.data['jsonPath'][0]][request.data['jsonPath'][1]][request.data['jsonPath'[3]]

Но количество параметров может меняться, и я хочу также записать его обратно в объект config, преобразовать его обратно в строку и записать в базу данных.

Я знаю, что это не очень приятно, но мне было бы интересно, как это можно сделать таким образом

Предположим, что у вас есть вложенный объект json, подобный этому:

mydict = {
    'key1': {
        'key2': {
            'key3': {
                'foo': 'bar'
            },
        },
    },
}

И у вас есть список "путей", подобный этому:

path = ['key1', 'key2', 'key3']

Вы можете пройти путь следующим образом:

data = mydict
for key in path:
    data = data[key]

data в итоге будет значением последнего элемента в path, в данном случае субдикта {'foo': 'bar'}

Вы можете использовать functools.reduce для этого:

functools.reduce(dict.get, path, data)

from functools import reduce

data = {'key1': {'key2': {'key3': {'foo': 'bar'}}}}
path = ['key1', 'key2', 'key3']

reduce(dict.get, path, data)["foo"] = baz

Результат:

{'key1': {'key2': {'key3': {'foo': 'baz'}}}}

P.S

Если тип данных data является чем-то другим, вы можете использовать operator.itemgetter

Вернуться на верх