在CouchDB数据模型中存储datetime

2024-09-24 06:29:10 发布

您现在位置:Python中文网/ 问答频道 /正文

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

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

因为似乎不可能自动序列化这个对象,所以我使用^{} method from ^{} 来获取一个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 ^{} object,如果不修改^{} source code,这对我来说似乎是不可能的

唯一的解决办法似乎是在SensorMeasure模型中使用string而不是datetime,并使用datetimeisoformat()方法。但这需要我

  1. install additional libraries for parsing
  2. 我必须在每次使用时解析它,并承担创建新对象、指定格式等开销

就设计而言,最好在类中有一个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

Tags: 对象模型self数据库类型datetimefloattemp
2条回答

可以定义自己的解码器/编码器。我这样做了,很好地保存了:

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端使用保存。您需要同时定义解码器和编码器,这就是为什么它不是空的

次优解决方法

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

通过一些转换操作,我的问题中的解决方案思想起了作用:

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

但这不仅仅是datetime序列化的大量代码/工作,这在每个模型中都是必需的。或者至少在每个模型中调用某个助手类

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

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

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

不幸的是pycouchdb目前doesn't allow this。当然,我可以发送pull请求,例如通过一个新参数来扩展此功能。但现在,我只是创建了自己的save方法,作为一个快速且合适的修复方法

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")

这将返回一个字典,因此仍然需要将字符串解析为datetime对象。这可以使用constructor来完成。不像我所知道的那么简单,例如从ASP.NET Core,序列化/反序列化可以用一行代码完成,但似乎是可能的

相关问题 更多 >