Programmation orientée objet sous Python

a marqué ce sujet comme résolu.

Bonjour à tous,

Dans le cadre d’un exercice, je dois construire une application en POO sous PyCharm, mais sans interface graphique (on utilisera donc cette appli depuis la console). Je dois pour ce faire séparer les couches métier et DAO, ce que je n’avais jamais fait avant, et c’est entre autres cela qui me pose problème.

Voici en quoi doit consister l’application:

Elle doit gérer les temps et les classements de courses d’athlétisme, je m’explique:

Cette appli s’adresse d’une part à des visiteurs lambdas (sans besoin de se connecter) qui désirent soit rechercher des courses prévues, soit consulter des courses passées (classement et chronos de tout le monde) soit consulter une fiche coureur de n’importe quel coureur ayant déjà participé à une course (fiche regroupant tous les chronos+classements des courses auxquelles ce coureur a déjà participé).

D’autre part cette appli s’adresse également aux "organisateurs" de courses (besoin de se connecter). Les organisateurs sont les seuls à:

-pouvoir inscrire (le un coureur à une course (c’est mal fait mais un coureur ne peut s’inscrire lui même)

-pouvoir créer/modifier/supprimer une nouvelle course (libellé/date et heure/lieu/distance)

-un organisateur peut créer autant de courses qu’il le souhaite mais ne peut pas toucher à celles des autres.

Ce que l’on me donne:

-Un ficher de quelques courses au format csv (je peux en ajouter ou en supprimer à ma guise)

-Un fichier csv répertoriant N coureurs. Je n’ai pas besoin de créer d’autres coureurs supplémentaires, mais pour chaque course i, je vais appeler une API que l’on me donne pour générer Ni coureurs pari ces N

Comme je le disais au début, mon gros point de blocage est la DAO, notion que l’on vient de m’introduire. En effet, je vais devoir créer sur SQL une table Coureurs, une table Courses ainsi qu’une table organisateurs (enfin je suppose).

D’après ce que j’ai compris la couche DAO, par le biais des requêtes SQL, va faire le lien entre la couche métier et mes tables SQL. Mais concrètement, je n’ai aucune idée de la manière d’implémenter cela…

Si quelqu’un pouvait m’éclairer sur ce sujet,

Merci d’avance,

Alexouu :)

Bonjour,

C’est bien de rappeler que DAO correspond à Data Access Object. Sur le coup ça me parlait pas, pourtant je l’utilise quotidiennement via une ORM (Object Relationnal Mapping). L’ORM est un peu le robot automatique de la DAO : on lui donne le modèle de la base de données et il va nous construire dynamiquement des classes managers.

En l’occurrence ici, pas d’ORM dans ton exercice, c’est à toi de construire les classes managers mais le principe reste le même. ;)

Une classe manager va effectivement servir d’interface entre ta couche métiers (ton application) et ta base de données (tes données). Elle possède des méthodes permettant d’interroger, modifier ou altérer la BDD ; c’est elle qui contiendra les requêtes SQL (ou NoSQL).

Le principe est que ta classe manager traduit constamment tes objets métiers en requêtes SQL et inversement. Je vais prendre un cas très simple avec un utilisateur :

class UtilisateurManager extends Connecteur {
  insert(item) {
    /* Création de l'utilisateur */
    return this.query(
      'INSERT INTO utilisateur (prenom, nom, email) '+
      'VALUES ("'+item.prenom+'", "'+item.nom+'", "'+item.email+'")'
    );
  }

  queryByEmail(email) {
    /* Récupération d'un utilisateur via son email */
    let items = this.query(
      'SELECT prenom, nom, email FROM utilisateur WHERE email = "'+email+'" LIMIT 1'
    );
    /* Transcription */
    if(items.length == 1) {
      return new Utilisateur(items[0].prenom, items[0].nom, items[0].email);
    }
    /* Sans gestion d'erreurs */
    return new Utilisateur();
  }
}

class Utilisateur {
  constructor (prenom='', nom='', email='') {
    this.prenom = prenom;
    this.nom = nom;
    this.email = email;
  }
}

/* Création d'un utilisateur */
let martin = new Utilisateur('Martin', 'GUITARE', 'mguitare@gmail.com');
UtilisateurManager.insert(martin);

