SQLAlchemy calculs côté serveur datetime
Pourquoi vous devriez essayer d'éviter les datetime calculs côté client à SQLAlchemy moins que...
Vous trouverez de nombreux exemples de SQLAlchemy datetime calcul utilisant par exemple la timedelta fonction s. PythonPourquoi ? Je ne comprends pas cela, sauf que c'est facile. Mais est-ce correct ?
Supposons que nous voulons que tous les enregistrements utilisateur ou objets créés il y a deux heures et la définition de l'enregistrement / objet est :
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)
Ensuite, si nous pouvions utiliser Python pour sélectionner les enregistrements / objets ajoutés au cours des 10 dernières minutes, nous pourrions faire quelque chose comme ceci :
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()
Le généré est :
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)}
Cela ne fonctionne, donne des résultats valides que si :
- le serveur de la base de données est exécuté sur le même serveur que celui sur lequel le Python code est exécuté
- le serveur de base de données fonctionne sur un serveur différent du serveur de Python code et le temps sur les deux serveurs est parfaitement synchronisé.
Supposons que vous avez un serveur de base de données séparé et que le temps de ce serveur est de 2 minutes désynchronisé. Alors vous vous trompez, les résultats sont incomplets. J'écris des requêtes côté serveur depuis de nombreuses années et je suis surpris qu'il y ait peu d'attention pour cela dans les SQLAlchemy questions et réponses.
La seule façon d'obtenir les bons résultats est d'utiliser les datetime timbres des enregistrements du serveur de base de données et d'y ajouter datetime ou d'en soustraire datetime des données. Avec MariaDB / MySQL vous pouvez utiliser l'instruction d'intervalle :
SELECT user.* FROM user WHERE created_on > (NOW() - INTERVAL 2 HOUR)
Malheureusement, je n'ai pas pu trouver une solution SQLAlchemy qui serait valable pour toutes les bases de données. SQLAlchemy a l' text() objet, il passe la valeur à la requête. Avec text(), la SQLAlchemy requête devient :
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()
Le généré est :
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
Sachez que cette requête peut ne pas fonctionner sur tous les systèmes de base de données. Il fonctionne avec MariaDB / MySQL mais il ne fonctionne certainement pas avec SQLite.
Si vous développez une application et exécutez tout sur un seul ordinateur, gardez toujours à l'esprit qu'à l'avenir vous voudrez peut-être exécuter la base de données sur un serveur séparé. Ce n'est donc pas une mauvaise idée de développer vos requêtes pour cette situation.
Liens / crédits
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
En savoir plus...
SQLAlchemy
Laissez un commentaire
Commentez anonymement ou connectez-vous pour commenter.
Commentaires (5)
Laissez une réponse
Répondez de manière anonyme ou connectez-vous pour répondre.
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.
Récent
- Masquer les clés primaires de la base de données UUID de votre application web
- Don't Repeat Yourself (DRY) avec Jinja2
- SQLAlchemy, PostgreSQL, nombre maximal de lignes par user
- Afficher les valeurs des filtres dynamiques SQLAlchemy
- Transfert de données sécurisé grâce au cryptage à Public Key et à pyNaCl
- rqlite : une alternative à haute disponibilité et dist distribuée SQLite
Les plus consultés
- Utilisation des Python's pyOpenSSL pour vérifier les certificats SSL téléchargés d'un hôte
- Utiliser UUIDs au lieu de Integer Autoincrement Primary Keys avec SQLAlchemy et MariaDb
- Utiliser PyInstaller et Cython pour créer un exécutable Python
- Connexion à un service sur un hôte Docker à partir d'un conteneur Docker
- SQLAlchemy : Utilisation de Cascade Deletes pour supprimer des objets connexes
- Flask RESTful API validation des paramètres de la requête avec les schémas Marshmallow