Cependant, s'il ne peut pas se connecter, alors
db
n'existera pas plus bas - c'est pourquoi j'ai définidb = None
au dessus de. Cependant, est-ce une bonne pratique ?
Non, réglage db = None
n'est pas la meilleure pratique. Il y a deux possibilités, soit la connexion à la base de données fonctionnera, soit elle ne fonctionnera pas.
-
La connexion à la base de données ne fonctionne pas :
Comme l'exception déclenchée a été interceptée et non relancée, vous continuez jusqu'à ce que vous atteigniez
cursor = db.Cursor()
.db == None
, donc, une exception qui ressemble àTypeError: 'NoneType' object has no attribute 'Cursor'
sera relevé. Comme l'exception générée lors de l'échec de la connexion à la base de données a déjà été interceptée, la raison de l'échec est déguisée.Personnellement, je déclencherais toujours une exception de connexion à moins que vous ne réessayiez sous peu. Comment vous l'attrapez dépend de vous; si l'erreur persiste, j'envoie un e-mail pour dire "allez vérifier la base de données".
-
La connexion à la base de données fonctionne :
La variable
db
est assigné dans votretry:... except
bloc. Si leconnect
la méthode fonctionne alorsdb
est remplacé par l'objet de connexion.
Dans tous les cas, la valeur initiale de db
n'est jamais utilisé.
Cependant, j'ai entendu dire que l'utilisation de la gestion des exceptions pour le contrôle de flux comme ceci est une mauvaise pratique.
Contrairement à d'autres langages, Python fait utiliser la gestion des exceptions pour le contrôle de flux. À la fin de ma réponse, j'ai lié plusieurs questions sur Stack Overflow et les programmeurs qui posent une question similaire. Dans chaque exemple, vous verrez les mots "mais en Python".
Cela ne veut pas dire que vous devriez aller trop loin, mais Python utilise généralement le mantra EAFP, "Il est plus facile de demander pardon que la permission." Les trois exemples les plus votés dans Comment vérifier si une variable existe ? sont de bons exemples de la façon dont vous pouvez utiliser ou non le contrôle de flux.
L'imbrication des exceptions est-elle une bonne idée ? Ou existe-t-il un meilleur moyen de gérer les exceptions dépendantes/en cascade comme celle-ci ?
Il n'y a rien de mal avec les exceptions imbriquées, encore une fois tant que vous le faites sainement. Considérez votre code. Vous pouvez supprimer toutes les exceptions et envelopper le tout dans un try:... except
bloc. Si une exception est déclenchée, vous savez de quoi il s'agit, mais il est un peu plus difficile de déterminer exactement ce qui n'a pas fonctionné.
Que se passe-t-il alors si vous voulez dire vous-même un e-mail en cas d'échec de cursor.execute
? Vous devriez avoir une exception autour de cursor.execute
afin d'accomplir cette seule tâche. Vous relancez ensuite l'exception pour qu'elle soit prise dans votre try:...
. Ne pas relancer entraînerait la poursuite de votre code comme si de rien n'était et quelle que soit la logique que vous aviez mise dans votre try:...
externe traiter une exception serait ignoré.
En fin de compte, toutes les exceptions sont héritées de BaseException
.
De plus, il y a certaines parties (par exemple, les échecs de connexion) où j'aimerais que le script se termine simplement - d'où l'appel sys.exit() commenté.
J'ai ajouté une classe simple et comment l'appeler, ce qui correspond à peu près à la façon dont je ferais ce que vous essayez de faire. Si cela doit être exécuté en arrière-plan, l'impression des erreurs n'en vaut pas la peine - les gens ne seront pas assis là à rechercher manuellement les erreurs. Ils doivent être connectés quelle que soit votre méthode habituelle et les personnes appropriées doivent être informées. J'ai supprimé l'impression pour cette raison et l'ai remplacée par un rappel de connexion.
Comme j'ai divisé la classe en plusieurs fonctions lorsque le connect
la méthode échoue et une exception est levée le execute
l'appel ne sera pas exécuté et le script se terminera après avoir tenté de se déconnecter.
import cx_Oracle
class Oracle(object):
def connect(self, username, password, hostname, port, servicename):
""" Connect to the database. """
try:
self.db = cx_Oracle.connect(username, password
, hostname + ':' + port + '/' + servicename)
except cx_Oracle.DatabaseError as e:
# Log error as appropriate
raise
# If the database connection succeeded create the cursor
# we-re going to use.
self.cursor = self.db.cursor()
def disconnect(self):
"""
Disconnect from the database. If this fails, for instance
if the connection instance doesn't exist, ignore the exception.
"""
try:
self.cursor.close()
self.db.close()
except cx_Oracle.DatabaseError:
pass
def execute(self, sql, bindvars=None, commit=False):
"""
Execute whatever SQL statements are passed to the method;
commit if specified. Do not specify fetchall() in here as
the SQL statement may not be a select.
bindvars is a dictionary of variables you pass to execute.
"""
try:
self.cursor.execute(sql, bindvars)
except cx_Oracle.DatabaseError as e:
# Log error as appropriate
raise
# Only commit if it-s necessary.
if commit:
self.db.commit()
Alors appelez-le :
if __name__ == "__main__":
oracle = Oracle.connect('username', 'password', 'hostname'
, 'port', 'servicename')
try:
# No commit as you don-t need to commit DDL.
oracle.execute('ddl_statements')
# Ensure that we always disconnect from the database to avoid
# ORA-00018: Maximum number of sessions exceeded.
finally:
oracle.disconnect()
Lectures complémentaires :
cx_Oracle
documents
Pourquoi ne pas utiliser les exceptions comme flux de contrôle régulier ?
La gestion des exceptions en python est-elle plus efficace que PHP et/ou d'autres langages ?
Arguments pour ou contre l'utilisation de try catch comme opérateurs logiques