/* Récupération d'un utilisateur */
let test = UtilisateurManager.queryByEmail('mguitare@gmail.com');
console.log(test.prenom); // affiche 'Martin'

Alors je tiens juste à préciser, qu’il y’a plein de choses qui n’irait pas dans un contexte de production. Tout d’abord d’un point de vue sécurité, dans la requête on peut injecter tout et surtout n’importe quoi (il vaut mieux utiliser des bibliothèques de connexion disposant d’une couche de sécurité / pré-validation comme PDO en PHP). Ensuite j’ai totalement ignoré les getters et les setters, même si pour illustrer j’ai pris la structure du Javascript (c’est pour éviter d’alourdir la lecture du code). Au niveau de la modélisation, l’utilisateur n’a pas d’ID c’est pourtant essentiel que la clé primaire serve à garantir l’unicité de l’entrée (on va dire qu’ici c’est l’email). Pour terminer, j’ai choisi d’étendre mon manager à la classe Connecteur qui dispose des éléments de connexion à la base de données et d’un système primitif de requête (symbolisé par this.query) - ce n’est pas mauvais en production, c’est juste que la méthode d’instanciation est ambigu, je précise donc qu’il s’agit d’une classe statique (utilisable sans instanciation) - ce n’est pas une norme / obligation.

L’avantage de cette structure, c’est que l’on peut utiliser et modifier nos objets comme bon nous semble, sans se soucier de la requête à exécuter (en tout cas, une fois tout en place). Si je veux modifier l’email de Martin, il suffit d’écrire martin.email = 'martintin@yahoo.fr'; et de passer l’objet dans le manager UtilisateurManager.update(martin); pour que celui-ci exécute la bonne requête.

En espérant que ce soit suffisamment compréhensible. :) j’ai dû ré-écrire deux fois mon illustration parce qu’au départ, j’étais parti sur l’ORM avec lequel je suis plus familier - ça permet une meilleure cohésion entre les managers, les classes métiers et les models mais ce n’était pas la question initiale.

+1 -0

Salut Yarflam,

Tout d’abord merci pour ta réponse détaillée ! :)

Néanmoins je n’ai pas tout compris, dans le détail comment ça marche une classe manager?

Déjà, il faut bien créer une classe manager par table que l’on va avoir dans notre base de données c’est ça? Puis au niveau des attributs et des méthodes, ça se passe comment?

Aussi pour reprendre ton exemple, à la ligne 10 tu écris "queryByEmail", mais ce n’est qu’un exemple de query, donc il faudrait toutes les lister normalement? Du style queryByNom, querybyPrenom, queryByNom_Prenom, etc?

J’admets que c’est encore flou dans ma tête actuellement :)

D’autant plus que de l’autre côté, il va rester quoi comme méthodes dans les classes métier?

Déjà, il faut bien créer une classe manager par table que l’on va avoir dans notre base de données c’est ça ?

En effet, c’est une classe manager par table. Exception toutefois avec des tables de liaisons (= deux ou trois clés étrangères formant une clé primaire avec parfois un attribut en complément) car ça ne correspond pas à une logique métier en particulier - on n’a pas besoin de manipuler directement une relation.

Puis au niveau des attributs et des méthodes, ça se passe comment ?

Y’a pas d’attributs particuliers. Au niveau des méthodes, ça va correspondre aux opérations que tu cherches à automatiser. Par exemple, tu souhaites extraire la liste des coureurs ayant dépassé le 32 Km/h, tu vas devoir créer une méthode que tu nommeras par exemple getBestRunnersKm() qui exécutera la requête SELECT * FROM coureurs WHERE moy_vitesse > 32. En retour, tu recevras un tableau d’objets instanciant la classe Coureur avec les données de tes champions.

Aussi pour reprendre ton exemple, à la ligne 10 tu écris "queryByEmail", mais ce n’est qu’un exemple de query, donc il faudrait toutes les lister normalement ?

Non pas forcément, tu crées celles dont tu as besoin. Après tu peux les créer dynamiquement en utilisant une méthode magique. En Python c’est getattr, ça te permet de capturer une méthode à la volée. Avec une simple détection, tu vérifies si une méthode type queryByX est appelé, si oui tu envoies ce X comme paramètre (Attention à la sécurité ! Déjà vérifies que le nom est accessible et puis ensuite un test de typage sur le paramètre).

