IdentifiantMot de passe
Loading...
Mot de passe oublié ?Je m'inscris ! (gratuit)
Navigation

Inscrivez-vous gratuitement
pour pouvoir participer, suivre les réponses en temps réel, voter pour les messages, poser vos propres questions et recevoir la newsletter

Python Discussion :

Sqlite3 : activer foreign_keys


Sujet :

Python

  1. #1
    Membre averti
    Avatar de antoinev2
    Profil pro
    Inscrit en
    Septembre 2008
    Messages
    177
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Septembre 2008
    Messages : 177
    Points : 376
    Points
    376
    Par défaut Sqlite3 : activer foreign_keys
    Bonjour, j'ai un problème quand je manipule une BDD SQLite3 via Python, concernant les commandes PRAGMA, à savoir foreign_keys.

    Les tables concernées sont la table adresse, "T_d_Adresse", et la table personne, "T_d_Personne".
    Dans la structure de la BD, je rattache chaque "personne" à une "adresse" (et je peux rattacher plusieurs personnes à la même adresse).
    On retrouve donc le champ "id_adresse" :
    • dans la table T_d_Adresse, où il est la clé primaire
    • dans la table T_d_Personne, où il est une clé étrangère


    J'arrive à activer foreign_keys quand je manipule ma BDD par la console de windows.
    Voici un exemple dans lequel ma tentative de suppression échoue, grâce à l'intégrité référentielle :



    Et voici ce qui se passe quand j'essaie de faire la même chose mais à partir du shelle de l'IDLE : je n'arrive pas à activer foreign_keys (impression écran ci-dessous en deux parties) :


  2. #2
    Expert éminent sénior
    Homme Profil pro
    Architecte technique retraité
    Inscrit en
    Juin 2008
    Messages
    21 287
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Manche (Basse Normandie)

    Informations professionnelles :
    Activité : Architecte technique retraité
    Secteur : Industrie

    Informations forums :
    Inscription : Juin 2008
    Messages : 21 287
    Points : 36 776
    Points
    36 776
    Par défaut
    Par défaut, les foreign keys ne sont pas activées, d'ailleurs dans l'exemple sqlite vous le mettez en route via PRAGMA foreign_keys=ON.
    Avec Python, c'est pareil: il faudra exécuter le SQL 'PRAGMA foreign_keys=ON;' via cursor.execute.
    - W
    Architectures post-modernes.
    Python sur DVP c'est aussi des FAQs, des cours et tutoriels

  3. #3
    Membre averti
    Avatar de antoinev2
    Profil pro
    Inscrit en
    Septembre 2008
    Messages
    177
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Septembre 2008
    Messages : 177
    Points : 376
    Points
    376
    Par défaut
    Merci pour ta réponse, et justement c'est ce que j'essaie de faire...
    à tout hasard je viens de retenter avec le point-virgule de fin, au cas où, mais ensuite lorsque je tente de supprimer une donnée "adresse" à laquelle est rattachée une donnée "personne", eh bien la suppression fonctionne.

    Je passe à côté de quelque chose, mais quoi...

  4. #4
    Expert éminent sénior
    Homme Profil pro
    Architecte technique retraité
    Inscrit en
    Juin 2008
    Messages
    21 287
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Manche (Basse Normandie)

    Informations professionnelles :
    Activité : Architecte technique retraité
    Secteur : Industrie

    Informations forums :
    Inscription : Juin 2008
    Messages : 21 287
    Points : 36 776
    Points
    36 776
    Par défaut
    Postez du code pouvant être facilement exécuté via un cut&paste qui reproduit ce qui vous ennuie.
    - W
    Architectures post-modernes.
    Python sur DVP c'est aussi des FAQs, des cours et tutoriels

  5. #5
    Membre averti
    Avatar de antoinev2
    Profil pro
    Inscrit en
    Septembre 2008
    Messages
    177
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Septembre 2008
    Messages : 177
    Points : 376
    Points
    376
    Par défaut
    Ok, alors voici déjà un lien pour télécharger la base de données : CarnetAdresse.db

    Et voici le code que j'exécute dans le shell de l'IDLE :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    #importer le module et se connecter
    import sqlite3
    cnx = sqlite3.connect('D:/CarnetAdresse.db')
    curs = cnx.cursor()
    #lister les personnes
    for row in curs.execute('SELECT id_personne, prenom, nom, id_adresse FROM T_d_Personne'):
        print row
    #lister les adresses
    for row in curs.execute('SELECT id_adresse, cp, ville FROM T_d_Adresse'):
        print row
    #activer les foreign_keys
    curs.execute("PRAGMA foreign_keys=on")
    #supprimer l'adresse 9, à laquelle la personne Joe Lecowboy est rattachée
    curs.execute('DELETE FROM T_d_Adresse WHERE id_adresse=9')
    #valider la suppression
    cnx.commit()
    #lister les adresses
    for row in curs.execute('SELECT id_adresse, cp, ville FROM T_d_Adresse'):
        print row
    #et se déconnecter
    curs.close()
    cnx.close()

  6. #6
    Expert éminent
    Avatar de tyrtamos
    Homme Profil pro
    Retraité
    Inscrit en
    Décembre 2007
    Messages
    4 462
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Var (Provence Alpes Côte d'Azur)

    Informations professionnelles :
    Activité : Retraité

    Informations forums :
    Inscription : Décembre 2007
    Messages : 4 462
    Points : 9 249
    Points
    9 249
    Billets dans le blog
    6
    Par défaut
    Bonjour,

    L'activation est facile à faire, et il n'est même pas nécessaire d'ouvrir un curseur. Après avoir obtenu une connexion avec connect, on fait:

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    cnx.execute("PRAGMA foreign_keys=on;")
    Et il faut le faire à chaque nouvelle connexion avec la base.
    Un expert est une personne qui a fait toutes les erreurs qui peuvent être faites, dans un domaine étroit... (Niels Bohr)
    Mes recettes python: http://www.jpvweb.com

  7. #7
    Membre averti
    Avatar de antoinev2
    Profil pro
    Inscrit en
    Septembre 2008
    Messages
    177
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Septembre 2008
    Messages : 177
    Points : 376
    Points
    376
    Par défaut
    Oui, je sais, Google m'a amené vers ton site Tyrtamos et j'y ai découvert la possibilité d'exécuter une requête sans passer par un objet Cursor.
    Merci d'ailleurs, ça fait plaisir d'apprendre qqch.

    J'ai testé bien sûr mais je n'arrive pas à faire fonctionner ça...
    Voici ci-dessous un exemple avec les résultats :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    import sqlite3
    cnx = sqlite3.connect('D:/CarnetAdresse.db')
    cnx.execute("PRAGMA foreign_keys=on;")
    <sqlite3.Cursor object at 0x011C5380>

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    for row in cnx.execute('SELECT id_adresse, cp, ville FROM T_d_Adresse'):
        print row
    (1, u'0', u'NC')
    (2, u'75000', u'Paris')
    (3, u'35000', u'Rennes')
    (4, u'59000', u'Roubaix')
    (5, u'75000', u'Paris')
    (6, u'29000', u'Brest')
    (7, u'64000', u'PAU')
    (8, u'64320', u'ARESSY')
    (9, u'64640', u'HELETTE')

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    cnx.execute('DELETE FROM T_d_Adresse WHERE id_adresse=9')
    <sqlite3.Cursor object at 0x011C5200>

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    cnx.commit()
    for row in cnx.execute('SELECT id_adresse, cp, ville FROM T_d_Adresse'):
        print row
    (1, u'0', u'NC')
    (2, u'75000', u'Paris')
    (3, u'35000', u'Rennes')
    (4, u'59000', u'Roubaix')
    (5, u'75000', u'Paris')
    (6, u'29000', u'Brest')
    (7, u'64000', u'PAU')
    (8, u'64320', u'ARESSY')

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    curs.close()
    cnx.close()
    Dans cet exemple, la suppression de l'adresse 9 a réussi : alors que l'intégrité référentielle devrait l'interdire...

    Ou alors le problème se situe ailleurs, mais dans ce cas, pourquoi la même manipulation effectuée depuis la console windows donne le résultat qui me semble logique, à savoir que la suppression est interdite?

    J'hésitais depuis tout-à-l'heure mais j'en ai besoin, tant pis pour le mur

  8. #8
    Expert éminent sénior
    Homme Profil pro
    Architecte technique retraité
    Inscrit en
    Juin 2008
    Messages
    21 287
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Manche (Basse Normandie)

    Informations professionnelles :
    Activité : Architecte technique retraité
    Secteur : Industrie

    Informations forums :
    Inscription : Juin 2008
    Messages : 21 287
    Points : 36 776
    Points
    36 776
    Par défaut
    Salut,
    Il faudrait expliciter la contrainte sur la foreign key pour que le delete ne se fasse pas. La documentation sqlite est ici.
    Pas le temps là tout de suite de coder un exemple qui fonctionne, mais il suffit d'ajouter ON DELETE RESTRICT à la déclaration de la foreign key.
    - W
    Architectures post-modernes.
    Python sur DVP c'est aussi des FAQs, des cours et tutoriels

  9. #9
    Expert éminent
    Avatar de tyrtamos
    Homme Profil pro
    Retraité
    Inscrit en
    Décembre 2007
    Messages
    4 462
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Var (Provence Alpes Côte d'Azur)

    Informations professionnelles :
    Activité : Retraité

    Informations forums :
    Inscription : Décembre 2007
    Messages : 4 462
    Points : 9 249
    Points
    9 249
    Billets dans le blog
    6
    Par défaut
    Il faudrait en effet connaitre les instructions de construction des tables pour voir s'il est normal que l'effacement soit accepté.

    NB: même si certaines requêtes peuvent se faire sans curseur (comme les PRAGMA), il vaut mieux, en général, les utiliser.

    Sans répondre à la question, je suggère d'utiliser un modèle de code pour tout ce qui est (tentative de) modification de la base de données comme suit:

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    try:
        cur.execute("delete ...")
        cur.execute("update ...")
        cnx.commit()
    except sqlite3.Error, err:
        cnx.rollback()
        print "Erreur:", err.args[0]
    J'ai cru un moment que seul commit pouvait générer des exceptions, mais c'est faux: n'importe quel execute le peut aussi. Ce modèle de code permet de faire fonctionner correctement les transactions, d'être sûr que les résultats des requêtes ne sont pas restés dans un cache, et éventuellement de prendre connaissance des bons messages d'erreurs.
    Un expert est une personne qui a fait toutes les erreurs qui peuvent être faites, dans un domaine étroit... (Niels Bohr)
    Mes recettes python: http://www.jpvweb.com

  10. #10
    Expert éminent sénior
    Homme Profil pro
    Architecte technique retraité
    Inscrit en
    Juin 2008
    Messages
    21 287
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Manche (Basse Normandie)

    Informations professionnelles :
    Activité : Architecte technique retraité
    Secteur : Industrie

    Informations forums :
    Inscription : Juin 2008
    Messages : 21 287
    Points : 36 776
    Points
    36 776
    Par défaut
    Citation Envoyé par tyrtamos Voir le message
    NB: même si certaines requêtes peuvent se faire sans curseur (comme les PRAGMA), il vaut mieux, en général, les utiliser.
    Le "cursor" n'existe pas en tant que tel côté Sqlite3, c'est un engin spécifique à la DB API Python.
    Lorsque vous utilisez la connection pour faire un execute sans cursor, l'API en créera à votre place qui sera "temporaire".
    Sans répondre à la question, je suggère d'utiliser un modèle de code pour tout ce qui est (tentative de) modification de la base de données comme suit:

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    try:
        cur.execute("delete ...")
        cur.execute("update ...")
        cnx.commit()
    except sqlite3.Error, err:
        cnx.rollback()
        print "Erreur:", err.args[0]
    Quite à encapsuler l'exécution du SQL pour attraper les erreurs, autant définir une API, genre:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    def do_sql(cursor, sql, values=None, tx_mode=False, bulkmode=False):
        '''tx_mode force commit/rollback
        bulkmode: submit values via executemany,
        '''
    Le code est assez simple et il évite de dupliquer du code et de se prendre le chou pour la plupart des types de requêtes.

    J'ai cru un moment que seul commit pouvait générer des exceptions, mais c'est faux: n'importe quel execute le peut aussi.
    Ben oui, lire la documentation ici
    - W
    Architectures post-modernes.
    Python sur DVP c'est aussi des FAQs, des cours et tutoriels

  11. #11
    Expert éminent sénior
    Homme Profil pro
    Architecte technique retraité
    Inscrit en
    Juin 2008
    Messages
    21 287
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Manche (Basse Normandie)

    Informations professionnelles :
    Activité : Architecte technique retraité
    Secteur : Industrie

    Informations forums :
    Inscription : Juin 2008
    Messages : 21 287
    Points : 36 776
    Points
    36 776
    Par défaut
    Citation Envoyé par tyrtamos Voir le message
    Il faudrait en effet connaitre les instructions de construction des tables pour voir s'il est normal que l'effacement soit accepté.
    Tout à fait mais il suffit de la lire dans le .db
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    CREATE TABLE T_d_Adresse (
    id_adresse INTEGER PRIMARY KEY AUTOINCREMENT UNIQUE,
    adresse1 VARCHAR (80) NOT NULL,
    adresse2 VARCHAR (80),
    adresse3 VARCHAR (80),
    cp VARCHAR (5),
    ville VARCHAR (80),
    crea DATETIME NOT NULL,
    maj DATETIME NOT NULL
    )
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    CREATE TABLE T_d_Personne (
    id_personne INTEGER PRIMARY KEY AUTOINCREMENT UNIQUE,
    prenom VARCHAR (25) NOT NULL,
    nom VARCHAR (40),
    id_adresse INTEGER NOT NULL,
    crea DATETIME NOT NULL,
    maj DATETIME NOT NULL,
    FOREIGN KEY (id_adresse) REFERENCES T_d_Adresse(id_adresse)
    )
    Il faudrait faire un alter de T_d_Personne pour y voir:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    FOREIGN KEY (id_adresse) REFERENCES T_d_Adresse(id_adresse) ON DELETE RESTRICT
    - W
    Architectures post-modernes.
    Python sur DVP c'est aussi des FAQs, des cours et tutoriels

  12. #12
    Membre averti
    Avatar de antoinev2
    Profil pro
    Inscrit en
    Septembre 2008
    Messages
    177
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Septembre 2008
    Messages : 177
    Points : 376
    Points
    376
    Par défaut
    Merci à vous deux pour votre aide, par contre je n'y arrive pas encore :

    je suis reparti des deux fichiers d'instructions SQL utilisés pour créer la db, et ai modifié le premier pour inclure le "ON DELETE RESTRICT".

    Voici des liens vers les fichiers isql1.txt et isql2.txt

    Voici la BD dans son état après création et alimentation par ces deux fichiers.

    Ci-dessous les manips effectuées par la console windows :
    http://www.antoinev2.com/DiversPubli...le_windows.gif

    Ci-dessous, les manips effectuées dans le shell IDLE :
    http://www.antoinev2.com/DiversPubli...ell_idle_1.gif
    http://www.antoinev2.com/DiversPubli...ell_idle_2.gif

    Et donc, le problème persiste...


    Tyrtamos je retiens cette manière de faire, que je comprends :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    try:
        cur.execute("delete ...")
        cur.execute("update ...")
        cnx.commit()
    except sqlite3.Error, err:
        cnx.rollback()
        print "Erreur:", err.args[0]

    Wiztricks, je ne comprends pas bien ce code :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    def do_sql(cursor, sql, values=None, tx_mode=False, bulkmode=False):
        '''tx_mode force commit/rollback
        bulkmode: submit values via executemany,
        '''
    Les arguments cursor et sql, ok.
    L'argument values, c'est pour exécuter la requête un peu comme cette façon expliquée sur le site officiel de Python?
    http://www.antoinev2.com/DiversPubli.../PythonDoc.gif
    values correspond à t?

  13. #13
    Expert éminent
    Avatar de tyrtamos
    Homme Profil pro
    Retraité
    Inscrit en
    Décembre 2007
    Messages
    4 462
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Var (Provence Alpes Côte d'Azur)

    Informations professionnelles :
    Activité : Retraité

    Informations forums :
    Inscription : Décembre 2007
    Messages : 4 462
    Points : 9 249
    Points
    9 249
    Billets dans le blog
    6
    Par défaut
    Bonjour,

    En ce qui concerne la proposition de simplification de wiztricks:

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    def do_sql(cursor, sql, values=None, tx_mode=False, bulkmode=False):
    j'ai déjà essayé, mais ça marche rarement parce qu'une transaction est à appliquer à un "paquet logique" de requêtes et non à une seule, sauf dans les cas très simples. Par exemple dans ton cas, si tu rentres en même temps une nouvelle personne avec une nouvelle adresse, si l'adresse échoue, il ne faut pas que la nouvelle personne puisse être enregistrée (=>rollback): c'est donc l'ensemble qui doit se trouver dans la même transaction. On peut bien sûr enchainer les requêtes dans le même script sql mais ça ne clarifie pas le code.


    Avec ta base sans modification (donc sans le "on delete restrict" proposé), j'ai essayé de supprimer l'adresse numéro 9 avec un script Python: il y a bien refus! Voilà mon code d'essai:

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    #!/usr/bin/python
    # -*- coding: utf-8 -*-
    from __future__ import division
    # Python 2.7
     
    import sqlite3
     
    #############################################################################
    def ouvrebase(basesql, curseur=False):
        """ouvre la base concours.db3 et renvoie la connexion
           si curseur est True, renvoie aussi un curseur
        """
        try:
            cnx = sqlite3.connect(basesql)
            cnx.execute("PRAGMA foreign_keys=on;") # active le foreign key
        except sqlite3.Error, err:
            raise # renvoie l'exception à l'appelant
        if curseur:
            cur = cnx.cursor()
            return cnx, cur
        return cnx
     
    #############################################################################
    def fermebase(cnx, cur=None):
        """Ferme la base ouverte avec la connexion cnx
           si le curseur cur est donné, il est fermé aussi
        """
        if cur != None:
            cur.close()
        cnx.close()
     
    #############################################################################
     
    basesql = u"CarnetAdresse.db"
     
    cnx, cur = ouvrebase(basesql, True)
     
    try:
        cur.execute("""DELETE 
                       FROM T_d_Adresse 
                       WHERE id_adresse=9 """)
        cnx.commit()
    except sqlite3.Error, err:
        cnx.rollback()
        print u"Erreur", err.args[0]    
     
    fermebase(cnx, cur)
    L'exécution de ce code donne l'erreur:

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    Erreur foreign key constraint failed
    Ce qui est le résultat correct. Donc, l'interpréteur Python fait bien son boulot!

    Il y a une autre solution qu'il m'arrive d'utiliser: au lieu de laisser l'interdiction de suppression d'adresse par contrainte "foreign key", j'accepte cette suppression d'adresse mais avec la suppression en cascade de tout ce qui dépend d'elle (on delete cascade). Ça peut être dangereux si on se trompe, mais dans certaines applications, c'est extrêmement pratique!


    A mon avis: abandonne Idle pour piloter ta base en interactif, et utilise n'importe quelle console non programmée en Python. J'aime bien idle, mais je m'en méfie parce qu'on fait exécuter du Python par du Python, et on ne sait jamais dans quelle mesure le code de Idle interagit avec le code qu'on lui demande d'exécuter. En tout cas, j'ai déjà rencontré des problèmes d'exécution qui n'existaient qu'avec Idle (dans des cas plus complexes il est vrai, genre thread).
    Un expert est une personne qui a fait toutes les erreurs qui peuvent être faites, dans un domaine étroit... (Niels Bohr)
    Mes recettes python: http://www.jpvweb.com

  14. #14
    Expert éminent sénior
    Homme Profil pro
    Architecte technique retraité
    Inscrit en
    Juin 2008
    Messages
    21 287
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Manche (Basse Normandie)

    Informations professionnelles :
    Activité : Architecte technique retraité
    Secteur : Industrie

    Informations forums :
    Inscription : Juin 2008
    Messages : 21 287
    Points : 36 776
    Points
    36 776
    Par défaut
    Salut,

    Citation Envoyé par tyrtamos Voir le message
    En ce qui concerne la proposition de simplification de wiztricks:

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    def do_sql(cursor, sql, values=None, tx_mode=False, bulkmode=False):
    j'ai déjà essayé, mais ça marche rarement parce qu'une transaction est à appliquer à un "paquet logique" de requêtes et non à une seule, sauf dans les cas très simples.
    Définir une fonction qui traite la plupart des cas permet de ne pas oublier le try...except... et d'éviter la gestion toujours difficile d'un code construit avec des cut&paste. C'est une démarche de factorisation élémentaire qui ouvre la voie à la création d'abstraction plus compliquées comme sous classer le cursor et/où écrire un with adapté.
    nota, si c'est la question utiliser un ORM comme SQLAchemy qui apporte déjà nombre d'abstraction sera préférable à ré-inventer la roue: ses abstractions sont indépendantes du SGDB même pour des opérations de bases comme l'écriture des requêtes SQL et la gestion des "cursors".

    Mais avant d'en arriver là, il faut avoir passé du temps à apprendre ce qu'est un SGDB et SQlite n'est pas si mal pour débuter - i.e. ne pas faire des choses compliquées avec: impossible d'utiliser proprement des outils tels que SQLAchemy sans passer par là.

    Citation Envoyé par tyrtamos Voir le message
    A mon avis: abandonne Idle pour piloter ta base en interactif, et utilise n'importe quelle console non programmée en Python. J'aime bien idle, mais je m'en méfie parce qu'on fait exécuter du Python par du Python, et on ne sait jamais dans quelle mesure le code de Idle interagit avec le code qu'on lui demande d'exécuter. En tout cas, j'ai déjà rencontré des problèmes d'exécution qui n'existaient qu'avec Idle (dans des cas plus complexes il est vrai, genre thread).
    Si vous regardez comment est écrit IDLE, vous constaterez que ce n'est pas du Python qui exécute du Python mais un IDE écrit en Python qui comme la plupart des IDE (nombreux sont écrit en Java) exécute le code "utilisateur" dans des threads séparées avec lesquelles il dialogue via RPC.
    L’interpréteur Python qui exécute le code utilisateur n'est pas le même que celui qui exécute IDLE: ça ne se mélange pas.

    Sûr qu'essayez de mettre au point un programme multi-thread, risquera de bloquer IDLE. Ce n'est pas spécifique à IDLE, le support du debug de programme multi-threads n'a jamais été facile pour un IDE: on peut aussi planter Netbeans et Eclipse sans problème avec des codes pourri, il suffit de les gratter là ou ils ne faut pas..

    Le fait est que les fonctionnalités d'IDLE restent limitées et que c'est un IDE spécifique à Python: c'est bien pour débuter en Python. Comme il est livré avec Python, c'est quand même bien d'avoir sous la main un éditeur contextuel qui connaisse Python. A défaut, il reste vi, notepad et des allez retours avec ftp ou la clé USB: ce n'est pas toujours pratique.

    Comme c'est un IDE spécifique à Python et que dans son métier le programmeur devra maîtriser plusieurs langage, le professionnel préférera utiliser un IDE plus versatile côté langages de programmation.
    Apprendre à bien l'utiliser (ou se sentir en confiance avec) prend un temps non négligeable et ce savoir faire acquis se maintien par la pratique.
    A la limite, il sera plus facile d'apprendre un nouveau langage qui pourra s'intégrer dans son IDE favori.

    Mais le débutant qui découvre Python et la programmation ne se pose pas encore ces questions.

    - W
    Architectures post-modernes.
    Python sur DVP c'est aussi des FAQs, des cours et tutoriels

  15. #15
    Expert éminent
    Avatar de tyrtamos
    Homme Profil pro
    Retraité
    Inscrit en
    Décembre 2007
    Messages
    4 462
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Var (Provence Alpes Côte d'Azur)

    Informations professionnelles :
    Activité : Retraité

    Informations forums :
    Inscription : Décembre 2007
    Messages : 4 462
    Points : 9 249
    Points
    9 249
    Billets dans le blog
    6
    Par défaut
    Bonjour wiztricks,

    Merci pour ces clarifications.

    Je n'ai rien contre Idle mais je constate qu'il pose de temps en temps (assez rarement cependant) des problèmes spécifiques que j'attribue, peut-être à tort, au fait qu'il est écrit en Python. Quand je développais avec, j'avais souvent une console sous la main pour vérifier que c'était bien le programme qui ne marchait pas et non l'outil de développement. Alors que je n'ai jamais ce genre de problème avec, par exemple, Eclipse (écrit en java) ou PyScripter (écrit en delphi).

    J'ai rencontré par contre le même genre de problème quand j'ai essayé de travailler avec eric écrit en Python/PyQt4: malgré le caractère séduisant du produit, je l'ai rapidement abandonné.

    Bref, vis-à-vis du problème posé par le posteur, j'ai un point de vue empirique: je constate que ça ne marche pas quand il utilise Idle, mais que ça marche avec une console ou quand, moi, j'utilise Python directement "hors Idle". Ma recommandation en découle.


    Quand à ta proposition "def do_sql(...)": ma solution marche bien, mais consomme pas mal de lignes de code. Aussi, si tu as une solution pour traiter de façon simple un ensemble de 4 ou 5 requêtes SQL faisant partie de la même transaction (sans aller jusqu'à utiliser un ORM), ça me conviendrait assez.
    Un expert est une personne qui a fait toutes les erreurs qui peuvent être faites, dans un domaine étroit... (Niels Bohr)
    Mes recettes python: http://www.jpvweb.com

  16. #16
    Membre averti
    Avatar de antoinev2
    Profil pro
    Inscrit en
    Septembre 2008
    Messages
    177
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Septembre 2008
    Messages : 177
    Points : 376
    Points
    376
    Par défaut
    Ok, merci!
    donc je retiens bien que le même code ne donne pas forcément le même résultat, selon si on l'exécute dans le shell de l'IDLE ou si on l'intègre à une application Python.
    J'avais supposé que le comportement serait identique, sans vérifier en exécutant le code directement dans une application.
    Donc j'ai testé dans le shell pour gagner du temps...
    Pour le coup j'ai plutôt perdu du temps mais j'ai appris qu'il faut se méfier du shell.

    Pour l'éditeur, en réalité j'utilise Notepad++ que je trouve assez pratique, le shell c'est juste pour des petits tests. Je vais passer à Eclipse + PyDev, il me semble que pas mal de développeurs Python travaillent avec ça.

    Sinon je retiens la possibilité de l'effacement en cascade, de toute façon dans mon appli, avant de tenter d'exécuter l'instruction sql DELETE, je commencerai justement par vérifier si une donnée est liée avant de la supprimer, pour afficher un message explicite à l'utilisateur si besoin.

    Merci pour votre aide! Je vérifierai juste ce soir que de mon côté aussi, quand le code est placé dans mon appli l'intégrité référentielle est respectée, on ne sait jamais.

  17. #17
    Expert éminent sénior
    Homme Profil pro
    Architecte technique retraité
    Inscrit en
    Juin 2008
    Messages
    21 287
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Manche (Basse Normandie)

    Informations professionnelles :
    Activité : Architecte technique retraité
    Secteur : Industrie

    Informations forums :
    Inscription : Juin 2008
    Messages : 21 287
    Points : 36 776
    Points
    36 776
    Par défaut
    Salut,
    Citation Envoyé par tyrtamos Voir le message
    Bref, vis-à-vis du problème posé par le posteur, j'ai un point de vue empirique: je constate que ça ne marche pas quand il utilise Idle, mais que ça marche avec une console ou quand, moi, j'utilise Python directement "hors Idle". Ma recommandation en découle.
    Comme je l'ai déjà signalé au PO, s'il ne soumet pas un code qu'on peux récupérer et exécuter facilement, je n'irai pas saisir son code.

    Quand à ta proposition "def do_sql(...)": ma solution marche bien, mais consomme pas mal de lignes de code. Aussi, si tu as une solution pour traiter de façon simple un ensemble de 4 ou 5 requêtes SQL faisant partie de la même transaction (sans aller jusqu'à utiliser un ORM), ça me conviendrait assez.
    Par défaut, SQlite travaille en autocommit.
    Le début d'une transaction est la fin de la précédente.
    Les transactions au niveau connection, pas cursor.
    => ouvrir plusieurs connections à la même BDD a du sens lorsque plusieurs threads/process ont à transformer une séquence de requêtes en une opération atomique.
    En deçà, c'est pour jouer.

    Regardez le tuto d'Hellmann sur le sujet et essayez de comprendre la documentation de SQlite sur ces sujets.

    Gérer proprement tous les cas demande pas mal de boulot dans un contexte où SQlite n'est pas le plus adapté (threads, process) même pour les transactions simples.

    Autant utiliser un ORM: vous développez un truc en pouvant le déployez sur le SGDB qui supportera le mieux la charge (et que les équipes du support client veulent bien supporter).

    - W
    Architectures post-modernes.
    Python sur DVP c'est aussi des FAQs, des cours et tutoriels

  18. #18
    Expert éminent
    Avatar de tyrtamos
    Homme Profil pro
    Retraité
    Inscrit en
    Décembre 2007
    Messages
    4 462
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Var (Provence Alpes Côte d'Azur)

    Informations professionnelles :
    Activité : Retraité

    Informations forums :
    Inscription : Décembre 2007
    Messages : 4 462
    Points : 9 249
    Points
    9 249
    Billets dans le blog
    6
    Par défaut
    Bonjour wiztricks,

    Je reviens sur le point suivant qui m'a un peu inquiété:

    Citation Envoyé par wiztricks Voir le message
    Par défaut, SQlite travaille en autocommit.
    L'autocommit, c'est: toute requête qui modifie la table est immédiatement appliquée. Tout se passe comme si chacune de ces requêtes était entourée par un début et une fin de transaction implicites.

    Mais ce n'est pas conforme à mon expérience de sqlite3: si après une telle requête j'oublie un cnx.commit() explicite, la table n'est pas modifiée (ça m'est déjà arrivé ). J'ai donc creusé un peu.

    Quand on veut le mode autocommit, il faut mettre isolation_level à None. Mais l'est-il par défaut à l'ouverture d'une connexion? Non! Mais quel mode a-t-il? Bizarrement, le mode par défaut est... une chaine vide. Les autres modes sont: “DEFERRED”, “IMMEDIATE” or “EXCLUSIVE”, ce qui correspond au type de verrous placés sur les tables pendant la transaction et au moment où ils sont placés et retirés pour éviter que des lectures/écritures concurrentes ne soient fausses.

    Donc, dans le mode par défaut, on sait qu'on n'est pas en autocommit, mais on ne sait pas vraiment dans quel mode on est: ça ce n'est pas bien! Heureusement, dans mes applications je n'ai pas d'accès concurrent, donc ça ne me gênera pas. Mais quand même...

    Pour confirmer qu'on n'est pas en autocommit, je fais un petit code avec plusieurs requêtes 'UPDATE' et une pause intercalée de type "x=raw_input()" . A chaque pause, j'essaie de lire la base par un autre moyen ("SqliteExpert"). Et effectivement, aucune modification de la base n'arrive avant le cnx.commit() explicite.

    Et si j'oublie ce commit explicite et que je ferme la base: cette base n'est pas modifiée.

    Il y a d'autres particularités de sqlite3 vis-à-vis des transactions, en particulier des débuts de transaction implicites (ex: à chaque requête qui modifie la table si aucune transaction n'est déjà ouverte), et des fins de transaction implicites (ex: lors de l'utilisation de CREATE TABLE si une transaction est déjà ouverte). Il faut vraiment éplucher la notice pour comprendre tous les choix faits par sqlite3 et/ou son pilote Python.

    En conclusion, je suis rassuré sur le fait que ce comportement de sqlite3 valide le modèle de code que j'ai proposé plus haut (et que j'utilise souvent), mais je ne suis pas très content de la notice qui pourrait être plus claire sur un sujet aussi important.

    N'hésite pas à ré-intervenir sur ce sujet: c'est important pour moi que mes applications SGBDR soient solides!
    Un expert est une personne qui a fait toutes les erreurs qui peuvent être faites, dans un domaine étroit... (Niels Bohr)
    Mes recettes python: http://www.jpvweb.com

  19. #19
    Membre averti
    Avatar de antoinev2
    Profil pro
    Inscrit en
    Septembre 2008
    Messages
    177
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Septembre 2008
    Messages : 177
    Points : 376
    Points
    376
    Par défaut
    Bonjour, et mauvaise nouvelle : pas d'erreur de mon côté, la requête est exécutée.

    Ca peut être par rapport à la version de Python? C'est la version 2.6.2 sur mon poste.

    Wiztricks j'avais cru comprendre que tu voulais du code à exécuter comme moi dans le shell, d'où mon post qui suivait ta demande.
    D'ailleurs je n'avais pas encore intégré le code qui pose problème à mon appli.

    Pour tester "dans une appli" et non dans le shell, j'ai repris ton code Tyrtamos, et modifié un peu puisque je suis sous windows, et ajouté quelques print et raw_input :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    # -*- coding: utf-8 -*-
    from __future__ import division
    # Python 2.6
     
    import sqlite3
     
    #############################################################################
    def ouvrebase(basesql, curseur=False):
        # """ouvre la base concours.db3 et renvoie la connexion
           # si curseur est True, renvoie aussi un curseur
        # """
        try:
            cnx = sqlite3.connect(basesql)
            cnx.execute("PRAGMA foreign_keys=on;") # active le foreign key
        except sqlite3.Error, err:
            raise # renvoie l'exception à l'appelant
        if curseur:
            cur = cnx.cursor()
            return cnx, cur
        return cnx
     
    #############################################################################
    def fermebase(cnx, cur=None):
        # """Ferme la base ouverte avec la connexion cnx
           # si le curseur cur est donné, il est fermé aussi
        # """
        if cur != None:
            cur.close()
        cnx.close()
     
    #############################################################################
     
    # basesql = u"CarnetAdresse.db"
    basesql = u"D:/Test/CarnetAdresse.db"
     
     
    cnx, cur = ouvrebase(basesql, True)
     
    try:
        cur.execute("""DELETE 
                       FROM T_d_Adresse 
                       WHERE id_adresse=9 """)
        cnx.commit()
        print u'requête exécutée avec succès.'
    except sqlite3.Error, err:
        cnx.rollback()
        print u"Erreur", err.args[0]    
     
    fermebase(cnx, cur)
    vSuite = raw_input('tapez entree pour fermer')
    et voici le résultat :


    Et je vérifie le contenu de la table, l'adresse 9 a bien été supprimée :



    Sinon j'ai une solution avec les triggers, en fouillant j'ai retrouvé des tests que j'avais faits il y a un moment et qui fonctionnaient avec ça, dans le shell.
    A vérifier.
    Mais bon j'aimerais comprendre d'où vient le problème...

  20. #20
    Expert éminent sénior
    Homme Profil pro
    Architecte technique retraité
    Inscrit en
    Juin 2008
    Messages
    21 287
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Manche (Basse Normandie)

    Informations professionnelles :
    Activité : Architecte technique retraité
    Secteur : Industrie

    Informations forums :
    Inscription : Juin 2008
    Messages : 21 287
    Points : 36 776
    Points
    36 776
    Par défaut
    Citation Envoyé par antoinev2 Voir le message
    Sinon je retiens la possibilité de l'effacement en cascade, de toute façon dans mon appli, avant de tenter d'exécuter l'instruction sql DELETE, je commencerai justement par vérifier si une donnée est liée avant de la supprimer, pour afficher un message explicite à l'utilisateur si besoin.
    Plusieurs choses.
    la contrainte d'intégrité initiale n'était pas "cascade" mais "restrict": la différence entre les deux est que:
    - cascade détruira automatiquement les records dans la table qui définit la fk vers le record qu'on détruit.
    - restrict empêche lui de détruire s'il y a des fk...

    Je ne sais pas comment vous vous débrouillez mais un reproducteur de votre problème c'est un exemple minimal en dehors de votre application.

    Exemple:
    On crée deux tables, un backref, on insère des enregistrements et on regarde ce qui remonte:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    import sqlite3 
     
    DDL='''pragma foreign_keys=1;
    create table base(
       id integer primary key
    );
    create table refs(
       id integer primary key,
       backref references base(id) ON DELETE RESTRICT
    );
    insert into base(id) values(1);
    insert into base(id) values(2);
    insert into refs(id, backref) values(1, 1);
    '''
    cnx = sqlite3.connect(':memory:')
    cnx.executescript(DDL)
     
    # pas de fk qui empeche de detruire id=2
    cnx.execute('delete from base where id=2;')
     
    # ici on a une exception integrity error.
    cnx.execute('delete from base where id=1;')
    C'est quand même pas si compliqué, non?

    - W
    Architectures post-modernes.
    Python sur DVP c'est aussi des FAQs, des cours et tutoriels

+ Répondre à la discussion
Cette discussion est résolue.
Page 1 sur 2 12 DernièreDernière

Discussions similaires

  1. [langage] Pb avec Active perl :s
    Par Cetras dans le forum Langage
    Réponses: 2
    Dernier message: 02/09/2003, 13h28
  2. Fonction qui s'active lorsqu'un Form bouge
    Par Xavier dans le forum C++Builder
    Réponses: 3
    Dernier message: 22/05/2003, 12h54
  3. [VB6][active x] faire du multi-thread avec vb
    Par pecheur dans le forum VB 6 et antérieur
    Réponses: 9
    Dernier message: 20/05/2003, 12h01
  4. Tester connexion Internet active sous Windows
    Par Altau dans le forum Développement
    Réponses: 3
    Dernier message: 12/08/2002, 12h43
  5. [Kylix] Clé d'activation
    Par Anonymous dans le forum EDI
    Réponses: 1
    Dernier message: 27/03/2002, 23h19

Partager

Partager
  • Envoyer la discussion sur Viadeo
  • Envoyer la discussion sur Twitter
  • Envoyer la discussion sur Google
  • Envoyer la discussion sur Facebook
  • Envoyer la discussion sur Digg
  • Envoyer la discussion sur Delicious
  • Envoyer la discussion sur MySpace
  • Envoyer la discussion sur Yahoo