SQLAlchemy datetime расчёты со стороны сервера
Почему вы должны стараться избегать расчетов со стороны клиента, если SQLAlchemy только...
Вы найдете множество примеров SQLAlchemy datetime вычислений, например, с помощью timedelta функции Python's'. Почему? Я не понимаю этого, за исключением того, что это просто. Но правильно ли это?
Предположим, нам нужны все пользовательские записи или объекты, созданные два часа назад, и запись / определение объекта таково:
class User(Base):
__tablename__ = 'user'
id = Column(Integer, primary_key=True)
created_on = Column(DateTime, server_default=func.now(), index=True)
email = Column(String(100), server_default='', index=True)
Затем, если бы мы могли использовать для выбора записей / объектов, добавленных за последние 10 минут, мы могли бы сделать что-то вроде этого:
from datetime import datetime, timedelta
now = datetime.now()
two_hours_ago = now - timedelta(hours=2)
# return all users created less then 2 hours ago
db.query(User).filter(User.created_on > two_hours_ago).all()
Генерируется:
SELECT user.id AS user_id, user.created_on AS user_created_on, user.email AS user_email
FROM user
WHERE user.created_on > %(created_on_1)s
INFO sqlalchemy.engine.base.Engine {'created_on_1': datetime.datetime(2019, 6, 25, 7, 31, 58, 630959)}
Это только работает, дает достоверные результаты, если:
- сервер базы данных работает на том же сервере, на котором запущен Python код.
- сервер базы данных работает на сервере, отличном от сервера Python кода, и время на обоих серверах идеально синхронизировано.
Допустим, у вас есть отдельный сервер базы данных, и время работы этого сервера составляет 2 минуты без синхронизации. Тогда вы получаете неправильные, неполные результаты. Я пишу запросы на стороне сервера в течение многих лет и удивлен, что этому уделяется мало внимания в SQLAlchemy вопросах и ответах.
Единственный способ получить правильный результат - это использование datetime штампов записей сервера БД и добавление или вычитание из них. С MariaDB / MySQL вы можете использовать интервал заявление:
SELECT user.* FROM user WHERE created_on > (NOW() - INTERVAL 2 HOUR)
К сожалению, я не смог найти решение, SQLAlchemy которое было бы действительным для всех баз данных. SQLAlchemy имеет text() объект, он передает значение запросу. С помощью , SQLAlchemy запрос становится:
from sqlalchemy import text
two_hours_ago = text('NOW() - INTERVAL 2 HOURS')
# return all users created less then 2 hours ago
db.query(User).filter(User.created_on > two_hours_ago).all()
Генерируется:
SELECT user.id AS user_id, user.created_on AS user_created_on, user.email AS user_email
FROM user
WHERE user.created_on > NOW() - INTERVAL 2 HOUR
Имейте в виду, что этот запрос может работать не на всех системах баз данных. Он работает с MariaDB / MySQL , но он определенно не работает с SQLite.
Если вы разрабатываете приложение и запускаете все на одном компьютере, то всегда помните, что в будущем вам может понадобиться запуск базы данных на отдельном сервере. Поэтому неплохо было бы разработать свои запросы для данной ситуации.
Ссылки / кредиты
Flask-sqlalchemy query datetime intervals
https://stackoverflow.com/questions/30495935/flask-sqlalchemy-query-datetime-intervals
SQLAlchemy datetime operations on server side
https://stackoverflow.com/questions/12540175/sqlalchemy-datetime-operations-on-server-side
SQLAlchemy default DateTime
https://stackoverflow.com/questions/13370317/sqlalchemy-default-datetime
Using DATEADD in sqlalchemy
https://stackoverflow.com/questions/15572292/using-dateadd-in-sqlalchemy/15573750#15573750
Подробнее
SQLAlchemy
Оставить комментарий
Комментируйте анонимно или войдите в систему, чтобы прокомментировать.
Комментарии (5)
Оставьте ответ
Ответьте анонимно или войдите в систему, чтобы ответить.
Another great post. I have to agree it is surprising most solutions on the web ignore the potential Python app versus DBMS clock difference problem. The kind of bug that waits patiently until its time!
FWIW `sqlalchemy.sql.expression.func.now() - timedelta(minutes=2)` seems to work for Postgres and I would expect others with a NOW() function.
I just noticed the post above immediately displayed as posted 1 hour ago . . . I'm on GMT (London).
Me again! But after posting the second post, the first changed to "2 minutes ago", the second started at "0 seconds ago"!
Then after the third post all three now say "1 hour" . . . had to wait 5 minutes to post this one :-)
Thank you for reporting this. I am still working on many parts of this website ... will fix this soon.
Недавний
- Скрытие первичных ключей базы данных UUID вашего веб-приложения
- Don't Repeat Yourself (DRY) с Jinja2
- SQLAlchemy, PostgreSQL, максимальное количество строк для user
- Показать значения в динамических фильтрах SQLAlchemy
- Безопасная передача данных с помощью шифрования Public Key и pyNaCl
- rqlite: альтернатива dist с высокой степенью готовности и SQLite
Большинство просмотренных
- Используя Python pyOpenSSL для проверки SSL-сертификатов, загруженных с хоста
- Использование UUID вместо Integer Autoincrement Primary Keys с SQLAlchemy и MariaDb
- Подключение к службе на хосте Docker из контейнера Docker
- Использование PyInstaller и Cython для создания исполняемого файла Python
- SQLAlchemy: Использование Cascade Deletes для удаления связанных объектов
- Flask Удовлетворительный запрос API проверка параметров запроса с помощью схем Маршмэллоу