J’admets que c’est encore flou dans ma tête actuellement.

Dans un premier temps, identifies les ensembles que tu vas devoir manipuler. Tu as des coureurs d’un côté, à quoi ils ressemblent ? Ont-ils un nom, prénom, email, âge, adresse etc ? Toutes ces caractéristiques vont appartenir à ta classe métier Coureur. Avec cette classe, je peux par exemple instancier Usain Bolt usain = new Coureur('Usain', 'Bolt', 1986, 'Jamaïque'), il existe ainsi virtuellement comme entité. Sauf que cette entité j’ai besoin quelle soit stockée en base. C’est à ce moment là que j’appelle ma classe CoureursManager pour lui demander d’ajouter mon nouveau coureur CoureursManager.insert(usain). Son rôle est de récupérer les informations essentielles du coureur pour construire la requête correspondante (l’idéal c’est de récupérer l’ID auto-incrémenté après l’insert pour donner la possibilité à la classe manager de mettre à jour tout de suite après - ce serait bête de créer deux entrées).

Ensuite … aujourd’hui, je dois compter les points de la course 127 dans laquelle joue Usain Bolt. Il me suffira d’interroger une fois encore mon manager pour récupérer les joueurs CoureursManager.queryByCourse(127) parmi les instances retournés par la requête, j’aurai bien de nouveau un objet coureur instancié avec les données de Usain Bolt. Là, je modifie ses points usain.setPoint(400) et je synchronise l’objet avec la base en appelant de nouveau mon manager CoureursManager.update(usain).

Ce que tu as dans tes classes métiers est (presque toujours) en miroir avec ce que tu as dans ta base de données et ce sont tes classes managers qui s’assurent de leurs cohérences. T’imagine si tu devais à chaque mise à jour ré-écrire ta requête ? Le temps perdu … le rôle de cette classe est indispensable ! :)

Schématiquement ça revient à effectuer cet échange :

Contrôleur —(Joueurs de la course 127 ?)—> CoureursManager.queryByCourse —(SELECT * FROM coureurs WHERE course = 127)—> BDD —(BDD.Objects)—> CoureursManager.queryByCourse —(Array<Coureur>)—> Contrôleur.

Encore une fois merci de ton temps Yarflam, je comprends petit à petit !

Cependant, en adaptant un exemple qu’avait donné mon prof, une classe DAO (ou classe abstraite DAO?) ressemblerait à cela, pourrais-tu m’éclairer là dessus?

class DaoCoureur(AbstractDao):

    def create(self, coureur):
        """
        Insère un coureur en base, et retourne ce même coureur mis à jour de l'id généré par la base
        :param coureur:
        :return: l'coureur mis à jour de son id en base
        """
        cur = self.connection.cursor()
        try:
            cur.execute(
                "INSERT INTO coureur (nom, prenom, annee_naissance,sexe,adresse,mail,"
                "telephone,num_licence,association,id_club) VALUES (%s, %s, %s, %s, %s, %s, %s, %s, %s, %s) RETURNING id_coureur;",
                (coureur.nom, coureur.prenom, coureur.annee_naissance, coureur.sexe, coureur.adresse,
                 coureur.mail, coureur.telephone, coureur.num_licence, coureur.association,coureur.id_club))


            coureur.id_coureur = cur.fetchone()[0]
            # la transaction est enregistrée en base
            self.connection.commit()
        except:
            # la transaction est annulée
            self.connection.rollback()
            raise
        finally:
            cur.close()

        return coureur

    def find_all(self):
        """
        :return: l'intégralité des coureurs en base
        """
        with self.connection.cursor() as cur:
            cur.execute(
                "select id_coureur, nom, prenom, annee_naissance, sexe, adresse, mail, telephone, num_licence, association,id_club from coureur") # A votre avis pourquoi on fait pas select * from coureurs ?

            # on récupère des tuples et les transforme en objects Coureur
            result = [Coureur(id_coureur=item[0], nom=item[1], prenom=item[2], annee_naissance=item[3], sexe=item[4],
                              adresse=item[5],mail=item[6],telephone=item[7],num_licence=item[8],association=item[9],id_club=item[10])
                      for item in cur.fetchall()]
            return result

    """
    Avec le choix d'utiliser une classe abastraite dont héritent les différentes DAO, il est obligatoire de creer
    toutes les méthodes abstraites dans les classes filles, même si on ne leur code aucun comportement pour le moment.
    Si vous utiliser PyCharm, il peut créer la définition des méthodes automatiquement.
    """
    def find_by_id(self, id):
        """
        :return : le club d'id donné
        :param id:
        :return:
        """

        raise NotImplementedError

    def update(self, business_object):
        raise NotImplementedError

