【问题标题】:Store datetime in a CouchDB data model在 CouchDB 数据模型中存储日期时间
【发布时间】:2020-07-12 10:25:36
【问题描述】:

为了使用 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())

虽然这适用于浮点等原始类型,但它会在 datetime 上中断:

TypeError: Object of type datetime is not JSON serializable

看来我必须tell the serializer how he could generate a string from the datetime object,如果不修改pycouchdbs source code,这对我来说似乎是不可能的。

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

  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

【问题讨论】:

    标签: python json python-3.x datetime couchdb


    【解决方案1】:

    次优解决方法

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

    通过一些转换操作,我的问题中的解决方法想法有效:

    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 目前doesn't allow this。当然,我可以发送扩展此功能的拉取请求,例如一个新参数。但就目前而言,我只是创建自己的保存方法作为快速且合适的修复方法。

    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 Core,序列化/反序列化可以用一行代码完成,但似乎可行。

    【讨论】:

      【解决方案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 端使用保存。你需要同时定义解码器和编码器,这就是它不为空的原因。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2021-09-27
        • 2017-05-31
        • 1970-01-01
        • 1970-01-01
        • 2011-04-17
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多