angle-uparrow-clockwisearrow-counterclockwisearrow-down-uparrow-leftatcalendarcard-listchatcheckenvelopefolderhouseinfo-circlepencilpeoplepersonperson-fillperson-plusphoneplusquestion-circlesearchtagtrashx

SQLAlchemy datetime расчёты со стороны сервера

Почему вы должны стараться избегать расчетов со стороны клиента, если SQLAlchemy только...

24 июня 2019
post main image
Original photo unsplash.com/@nputra.

Вы найдете множество примеров 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)

Оставьте ответ

Ответьте анонимно или войдите в систему, чтобы ответить.

avatar

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.

avatar
Анонимный посетитель (не вошел в систему) 3 года назад Анонимный посетитель (не вошел в систему)

I just noticed the post above immediately displayed as posted 1 hour ago . . . I'm on GMT (London).

avatar
Анонимный посетитель (не вошел в систему) 3 года назад Анонимный посетитель (не вошел в систему)

Me again! But after posting the second post, the first changed to "2 minutes ago", the second started at "0 seconds ago"!

avatar
Анонимный посетитель (не вошел в систему) 3 года назад Анонимный посетитель (не вошел в систему)

Then after the third post all three now say "1 hour" . . . had to wait 5 minutes to post this one :-)

avatar
peter 3 года назад Анонимный посетитель (не вошел в систему)

Thank you for reporting this. I am still working on many parts of this website ... will fix this soon.