Merci,

ALexouu :)

Encore une fois merci de ton temps Yarflam, je comprends petit à petit !

Alexouu

Avec plaisir Alexouu ! :p

Cependant, en adaptant un exemple qu’avait donné mon prof, une classe DAO (ou classe abstraite DAO?) ressemblerait à cela, pourrais-tu m’éclairer là dessus?

Alexouu

Une classe abstraite est une classe que l’on peut pas instancier directement, elle nécessite un héritage. L’héritage ça permet tout simplement d’accéder à des attributs et à des méthodes communes à plusieurs instances de classe. Ici l’objectif est de permettre à tes managers d’utiliser le connecteur - typiquement l’instance qui se connecte à la base de données afin que tu puisses exécuter tes requêtes SQL.

Je vais détailler un peu …

L’héritage est présent dans la déclaration de ton manager class DaoCoureur(AbstractDao):, ça permet de dire à Python Ma nouvelle classe DaoCoureur hérite de AbstractDao. Dit autrement : tout ce que sait AbstractDao, ma classe DaoCoureur le sait aussi.

A l’intérieur de ta classe DaoCoureur, tu as quatre méthodes : create, find_all, find_by_id et update. Ce sont des méthodes te permettant d’ajouter, récupérer et mettre à jour tes objets (ces dernières instanciés par ta classe métier Coureur). Chacune des méthodes emprunte à la classe abstraite, l’instance de connexion self.connection. Y’a un léger raccourci, puis-ce qu’ici la méthode va directement récupérer le curseur. Le curseur c’est l’outil qui gère la pagination d’une requête - alors tout dépend du connecteur mais il va par exemple t’afficher les 10 premières entrées trouvées et tu pourras alors rappeler le curseur pour voir les 10 suivantes (ainsi de suite jusqu’à la fin). Après avec MySQL, ça ne m’étonnerai pas que ce soit une seule entrée …

En résumé :

  • Classe Abstraite : elle a besoin d’un héritage pour être utilisée. Sauf si elle dispose de méthodes statiques (en Python on ajoute le décorateur @staticmethod) ; pour le cas de AbstractDao c’est possible que ce soit le cas, ça permet de lui passer la connexion à la BDD.

  • Classe manager : c’est le rôle donnée à une classe DAO, ici DaoCoureur. Elle doit traiter avec la base de données afin de synchroniser les données avec les instances de classes métiers.

  • Classe métier : elle fabrique des d’objets spécifiques répondant aux différentes fonctionnalités de l’application (admin, utilisateurs, produits, coureurs etc).

  • Héritage : nom donné au comportement d’une classe mère qui donne accès à ses attributs et ses méthodes à une ou plusieurs classes filles.

Je connais la notion d’héritage, cependant je ne comprends pas d’où sort cette classe abstraite, ce qu’il y a dedans et pourquoi elle est nécessaire :)

Par ailleurs, dans une classe comme celle-ci (DaoCoureur), c’est ici qu’on devrait définir des méthodes qui font appel à des requêtes SQL, non?

Je parle par exemple des queryByEmail que tu évoquais

Donc si je cherche à adapter cela au morceau de code que j’ai envoyé précédemment, ce serait une fonction comme celle qui vient?

def find_by_id(self, id):
    return (SELECT * FROM.....)

Je connais la notion d’héritage, cependant je ne comprends pas d’où sort cette classe abstraite, ce qu’il y a dedans et pourquoi elle est nécessaire :)

Alexouu

Il faut créer cette classe abstraite. Comme je l’ai expliqué dans ma précédente réponse, elle contient la connexion à la base de données. La logique est d’avoir une seule connexion pour tous les managers (d’où l’héritage).

Après une recherche sur Google, je suis tombé sur PySimpleDb, il semble que ton prof l’importe pour utiliser AbstractDao. Tu peux gagner un peu de temps là-dessus.

