Я использую SQLAlchemy для работы с удаленной базой данных, которая использует странный формат timestamp - она хранит временные метки как миллисекунды с двойной точностью с эпохи. Я хотел бы работать с объектами питона даты и времени, поэтому я написал методы геттер/сеттер в моей модели, следуя this gist:Преобразование даты и времени в unix timestamp в SQLAlchemy модели перед выполнением запроса?
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.orm import synonym
from sqlalchemy.dialects.mysql import DOUBLE
import datetime
Base = declarative_base()
class Table(Base):
__tablename__ = "table"
id = Column(Integer, primary_key=True)
_timestamp = Column("timestamp", DOUBLE(asdecimal=False))
@property
def timestamp(self):
return datetime.datetime.utcfromtimestamp(float(self._timestamp)/1000.)
@timestamp.setter
def timestamp(self, dt):
self._timestamp = float(dt.strftime("%s"))*1000.
timestamp = synonym('_timestamp', descriptor=timestamp)
Это прекрасно работает для вставки новых строк в таблицу и работать с объектами из таблицы:
>>> table = session.query(Table).first()
<Table id=1>
>>> table.timestamp
datetime.datetime(2016, 6, 27, 16, 9, 3, 320000)
>>> table._timestamp
1467043743320.0
Однако, это ломается, когда я пытаюсь использовать DateTime в выражении фильтра:
>>> july = datetime.datetime(2016, 7, 1)
>>> old = session.query(Table).filter(Table.timestamp < july).first()
/lib/python2.7/site-packages/sqlalchemy/engine/default.py:450: Warning: Truncated incorrect DOUBLE value: '2016-07-01 00:00:00'
>>> july_flt = float(july.strftime("%s"))*1000.
>>> old = session.query(Table).filter(Table.timestamp < july_flt).first()
<Table id=1>
Я предполагаю, что это потому, что мои геттер/сеттер методы применяются т o экземпляры класса таблицы, но не изменяйте поведение самого класса. Я попытался переписывание с использованием гибридного свойства вместо синонима:
from sqlalchemy.ext.hybrid import hybrid_property
class Table(Base):
__tablename__ = "table"
id = Column(Integer, primary_key=True)
_timestamp = Column("timestamp", DOUBLE(asdecimal=False))
@hybrid_property
def timestamp(self):
return datetime.datetime.utcfromtimestamp(float(self._timestamp)/1000.)
@timestamp.setter
def timestamp(self, dt):
self._timestamp = float(dt.strftime("%s"))*1000.
Опять же, это работает с экземплярами таблицы, но терпит неудача в запросе - теперь он ударился геттером, когда я запускаю запрос:
>>> july = datetime.datetime(2016, 7, 1)
>>> old = session.query(Table).filter(Table.timestamp < july).first()
Traceback:
File "models.py", line 42, in timestamp
return datetime.datetime.utcfromtimestamp(float(self._timestamp)/1000.)
TypeError: float() argument must be a string or a number
С отладчиком я вижу, что получатель получает класс Table._timestamp (а не конкретный Table._timestamp, а не 'июль).
Я вижу, что я мог бы использовать декоратор hybrid_property.expression, чтобы определить выражение SQL для преобразования временных меток в datetime, но мне бы очень хотелось, чтобы преобразовать дату и время в метку времени на стороне python, а затем запустить запрос с использованием временных меток , Другими словами, я хотел бы использовать datetime везде (в том числе в запросах), но все это делается с микросекундными метками времени на стороне SQL. Как я могу это сделать?
Спасибо! Я попробую это сегодня днем и приму ответ, когда он сработает. Re: Мили против микро, вы правы; это ошибка в документах программиста базы данных, которые я перенес в почту. Re: strftime, к сожалению, я использую Python 2.7 и не получаю метод 'datetime.timestamp()'. Метод «strftime» - это мой предпочтительный kludge, если я только запускаю его на своей локальной машине :) – nrlakin
Решение 2.7 (dt - datetime (1970, 1, 1))/timedelta (seconds = 1) 'достаточно простое тоже :) – RazerM
Да, но это так много. Если я пишу что-то для сервера, где я не знаю, на какой платформе он будет работать, я буду использовать что-то более безопасное, чем 'strftime'. Странно, что все из 2.7 решений для общей проблемы настолько неэлегантные ... – nrlakin