Вложенный JSON массив не обновляется в БД - PullRequest
0 голосов
/ 27 марта 2020

Использование: Python 3.7.3, SQLAlchemy 1.3.13 с sqlite.

Я пытаюсь добавить данные из следующей структуры JSON в поле JSON в моей базе данных.

Это то, чего я добиваюсь, но я изо всех сил стараюсь добавить: «Второе сообщение», «Третье сообщение» и т. Д. c ...

data = {
    "title":'some title',
    "status": "some status",
    "logs": [
        [mytimestamp, "First message"],
        [mytimestamp, "Second message"],
        [mytimestamp, "Third message"]
    ]
}

Вставка вышеуказанной структуры данных в базу данных работает нормально. Как это:

tablename = Tablename(someint=1, data=data)
try:
    db.session.add(tablename)
    db.session.commit()  <--- success every time
except:
    print('Error creating record')

Обновление и изменение элементов верхнего уровня (таких как «заголовок», «статус») я могу сделать без проблем. Я даже могу добавить дополнительные.

tablename = Tablename.query.get(id)
try:
    tablename.data = newdata <--- newdata is where the problem is
    tablename.someint = 2 <--- this always updates without problem
    db.session.commit()
except:
    print('Error updating record')

Но когда дело доходит до добавления дополнительных элементов массива в «журналы», это то, где это становится совершенно странным ... запись базы данных будет обновляться, но за исключением моего JSON поле, но оно также не выдает исключение, оно просто обновляет другие столбцы базы данных, но игнорирует мое поле JSON.

Я пробовал различные способы добавления (), обновления (), даже это работает tablename.data = {**olddata, **newdata} но ТОЛЬКО для предметов верхнего уровня. Как только я пытаюсь манипулировать данными ВНУТРИ "журналов", тогда это:

  • Либо: ошибки, потому что я пытаюсь манипулировать данными неправильно (достаточно справедливо)

  • Или: молча игнорирует тот факт, что я обновляю столбец данных ДАЖЕ, если при печати (newdata) и выходном значении, которое я хочу поместить в БД, я МОГУ ВИДЕТЬ, что это правильно .. Но БД просто игнорирует это!

Чего я не понимаю, так это того, что data с радостью будет вставлен в БД, и я смогу найти способы обновить объект (даже если это некрасиво, на данном этапе я просто хочу, чтобы он работал!), который я получаю из базы данных с помощью запроса, но я не понимаю, почему я могу напечатать полученный объект в CLI, и он выглядит хорошо, но БД просто молча игнорирует это (someint всегда обновляется во время тихих сбоев)! По крайней мере, выдать ошибку, если она не действительна?

У кого-нибудь есть идеи? Как бы вы добавили строки в «журналы»?

--- ВАЖНОЕ ОБНОВЛЕНИЕ ---

Поскольку результаты, которые я получил вчера, не имели для меня никакого логического смысла, я отбросил тему и вернулись сегодня с свободными sh глазами. Это то, что я сделал и нашел до сих пор, мои выводы не радуют , но по крайней мере у меня есть решение, которое теперь, кажется, работает: используйте поле VARCHAR вместо поля JSON в база данных.

Вот как я пришел к выводу:

Я добавил дополнительный столбец в таблицу базы данных, поэтому теперь у меня есть:

data1 -> type: JSON
data2 -> type: VARCHAR

Это мой тест код:

# Get the data
tablename = Tablename.query.get(id)
d1 = tablename.data1 # from JSON field
d2 = json.loads(tablename.data2) # from VARCHAR field

# Append a log message
logs = d1['logs'] # <--- See note 1 below
# logs = d2['logs'] # <--- See note 2 below
logs.append([mytimestamp, message])
newlogs = {'logs': logs}

# Update database record with new data
tablename.data1 = {**d1, **newlogs} # Into JSON field
tablename.data2 = json.dumps({**d2, **newlogs}) # Into VARCHAR field
db.session.commit()

Примечание 1. Если я использую d1 в качестве источника (то есть: из столбца JSON), тогда поле data1 не обновляется с новым сообщением, но data2 поле делает!

Примечание 2: Если я использую d2 в качестве источника (то есть: из столбца VARCHAR), тогда оба поля data1 и data2 будут успешно обновлены новым сообщением.

1 Ответ

1 голос
/ 30 марта 2020

Обход / решение найдено!

Проблема в том, что, согласно документации, ожидаемое поведение. В документации SQLAlchemy указано, и я цитирую:

Обнаружение изменений в столбцах JSON при использовании ORM : тип JSON при использовании с ORM SQLAlchemy, не обнаруживает мутации на месте структуры. Чтобы обнаружить их, необходимо использовать расширение sqlalchemy.ext.mutable. Это расширение позволит «на месте» вносить изменения в структуру данных для создания событий, которые будут обнаружены единицей работы. См. Пример в HSTORE для простого примера, включающего словарь.

Недостатком является то, что реализация вышеупомянутого является относительно сложной и, что более важно, "дорогой". Однако гораздо более легкое и простое решение, которое я нашел, состоит в том, чтобы всегда обновлять элемент верхнего уровня всякий раз, когда вы хотите внести изменение. Например, обновление значения last_updated в приведенном ниже примере каждый раз, когда вы хотите добавить новый элемент в массив logs, приведет к обновлению полной записи. В противном случае ваши обновленные данные JSON будут потеряны, и даже try: except: не сообщит вам об этом.

data = {
    "title":'some title',
    "status": "some status",
    "last_updated": int(floor(time.time() * 1000)),
    "logs": [
        [mytimestamp, "First message"],
        [mytimestamp, "Second message"],
        [mytimestamp, "Third message"]
    ]
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...