class AbstractDao(object):
    """
    A simple base class for creating data access objects.

    Each new DAO can simply inheit from this class and define its own set of
    queries.  A function is automatically generated for every query that is
    defined.

      Ex.
        class MyObjectDao(AbstractDao):
            def __init__(self, db):
                self.queries = {
                    'get': {
                        'sql': 'SELECT * FROM mytable WHERE id = :id',
                        'execType': 'queryForObject',
                        'resultType': MyObject
                    }
                }
                AbstractDao.__init__(self, db, queries)

        dao = MyObjectDao(db)
        obj = dao.get(1)

    As you can see from the example you define your queries inside of the
    __init__ function and then pass them to the super class's __init__ function.

    This will then automatically create a function for each query defined.
    """
    def __init__(self, db, queries):
        """
        Initializes an interal Sql object which is used to query the database.

        In addition, the following self.queries options are defined...

         defauktKeyParam  - the default column name used as the key of the map.

                execType  - the way that this message should be called.  This
                            corresponds to the functions that are defined by
                            the Sql object.  The following choices are
                            available...

                             queryForMap
                             queryForObject
                             queryForList
                             queryForScalar
                             insert
                             update
                             delete

                            See the Sql object for more information on how each
                            of these functions behaves.
        """
        self.sql = Sql(db, queries)
        for query in queries.values():
            if not query.has_key('execType'):
                if query.has_key('rowMapper'):
                    query['execType'] = query['rowMapper'].DEFAULT_EXEC_TYPE
                else:
                    query['execType'] = 'update'

    def __getattr__(self, attr):
        """
        Creates a dynamic function for each defined query.

        One dynamic function is created for each query and has the same name
        as the key in the queries dictionary.
        """
        if attr in self.sql.queries:
            val = self.sql.queries[attr]

            if val.has_key('execType'):
                execType = val['execType']
                execFunc = getattr(self.sql, execType)

                if execType in ('queryForMap',):
                    keyParam = None
                    if val.has_key('defaultKeyParam'):
                        keyParam = val['defaultKeyParam']
                    def callable(keyParam=keyParam, **kwargs):
                        return execFunc(attr, keyParam=keyParam, **kwargs)

                elif execType in ('queryForObject', 'queryForList'):
                    def callable(**kwargs):
                        return execFunc(attr, **kwargs)

                elif execType in ('update', 'batch'):
                    def callable(obj=None):
                        return execFunc(attr, obj)

                else:
                    raise TypeError("Invalid Query Type %s" % execType)

                return callable
            else:
                raise AttributeError("Missing method %s called." % attr)

D’après les commentaires, tu as même des fonctions pour t’aider à construire tes méthodes.

Par ailleurs, dans une classe comme celle-ci (DaoCoureur), c’est ici qu’on devrait définir des méthodes qui font appel à des requêtes SQL, non?

Alexouu

C’est le cas si tu regardes bien dans tes méthodes create et find_all. Je comprends même pas pourquoi tu poses la question. Y’a deux requêtes : un INSERT INTO (ligne 12) et un SELECT (ligne 36). Ton prof t’a laissé le soin de continuer find_by_id (raise NotImplementedError).

Je parle par exemple des queryByEmail que tu évoquais

Donc si je cherche à adapter cela au morceau de code que j’ai envoyé précédemment, ce serait une fonction comme celle qui vient?

def find_by_id(self, id):
    return (SELECT * FROM.....)

Alexouu

Hé bien non, au mieux tu retournes une chaîne de caractères à ton contrôleur (si tu mets les guillemets). Il faut envoyer ta requête SQL via le connecteur MySQL.

with self.connection.cursor() as cur:
  cur.execute("SELECT * FROM ...")

Puis boucler sur les éléments retournés en instanciant les classes métiers.

Je réponds à ton commentaire sur le code source (je ne l’avais pas remarqué) :

A votre avis pourquoi on fait pas select * from coureurs ?

C’est pour être sûr du positionnement des champs. Ligne 39, y’a une instanciation de la classe métier avec le tableau item. Après c’est pas forcément ultra propre, le mieux serait d’utiliser un tableau associatif. Même si bon, je comprends que l’on puisse avoir la flemme … :D

+0 -0

Salut Yarflam,

merci de répondre à toutes mes questions même si je me rends compte après coup que certaines ne sont pas très pertinentes en effet !

Mais j’en ai encore plein d’autres ne t’en fais pas :)

