Хранить дату и время в модели данных CouchDB - PullRequest
0 голосов
/ 12 июля 2020

Чтобы сохранить некоторые данные датчиков в RPI в CouchDB с использованием pycouchdb, я создал класс модели базы данных, чтобы иметь четкую структуру вместо слабо типизированных dicts

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

Поскольку кажется невозможным сериализовать этот объект автоматически, я использовал метод _asdict() из 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

Кажется, что я должен сообщить сериализатору, как он может сгенерировать строку из объекта datetime , что кажется для меня это невозможно без изменения pycouchdbs исходного кода .

Единственный рабочий обходной путь, кажется, использует string вместо datetime в модели SensorMeasure и использует isoformat() метод datetime. Но для этого мне потребуется

  1. установить дополнительные библиотеки для синтаксического анализа
  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

1 Ответ

0 голосов
/ 12 июля 2020

Неоптимальный обходной путь

Это просто для целей / полноты документации. Прокрутите вниз, чтобы найти лучшее решение

С несколькими операциями преобразования идея обходного решения из моего вопроса работает:

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, которая требуется в каждой модели. Или, по крайней мере, вызов в каждой модели некоторого вспомогательного класса.

Лучшее решение: Custom db.save method

Лучшим решением было бы установить метод синтаксического анализа по умолчанию на json.dumps

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

К сожалению, pycouchdb в настоящее время не позволяет . Конечно, я могу отправить запрос на перенос, который расширяет эту функциональность, например, с помощью нового параметра. Но пока я просто создаю свой собственный метод сохранения в качестве быстрого и удобного исправления.

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 по-прежнему требуется. Это можно сделать с помощью конструктора . Не так просто, как я знаю, например, из ASP. NET Core, где сериализацию / десериализацию можно выполнить с помощью одной строчки кода, но кажется возможным.

...