嵌套JSON数组未在数据库中更新

时间:2020-03-27 20:09:53

标签: python json python-3.x sqlite sqlalchemy

使用:带sqlite的Python 3.7.3,SQLAlchemy 1.3.13。

我正在努力将以下JSON结构中的数据附加到数据库中的JSON字段中。

这是我想要实现的目标,但是我正在努力做到的是添加:“第二条消息”,“第三条消息”等...

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字段。

我已经尝试了多种方法append(),update(),即使此方法tablename.data = {**olddata, **newdata}也有效,但仅适用于顶级项目。只要我尝试在“日志”中操作数据,就可以:

  • 要么:错误,因为我试图错误地操作数据(足够公平)

  • 或者:默默地忽略我正在更新数据列的事实,即使在我打印(newdata)和要放入db的输出值时也可以看到它是正确的...但是数据库只会忽略它!

我没有得到的是data首先会很高兴地插入数据库,然后我可以找到更新对象的方法(即使它很丑,在这个阶段我只想它可以正常工作!)我通过查询从数据库中退出,但我不明白的是为什么我可以将结果对象打印到CLI上,看起来还可以,但是数据库只是默默地忽略它(someint在静默失败期间得到更新)!如果无效,至少会抛出一个错误?

有人有什么想法吗?您如何在“日志”中添加行?

-重要更新---

由于我昨天获得的结果对我完全没有逻辑意义,所以我放弃了这个话题,今天又有了新鲜的目光。到目前为止,这是我已经完成并发现的结果,令人不满意,但是至少我现在有一个可行的解决方案:在数据库中使用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字段会更新! / p>

注2:如果我使用d2作为来源(即:从VARCHAR列),则data1data2字段都将成功更新为新消息。

1 个答案:

答案 0 :(得分:1)

已找到解决方法/解决方案!

根据文档,问题实际上是预期的行为。 SQLAlchemy documentation的状态,我引用:

使用ORM时检测JSON列中的更改 : JSON类型与SQLAlchemy ORM一起使用时,不会检测到该结构的就地突变。为了检测到这些,必须使用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"]
    ]
}