1) Dans la classe AbstractDao (voir ci-dessous), il y a 5 méthodes définies mais dois-je en rajouter d’autres?

class AbstractDao:
    """Classe abstraite dont les DAO doivent hériter. Permet de gérer simplement la connection, et d'avoir des noms
    méthodes de base des DAO identique. Permet une meilleure lisibilité du code"""

    connection = get_connection()

    @abstractmethod
    def find_by_id(self, id):
        """Va chercher une élément de la base grâce à son id et retourne l'objet python associé"""
        return NotImplementedError

    @abstractmethod
    def find_all(self):
        """Retourne tous les éléments d'une table sous forme de liste d'objets python"""
        return NotImplementedError

    @abstractmethod
    def update(self, business_object):
        """Met à jour la ligne en base de donnée associé à l'objet métier en paramètre"""
        return NotImplementedError

    @abstractmethod
    def create(self, business_object):
        """Insère une ligne en base avec l'objet en paramètre. Retourne l'objet mise à jour avec son id de la base"""
        return NotImplementedError

    @abstractmethod
    def delete(self, business_object):
        """Supprime la ligne en base représentant l'objet en paramètre"""
        return NotImplementedError

Par ce que par exemple à la ligne 8 c’est find_by_id mais ça pourrait tout aussi bien être find_by_nom_prenom, etc.. ?

2) Maintenant si on se place dans le cas des classes DAO, qui j’ai bien compris héritent de la classe Abstract, et si je reprends une de leur méthode find_by_id:

    def find_by_id(self, id):
        """
        :return : le club d'id donné
        :param id:
        :return:
        """
        with self.connection.cursor() as cur:
            cur.execute(
                "select id_coureur, nom, prenom, annee_naissance, sexe, adresse, mail, telephone, num_licence, association,id_club from coureur where id_coureur=self.id"
            )
        raise NotImplementedError

J’aimerais être sûr que le coup du =self.id (ligne 9) fonctionne ici.

Puis comment gérer le cas où l’id que j’entre n’existe pas? C’est à cela que correspond l’exception "raise NotImplementedError"?

3)Si je considère maintenant la méthode DELETE des classes DAO.

Mettons que je veuille DELETE un coureur par son id, je sais écrire la requête SQL associée mais je comprends pas si je dois mettre l’id en argument de la méthode DELETE. Et aussi, on pourrait vouloir supprimer un coureur en le recherchant également par son mail, du coup il faudrait faire plusieurs requêtes (1 pour l’id et 1 pour le mail) ou alors on écrit "delete from Coureur where id=’X' or mail=’Y' "?

Je sais que j’ai de plus en plus de questions alors encore une fois merci de ton aide Yarflam !

Alexouu :)

1) Dans la classe AbstractDao (voir ci-dessous), il y a 5 méthodes définies mais dois-je en rajouter d’autres?

Par ce que par exemple à la ligne 8 c’est find_by_id mais ça pourrait tout aussi bien être find_by_nom_prenom, etc.. ?

Alexouu

C’est ton prof qui t’as passé ce bout de code ? Techniquement, on est plus proche de l’ORM que de la DAO. En principe AbstractDao n’a pas besoin d’avoir toutes ces méthodes. Il suffit de regarder le code source de PySimpleDb pour s’en rendre compte, je te conseille de le dézipper pour lire le code source (./src/pysimpledb/sql.py).

Après comme je te l’ai expliqué plus tôt, tu peux utiliser une méthode magique pour récupérer tous les queryByX (getattr - en savoir plus) mais tout ça se met dans une classe manager qui hérite de AbstractDao (voir un exemple de classe technique / DAO).

2) Maintenant si on se place dans le cas des classes DAO, qui j’ai bien compris héritent de la classe Abstract, et si je reprends une de leur méthode find_by_id: J’aimerais être sûr que le coup du =self.id (ligne 9) fonctionne ici.

Alexouu

L’héritage permet d’utiliser le patron de conception de la classe mère et de la classe fille, donc oui, une instanciation est possible et ça donne accès aux méthodes et propriétés des deux classes via le self.

Puis comment gérer le cas où l’id que j’entre n’existe pas? C’est à cela que correspond l’exception "raise NotImplementedError"?

Alexouu

