Journal python-sql n'est pas un ORM

Posté par (page perso) . Licence CC by-sa
Tags :
20
14
sept.
2013

La version 0.1 de la librairie python-sql vient d'être publiée. python-sql est une librairie Python pour écrire des requêtes SQL de manière pythonique. Elle n'a pas la prétention d'être un ORM comme SQLAlchemy et de ce fait, elle ne cache pas le SQL bien au contraire elle propose une manière plus flexible d'écrire une requête qu'en manipulant des strings.
Par défaut, elle génère uniquement des requêtes SQL standard mais un système de Flavor permet d'adapter le résultat à un n'importe quel SGDB spécifique (il n'y a aucune dépendance à des librairies de base de données).
Pour l'instant, elle vient d'être utilisée par Tryton comme outil de base de son ORM pour la prochaine version, ainsi que pour le développement d'hebi un micro-framework pour du BI.

  • # Intérêt par rapport à du SQL brut

    Posté par (page perso) . Évalué à 6.

    Pour moi, le SQL est déjà un langage simple et adapté pour interroger les bases de données.
    Après, on a inventé des ORM qui fournissent une grosse couche d'abstraction et permettent de manipuler des objets.

    Mais là, je ne vois pas trop l'intérêt de python-sql : ça rajoute une couche intermédiaire qu'on doit apprendre, et j'aurais plutôt envie d'écrire directement en SQL dans ce cas.

    • [^] # Re: Intérêt par rapport à du SQL brut

      Posté par . Évalué à 7.

      Quand tu dois construire des requêtes de manière dynamique des "formulaire de recherche avancée" etc, c'est plus pratique et sécuritaire d'utiliser un ORM ou une bibliothèque de ce genre que de container des bouts de SQL sous forme de chaines.

      Cela t'offre aussi une certaine abstraction par rapport au SGBD, par exemple si tu veux utiliser Postgres en production mais SQLite en développement et en test.

      Disons que c'est un compromis intéressant entre ORM et plain SQL.

      • [^] # Re: Intérêt par rapport à du SQL brut

        Posté par (page perso) . Évalué à 5.

        L'approche n'est pas sans rappeler par un certain point les requêtes Linq, que l'on retrouve en C#. L'avantage de ce genre d'approche, est d'avoir une requête dont la syntaxe est vérifiée à la compilation, et non à l'exécution.

        • [^] # Re: Intérêt par rapport à du SQL brut

          Posté par . Évalué à 9. Dernière modification le 14/09/13 à 18:17.

          L'avantage de ce genre d'approche, est d'avoir une requête dont la syntaxe est vérifiée à la compilation, et non à l'exécution.

          Pour le coup c'est un peu raté.

          " python-sql […] est une librairie Python "

          Plus sérieusement le scope de Linq et de python-sql me semblent très très différents et tu fais fausse route en les comparant. python-sql c'est juste une façon de générer des requêtes SQL depuis une API plutôt que d'écrire la chaîne SQL. Ni plus ni moins. Il n'y a aucun lien avec les objets métier, la base, les schema, ou la façon d'interroger la base. Ca n'offre presque aucune sécurité supplémentaire non plus, d'ailleurs à priori je ne vois aucun soucis pour faire une bête injection sql avec python-sql.

          Python-sql n'a l'air de s'intéresser qu'à une toute petite partie très précise du problème. À voir si ca peut être utile et à quoi.

          • [^] # Re: Intérêt par rapport à du SQL brut

            Posté par . Évalué à 3.

            à priori je ne vois aucun soucis pour faire une bête injection sql avec python-sql.

            Ah ? Je ne suis pas allé voir le code, mais de base je supposait qu'il profitait de cette abstraction pour échapper les entrées de données ou forcer l'utilisation de "bindings" …

            • [^] # Re: Intérêt par rapport à du SQL brut

              Posté par . Évalué à 4.

              >>> select = user.select()
              >>> select.where = user.name == "toto';"
              >>> tuple(select)
              ('SELECT * FROM "user" AS "a" WHERE ("a"."name" = %s)', ("toto';",))
              

              Après ça dépend comment c'est utilisé et ou sont fait les vérifs. Mais de ce que j'ai compris ce n'est pas dans les buts du projet. C'est vraiment un DSL pour générer du SQL. L'auteur me corrigera si je dis des conneries.

              Pour le code ca va très vite à explorer. Il n'y a pas de magie.

              • [^] # Re: Intérêt par rapport à du SQL brut

                Posté par (page perso) . Évalué à 4.

                C'est justement un des intérêts de python-sql, c'est qu'il met automatiquement en paramètre les valeurs 'externes' suivant la PEP249. Et comme toute bonne librairie de connexion doit appliquer l'échappement sur les paramètres, il n'y a pas de risque d'injection SQL.
                Construire des queries un peu importants manuellement et bien mettre tout en paramètres est vraiment très fastidieux. C'était une des raisons de la création de python-sql.

          • [^] # Re: Intérêt par rapport à du SQL brut

            Posté par (page perso) . Évalué à 2.

            " python-sql […] est une librairie Python "

            J'avais bien compris qu'il s'agissait d'une bibliothèque python ;) Et comme cela me rappelait une techno disponible pour un autre langage (en l'occurence, le C#), j'ai fait une comparaison.

            Ensuite, en C#, linq, c'est deux choses :
            * une bibliothèque
            * une abstraction sous forme de sucre syntaxique

            En utilisant la forme classique (sous forme d'API donc), un select peut s'écrire simplement ainsi :
            users.Where(x => x.name == "toto")

            En utilisant la forme intégrée, cela donnerait
            from u in users where u.name == "toto" select u

            A moins d'aller modifier le langage Python lui-même, la seconde approche n'est pas et ne sera pas possible.

            C'est donc vis-à-vis de la première approche que j'ai fait le rapprochement. Ensuite, il est possible de construire des requêtes un peu plus complexe en composant différentes fonctions :
            users.Where(x => x.name.StartWith("a")).OrderBy(x => x.name)

            Et on peut continuer longuement ainsi !

            Pour en revenir à la bibliothèque Python, même si elle ne permet pas de faire une vérification des entités manipulés à la compilation (nom des tables, des colonnes), elle permet néanmoins d'avoir une vérification de la syntaxe SQL. Mais vérification malgré tout limité à cause de l'utilisation d'attributs (par exemple, pour les clauses where). Une faute de frappe, de style
            select.wehre = user.name == "Toto"
            ne sera pas détectée.

            En fonction des souhaits de l'auteur, il serait peut être intéressant de ne se baser que sur l'utilisation de méthodes pour construire la requête pour fournir une vérification syntaxique, et donc d'en remplacer la clause where par une approche du style :
            select.where(user.name == "Toto")

  • # SQLalchemy

    Posté par . Évalué à 6.

    SQLAlchemy n'est pas ORM, ou plutôt, n'est pas qu'un ORM. Il y a toute une partie dédiée à la génération de requête SQL : http://docs.sqlalchemy.org/en/rel_0_8/core/tutorial.html

    Contrairement à python-sql et l'appel à la fonction tuple(), c'est un peu plus complexe pour récupérer la requête et les paramètres qui vont avec car c'est prévu pour être passé à un objet engine, qui fait se boulot là à la place. Ceci dit, avec un minimum, on peut quand même récupérer toutes ces informations.

    Vous avez considéré l'utilisation de cette partie de SQLAlchemy ou pas ? Si oui, qu'est-ce qui vous a rebuté ?

    • [^] # Re: SQLalchemy

      Posté par (page perso) . Évalué à 2.

      Oui mais avec SQLAlchemy, il faut déclarer la définition des tables avant de pouvoir écrire des requêtes or on ne voulait pas avoir à faire cette déclaration.

      • [^] # Re: SQLalchemy

        Posté par . Évalué à 4.

        Tu peux à priori utiliser Table(autoload=True), et tu n'as pas besoin de déclarer les colonnes et tout, si c'est ce que tu entends par "définitions des tables".

        • [^] # Re: SQLalchemy

          Posté par (page perso) . Évalué à 2.

          Sauf que c'est pas très performant parce qu'il faut aller lire la définition via le SGDB et plus comme on ne veut dépendre d'aucun SGDB en particulier.

          • [^] # Re: SQLalchemy

            Posté par . Évalué à 3.

            Même si effectivement, il y a un cout à aller demander à la base de données ses définitions, je ne pense pas que ça soit très impactant niveau performance, surtout que c'est effectué qu'une seule fois au chargement de l'application, et que c'est probablement optimisé et rapide de toute manière (pas testé, mais je suppose). C'est éventuellement un problème pour des scripts qui ne s'exécute qu'une seule fois, mais je pense que le cout est minime par rapport au reste malgré tout.

            Pas sur de comprendre la deuxième partie de ta phrase ? Le SQL a beau être une norme, vous serez obligés de dépendre d'un SGBD de toutes manières, ne serait-ce que pour les différences de syntaxe entre chaque moteur de base de données.

            J'avais fais un truc qui construisait des requêtes avec cette partie de SQLAlchemy, et qui générait derrière des vrais requêtes SQL complètes (pas séparées entre requête et données comme vous faites dans python-sql) suivant le type de moteur qu'on voulait interroger (SQLite, PostgreSQL, MySQL, etc.), pour le donner ensuite au requêteur de Twisted. C'était une fonction de 5-6 lignes à tout péter je crois, mais j'arrive pas à remettre la main dessus :(

          • [^] # Re: SQLalchemy

            Posté par . Évalué à 2.

            Ben, c'est un peu con ça. C'est précisément de connaître le SGBD et la structure de la table qui va permettre à la couche d'abstraction de savoir comment (dé)sérialiser les valeurs sans demander à l'utilisateur. Sinon comment tu fais pour sérialiser, par exemple, un objet datetime ?

            • [^] # Re: SQLalchemy

              Posté par (page perso) . Évalué à 3.

              C'est pris en charge par la PEP249: http://www.python.org/dev/peps/pep-0249/#type-objects-and-constructors
              Et donc il suffit d'utiliser l'expression Literal avec le bon type.
              De plus la plupart des librairies de connexion ont des méthodes pour enregistrer des adaptateurs:

              • [^] # Re: SQLalchemy

                Posté par . Évalué à 4.

                C'est pris en charge par la PEP249: http://www.python.org/dev/peps/pep-0249/#type-objects-and-constructors

                Sauf que par exemple ça ne gère pas les fractions de seconde ni les fuseaux horaires.
                Avec une bonne couche d'abstraction, si j'ai un datetime avec un fuseau horaire et des microsecondes et que je le sauve dans une base PostgreSQL, je ne perds pas d'information.

                De plus la plupart des librairies de connexion ont des méthodes pour enregistrer des adaptateurs:

                Ben oui, mais qui écrit / invoque l'adaptateur ? Si c'est ta bibliothèque, alors elle n'ignore plus le type de BD utilisé :-) Si c'est l'utilisateur, ta bibliothèque est sous-optimale par rapport à d'autres solutions.

                • [^] # Re: SQLalchemy

                  Posté par (page perso) . Évalué à 3.

                  Sauf que par exemple ça ne gère pas les fractions de seconde ni les fuseaux horaires.
                  Avec une bonne couche d'abstraction, si j'ai un datetime avec un fuseau horaire et des microsecondes et que je le sauve dans une base PostgreSQL, je ne perds pas d'information.

                  Ça c'est un manquement dans la PEP. Bien que perso, je pense qu'il est préférable de stocker tout en UTC.
                  De plus, tous les SGDB ne supportent pas les fuseaux horaires par example SQLite.

                  Ben oui, mais qui écrit / invoque l'adaptateur ? Si c'est ta bibliothèque, alors elle n'ignore plus le type de BD utilisé :-) Si c'est l'utilisateur, ta bibliothèque est sous-optimale par rapport à d'autres solutions.

                  Je ne trouve pas que ce soit le rôle de la librairie de faire ces choix.
                  Par exemple, pour Tryton, on a des adaptateurs spécifiques pour les float et Decimal sous PostgreSQL.

                  • [^] # Re: SQLalchemy

                    Posté par . Évalué à 2.

                    Ça c'est un manquement dans la PEP.

                    Oui et donc ? Tu te limites à ce que spécifie cette PEP ?

                    Je ne trouve pas que ce soit le rôle de la librairie de faire ces choix.

                    Si une couche d'abstraction de BD ne choisit pas la bonne conversion en fonction de la BD sous-jacente, à quoi sert-elle ?

                    • [^] # Re: SQLalchemy

                      Posté par (page perso) . Évalué à 1.

                      C'est sur ce point qu'on ne se comprend pas. python-sql n'est pas un couche d'abstraction de BD. C'est juste une librairie pour générer des requêtes SQL en les écrivant explicitement avec des objet python plutôt que de manipuler des strings.
                      python-sql ne connait rien de la BD sur la quelle la requête va être exécutée, d’ailleurs la même requête peut être utilisée pour différentes BD tant qu'on utilise la bonne Flavor de contexte. On se repose entièrement sur les librairies de connexion pour faire leur boulot. S'il y a des manques pour l'une d'entre elle, c'est celle-ci qu'il faut améliorer (pour info, psycopg2 gère les datetime avec fuseau horaire).

  • # C'est une bonne idée et cela manquait

    Posté par . Évalué à 1. Dernière modification le 16/09/13 à 14:05.

    Bonjour,

    Pour ma part je pense qu'il s'agit d'un bonne idée, avec un ORM on est un peu prisonnier, c'est très pratique sur certains points, mais sur d'autres on a vite fait de reprendre les "connexion.execute" pour envoyer directement la requête SQL que l'on souhaite, et pas celle que génère l'ORM, pour des raisons de maintenance et/ou de performance.
    Cela sera toujours plus lisible que les concaténations de chaines :)

    • [^] # Re: C'est une bonne idée et cela manquait

      Posté par . Évalué à 1.

      pour des raisons de maintenance et/ou de performance.

      Je trouve personnellement que SQLAlchemy fournit toutes les alternatives afin d'optimiser les requêtes (pré-chargement de certaines relations , rapatriement des scalaires seuls, etc) et de ne pas perdre en lisibilité en ayant des très bonnes performances.

      • [^] # Re: C'est une bonne idée et cela manquait

        Posté par . Évalué à 1.

        Je n'ai pas dit le contraire, et d'ailleurs l'apprentissage et la maitrise d'un outil comme SQL Alchemy permet certainement de coder mieux et plus vite. Je connais un peu et de ce que j'en ai vu c'est plus que sympathique.

        Mais parfois, avec des structures de base que l'on n'a pas crée et des demandes tordues de requêtes, je préfère structurer les requêtes à ma manière surtout si c'est moi qui doit maintenir derrière.

Suivre le flux des commentaires

Note : les commentaires appartiennent à ceux qui les ont postés. Nous n'en sommes pas responsables.