在CouchDB数据模型中存储日期时间

pn9klfpd  于 2022-12-09  发布在  CouchDB
关注(0)|答案(2)|浏览(175)

为了使用pycouchdb在CouchDB中的RPI上保存一些传感器数据,我创建了一个数据库模型类,使其具有清晰的结构,而不是松散类型的字典

class SensorMeasure(NamedTuple):
  temp: float
  soilMoisture: float
  dateTime: datetime

由于似乎不可能自动序列化该对象,因此我使用_asdict() method from NamedTuple来获取一个dict对象,该对象可以存储在数据库中

server = pycouchdb.Server("http://127.0.0.1:5984/")
db = server.database(dbName)
measure = SensorMeasure(temp=sensor.getTemperature(), soilMoisture = sensor.getMoisture(), dateTime = datetime.now())
db.save(measure._asdict())

虽然这对于像float这样的基元类型很有效,但它在datetime上中断:

TypeError: Object of type datetime is not JSON serializable

看来我必须tell the serializer how he could generate a string from the datetime object,这似乎是不可能的,我没有修改pycouchdbs源代码。
唯一可行的解决方法似乎是在SensorMeasure模型中使用string而不是datetime,并使用datetimeisoformat()方法。

  1. install additional libraries for parsing
    1.我必须在每次使用时解析它,开销包括创建新对象、指定格式...
    在设计方面,类中有一个datetime属性会更好。我如何归档这个属性?

其他解决方法

使用zip函数,似乎可以定义哪些键应该被序列化。这使我想到删除dateTime字段,然后将其作为字符串值重新添加,如下所示:

class SensorMeasure(NamedTuple):
  temp:float
  soilMoisture: float
  dateTime: datetime

  def test(self):
    serializeFields = list(self._fields)
    del serializeFields['dateTime']

    serialized = OrderedDict(zip(serializeFields, self))
    print(serialized)

    serialized['dateTime'] = dateTime.isoformat()
    print(serialized)

但这并不起作用,因为返回的元组是不可变的。将其转换为列表应该允许写入,但列表似乎只允许整数键:

TypeError: list indices must be integers or slices, not str
guz6ccqo

guz6ccqo1#

次优解决方案

这只是为了说明文档/完整性。向下滚动以获得更好的解决方案

通过几个转换操作,我的问题中的解决方案想法奏效了:

class SensorMeasure(NamedTuple):
  temp: float
  soilMoisture: float
  dateTime: datetime

  def test(self):
    serializeFields = list(self._fields)
    del serializeFields[serializeFields.index('dateTime')]

    serialized = OrderedDict(zip(serializeFields, self))
    tmp = list(serialized.items())
    tmp.append(('dateTime', self.dateTime.isoformat()))

    finalDict = OrderedDict(tmp)
    return finalDict

但是,这不仅仅是每个模型都需要的日期时间序列化的大量代码/工作,或者至少是每个模型中对某个帮助器类的调用。

更好的解决方案:自定义db.save方法

更好的解决方案是在json.dumps上设置默认解析方法

jsonStr = json.dumps(doc, default = str)

遗憾的是pycouchdb目前不允许这样做。当然我可以发送pull请求,通过一个新的参数来扩展这个功能。但是现在,我只是创建我自己的保存方法作为一个快速和合适的修复。

def save(sensorMeasure):
  doc = copy.deepcopy(sensorMeasure._asdict())
  doc['_id'] = uuid.uuid4().hex

  # Default is important to parse datetime objects
  jsonStr = json.dumps(doc, default = str)
  data = pycouchdb.utils.force_bytes(jsonStr)
  (resp, results) = db.resource(doc['_id']).put(data=data)

唯一仍然存在的问题是使用db.get()时缺少类型:

doc = db.get("bb29e02dc2364a57ac1e707d7dc2134b")

这将返回一个字典,因此仍然需要将字符串解析为一个日期时间对象。可以使用constructor来完成。这并不像我所知道的那样容易,例如在ASP.NET核心中,串行化/反串行化可以用一行代码完成,但似乎是可能的。

svujldwt

svujldwt2#

可以定义您自己的解码器/编码器。我这样做了,工作正常保存:

import json
import couchdb
from datetime import datetime, date

class JSONEncoderExtendDate(json.JSONEncoder):
    def default(self, obj):
        if isinstance(obj, datetime):
            return obj.isoformat()
        elif isinstance(obj, date):
            return obj.isoformat()

        return json.JSONEncoder.default(self, obj)

def encode_json(obj):
    return json.dumps(obj, cls=JSONEncoderExtendDate)
def decode_json(obj):
    return json.loads(obj)

couchdb.json.use(decode=decode_json, encode=encode_json)

然后正常执行保存。
可能还应该实现解码器,但这是很明显的。我只在python端使用保存。你需要定义解码器和编码器,这就是为什么它不是空的。

相关问题