Non … raise NotImplementedError c’est pour prévenir le développeur que la méthode n’a pas encore été implémenté / développé. Si l’ID n’existe pas, tu recevras un tableau vide - ton curseur ne retournera rien.

3)Si je considère maintenant la méthode DELETE des classes DAO.

Mettons que je veuille DELETE un coureur par son id, je sais écrire la requête SQL associée mais je comprends pas si je dois mettre l’id en argument de la méthode DELETE. Et aussi, on pourrait vouloir supprimer un coureur en le recherchant également par son mail, du coup il faudrait faire plusieurs requêtes (1 pour l’id et 1 pour le mail) ou alors on écrit "delete from Coureur where id=’X' or mail=’Y' "?

Alexouu

Je pense qu’il faut éviter de se compliquer la vie. Le mieux c’est de prendre l’habitude d’utiliser systématiquement l’ID pour supprimer un élément. On sait que c’est ciblé / unique, y’a moins de risque que ça déborde sur des enregistrements que l’on avait pas considéré au départ. A cas échéant, si vraiment nous devons supprimer des quantités d’enregistrement dans ce cas-là on rajoute une méthode pour un champ spécifique. Mais tu ne fais pas encore de la Big Data ?! :) donc ça va.

Yes c’est mon prof qui a fournit le bout de code d’AbstractDao. Je vais aller voir le code source dont tu parles du coup.

Ok pour le cou^p de l’id qui n’existe pas il faut juste que je fasse en sorte d’afficher un message d’erreur si c’est le cas.

Pour le DELETE: Je comprends pourquoi tu dis que je me complique la vie, car l’ID est unique pour chaque coureur. Mais j’essaie juste de me placer dans le cas où je voudrais delete "Antoine Dupont" mais que j’ai pas forcément son ID sous la main (Oui il peut y avoir plusieurs Antoine Dupont, c’est pour cela que j’avais pris l’exemple de l’adresse mail à la place du nom_prenom

Mais j’essaie juste de me placer dans le cas où je voudrais delete "Antoine Dupont" mais que j’ai pas forcément son ID sous la main

Tu l’as forcément pour mettre à jour tes données. Ton application va afficher une liste ou un profil utilisateur avec l'objet métier de ton coureur. Tu peux y glisser l’ID dans ton bouton de suppression.

Oui oui bien sûr que je peux avoir accès à l’ID à partir d’un nom :)

Mais de manière générale, j’ai du mal à comprendre l’intuition derrière ces méthodes de classes.

Je me répète, ET JE N’AI PAS ENCORE LU TOUT CE QUI EST getattr et autres choses auxquelles tu as fait référence, mais voilà, si je voulais récupérer un ID à partir d’un nom, tel que je vois les choses actuellement il faudrait faire:

def find_by_name(self, name)

Mais avec ce raisonnement-là j’implémente une nouvelle méthode par requête SQL, ce n’est pas possible…

Oui oui bien sûr que je peux avoir accès à l’ID à partir d’un nom :)

Mais de manière générale, j’ai du mal à comprendre l’intuition derrière ces méthodes de classes.

Je me répète, ET JE N’AI PAS ENCORE LU TOUT CE QUI EST getattr et autres choses auxquelles tu as fait référence, mais voilà, si je voulais récupérer un ID à partir d’un nom, tel que je vois les choses actuellement il faudrait faire:

def find_by_name(self, name)

Mais avec ce raisonnement-là j’implémente une nouvelle méthode par requête SQL, ce n’est pas possible…

Alexouu

Je pense que tu as mal compris la dernière suggestion de @Yarflam :)

En gros, il te suggère d’avoir systématiquement sous la main (même si il n’est pas afficher) l’ID du coureur
Ainsi inutile de faire X méthode, 1 seul suffit : celle qui delete (c’est pareil pour update d’ailleurs) via l’ID

Merci Angelo. Ça va même plus loin que simplement garder l’id : l’objet métier du coureur doit être transporté dans toutes les vues ; c’est le comportement attendu d’un flux de données objets au sein d’une application. Ce qui veut dire que si on a accès au nom du coureur, on a également accès à son ID et à tous ses autres attributs.

Connectez-vous pour pouvoir poster un message.
Connexion

Pas encore membre ?

Créez un compte en une minute pour profiter pleinement de toutes les fonctionnalités de Zeste de Savoir. Ici, tout est gratuit et sans publicité.
Créer un compte