Derniers messages sur Zeste de Savoirhttps://zestedesavoir.com/forums/2024-03-27T16:45:22+01:00Les derniers messages parus sur le forum de Zeste de Savoir.organisation projet python, message #2544272024-03-27T16:45:22+01:00adri1/@adri1https://zestedesavoir.com/forums/sujet/17384/organisation-projet-python/?page=1#p254427<blockquote>
<p>J’imagine que par ce biais, Pycharm met le répertoire src dans le path de test ? Est-ce d’ailleurs cela que fait tox, l’outil que tu proposes <a href="/@adri1" rel="nofollow" class="ping ping-link">@<span class="ping-username">adri1</span></a> ?</p>
</blockquote>
<p>Je ne sais pas ce que Pycharm appelle le "path de test". <code>tox</code> est un outil en ligne de commande qui permet de gérer des environnement virtuels et faire tourner les tests (typiquement en appelant <code>pytest</code>). Je ne sais pas du tout ce que fait Pycharm donc je ne sais pas comment ça compare.</p>
<blockquote>
<p>Sinon comment vous organisez le pyproject.toml ? Est-il vraiment utile dans le sens ou je ne compte pas faire un package global mais plutôt une application que l’on exécute ?</p>
</blockquote>
<p><code>pyproject.toml</code> est l’endroit canonique pour définir un paquet Python, y compris si le but est de l’utiliser comme un outil en ligne de commande. Tu peux y définir des point d’entrées dans <code>project.scripts</code> (chaque point d’entrée est tout bêtement la fonction exécutée par l’outil en ligne de commande), mais aussi les dépendances de ton paquet, ainsi que des configurations pour des outils de développement (comme mypy, ruff, etc). <a href="https://packaging.python.org/en/latest/guides/writing-pyproject-toml/">La documentation de la PyPA</a> est très bien. <a href="https://zestedesavoir.com/forums/sujet/16349/wct-assistant-pour-larborescence-des-ordinateurs/?page=1#p244031">Voir ici pour un exemple minimal</a>, probablement assez proche de ce que tu veux faire.</p>organisation projet python, message #2544262024-03-27T15:59:13+01:00diony/@dionyhttps://zestedesavoir.com/forums/sujet/17384/organisation-projet-python/?page=1#p254426<p>Merci pour vos réponses, ça m’a bien aidé et désormais mon problème est résolu !</p>
<p>Alors du coup j’ai refait la structure du programme identique à l’exemple d'<a href="/@entwanne" rel="nofollow" class="ping ping-link">@<span class="ping-username">entwanne</span></a> afin de pouvoir faire différents tests et me rendre compte du fonctionnement déjà depuis la ligne de commande. Effectivement, mon problème était que j’avais tendance à exécuter le script test depuis le test directement (ou depuis Pycharm mais qui n’exécutais que le fichier.</p>
<p>Maintenant, j’ai adapté mon projet de façon à ce qu’il soit semblable à celui d'<a href="/@adri1" rel="nofollow" class="ping ping-link">@<span class="ping-username">adri1</span></a>. Tout fonctionne.</p>
<div class="custom-block custom-block-information"><div class="custom-block-body"><p>Il faut noter aussi que j’ai adapté la configuration dans Pycharm en ajoutant un marquage comme quoi le répertoire <em>test </em>est un répertoire <em>test </em>et que le répertoire <em>src </em>est le répertoire <em>root</em>. J’imagine que par ce biais, Pycharm met le répertoire src dans le path de test ? Est-ce d’ailleurs cela que fait tox, l’outil que tu proposes <a href="/@adri1" rel="nofollow" class="ping ping-link">@<span class="ping-username">adri1</span></a> ? </p></div></div>
<hr>
<p>Sinon comment vous organisez le pyproject.toml ? Est-il vraiment utile dans le sens ou je ne compte pas faire un package global mais plutôt une application que l’on exécute ?</p>organisation projet python, message #2544212024-03-27T08:18:52+01:00entwanne/@entwannehttps://zestedesavoir.com/forums/sujet/17384/organisation-projet-python/?page=1#p254421<figure><blockquote>
<p>L’organisation de ton <code>project</code> me parait un peu étrange. Si <code>project/</code> est un module Python lui-même avec <code>main</code>, <code>algorithms</code> et <code>test</code> qui sont des sous-modules, alors <code>from ..algorithms import algo</code> depuis <code>test_algo.py</code> devrait fonctionner.</p>
</blockquote><figcaption><a href="https://zestedesavoir.com/forums/sujet/17384/organisation-projet-python/?page=1#p254419">adri1</a></figcaption></figure>
<p>Mais suivant le répertoire où l’on se trouve quand on exécute le programme / les tests et celui dans lequel se trouvent les fichiers, le paquet courant peut être trouvé ou non.</p>
<p>La solution pour que le paquet soit systématiquement trouvé est soit de toujours se placer dans le répertoire parent au projet pour exécuter des commandes et d’utiliser <code>python -m</code>, soit d’installer le projet (<code>pip install -e .</code> exécuté une fois dans l’environnement virtuel, ensuite le paquet est toujours trouvé quand l’env est activé).</p>
<p>Mais oui, tu ne devrais jamais avoir à trifouiller <code>PYTHONPATH</code> pour ça.</p>
<hr>
<p>Petite illustration avec la hiérarchie suivante :</p>
<div class="hljs-code-div hljs-code-text"><div class="hljs-line-numbers"><span data-count="1"></span><span data-count="2"></span><span data-count="3"></span><span data-count="4"></span><span data-count="5"></span><span data-count="6"></span><span data-count="7"></span><span data-count="8"></span></div><pre><code class="hljs language-text">.
├── pyproject.toml
├── project
│ ├── addition.py
│ └── __init__.py
└── tests
├── __init__.py
└── test_addition.py
</code></pre></div>
<p>dont les fichiers contiennent :</p>
<figure><div class="hljs-code-div hljs-code-python"><div class="hljs-line-numbers"><span data-count="1"></span><span data-count="2"></span></div><pre><code class="hljs language-python"><span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">addition</span>(<span class="hljs-params">a, b</span>):</span>
<span class="hljs-keyword">return</span> a + b
</code></pre></div><figcaption><code>project/addition.py</code></figcaption></figure>
<figure><div class="hljs-code-div hljs-code-python"><div class="hljs-line-numbers"><span data-count="1"></span><span data-count="2"></span><span data-count="3"></span><span data-count="4"></span><span data-count="5"></span></div><pre><code class="hljs language-python"><span class="hljs-keyword">from</span> project.addition <span class="hljs-keyword">import</span> addition
<span class="hljs-keyword">assert</span> addition(<span class="hljs-number">3</span>, <span class="hljs-number">5</span>) == <span class="hljs-number">8</span>
<span class="hljs-built_in">print</span>(<span class="hljs-string">'passed'</span>)
</code></pre></div><figcaption><code>tests/test_addition.py</code></figcaption></figure>
<p>Les fichiers <code>__init__.py</code> et <code>pyproject.toml</code> sont vides.</p>
<p>Si je me place dans le répertoire parent au projet (répertoire <code>.</code> dans la hiérarchie) et exécute <code>python tests/test_addition.py</code>, j’obtiens une erreur parce que le paquet <code>project</code> n’est pas trouvé.</p>
<div class="hljs-code-div hljs-code-sh"><div class="hljs-line-numbers"><span data-count="1"></span><span data-count="2"></span><span data-count="3"></span><span data-count="4"></span><span data-count="5"></span></div><pre><code class="hljs language-sh">~ % python tests/test_addition.py
Traceback (most recent call last):
File <span class="hljs-string">"/tmp/test/tests/test_addition.py"</span>, line 1, <span class="hljs-keyword">in</span> <module>
from project.addition import addition
ModuleNotFoundError: No module named <span class="hljs-string">'project'</span>
</code></pre></div>
<p>Si j’utilise <code>python -m</code> suivi du module de tests, le répertoire courant est automatiquement ajouté aux chemins donc ça marche.</p>
<div class="hljs-code-div hljs-code-sh"><div class="hljs-line-numbers"><span data-count="1"></span><span data-count="2"></span></div><pre><code class="hljs language-sh">~ % python -m tests.test_addition
passed
</code></pre></div>
<p>Si je me place dans le répertoire <code>tests</code> ça ne fonctionne plus.</p>
<div class="hljs-code-div hljs-code-sh"><div class="hljs-line-numbers"><span data-count="1"></span><span data-count="2"></span><span data-count="3"></span><span data-count="4"></span><span data-count="5"></span><span data-count="6"></span><span data-count="7"></span><span data-count="8"></span></div><pre><code class="hljs language-sh">~ % <span class="hljs-built_in">cd</span> tests
~/tests % python -m test_addition
Traceback (most recent call last):
File <span class="hljs-string">"<frozen runpy>"</span>, line 198, <span class="hljs-keyword">in</span> _run_module_as_main
File <span class="hljs-string">"<frozen runpy>"</span>, line 88, <span class="hljs-keyword">in</span> _run_code
File <span class="hljs-string">"/tmp/test/tests/test_addition.py"</span>, line 1, <span class="hljs-keyword">in</span> <module>
from project.addition import addition
ModuleNotFoundError: No module named <span class="hljs-string">'project'</span>
</code></pre></div>
<p>Si j’utilise un environnement virtuel et installe mon projet (le fichier <code>pyproject.toml</code> vide suffit pour rendre le projet installable).</p>
<div class="hljs-code-div hljs-code-sh"><div class="hljs-line-numbers"><span data-count="1"></span><span data-count="2"></span><span data-count="3"></span><span data-count="4"></span><span data-count="5"></span></div><pre><code class="hljs language-sh">~/tests % <span class="hljs-built_in">cd</span> ..
~ % python -m venv env
~ % <span class="hljs-built_in">source</span> env/bin/activate
(env) ~ % pip install -e .
...
</code></pre></div>
<p>Alors toutes les configurations précédentes fonctionnent correctement.</p>
<div class="hljs-code-div hljs-code-sh"><div class="hljs-line-numbers"><span data-count="1"></span><span data-count="2"></span><span data-count="3"></span><span data-count="4"></span><span data-count="5"></span><span data-count="6"></span><span data-count="7"></span><span data-count="8"></span><span data-count="9"></span></div><pre><code class="hljs language-sh">(env) ~ % python tests/test_addition.py
passed
(env) ~ % python -m tests.test_addition
passed
(env) ~ % <span class="hljs-built_in">cd</span> tests
(env) ~/tests % python test_addition.py
passed
(env) ~/tests % python -m test_addition
passed
</code></pre></div>organisation projet python, message #2544192024-03-27T00:31:54+01:00adri1/@adri1https://zestedesavoir.com/forums/sujet/17384/organisation-projet-python/?page=1#p254419<p>Salut,</p>
<p>L’organisation de ton <code>project</code> me parait un peu étrange. Si <code>project/</code> est un module Python lui-même avec <code>main</code>, <code>algorithms</code> et <code>test</code> qui sont des sous-modules, alors <code>from ..algorithms import algo</code> depuis <code>test_algo.py</code> devrait fonctionner. Cela dit, ce serait une organisation peu conventionnelle. En principe, un projet Python s’organise de la façon suivante :</p>
<div class="hljs-code-div hljs-code-text"><div class="hljs-line-numbers"><span data-count="1"></span><span data-count="2"></span><span data-count="3"></span><span data-count="4"></span><span data-count="5"></span><span data-count="6"></span><span data-count="7"></span><span data-count="8"></span><span data-count="9"></span><span data-count="10"></span><span data-count="11"></span><span data-count="12"></span><span data-count="13"></span><span data-count="14"></span><span data-count="15"></span><span data-count="16"></span><span data-count="17"></span><span data-count="18"></span><span data-count="19"></span><span data-count="20"></span><span data-count="21"></span></div><pre><code class="hljs language-text">.
├── pyproject.toml
├── src
│ ├── module1
│ │ ├── __init__.py
│ │ ├── submodule
│ │ | └── ...
│ │ └── some_module.py
│ ├── module2
│ │ ├── __init__.py
│ │ ├── __main__.py
│ │ └── other_module.py
│ └── some_tests
│ ├── test_mod1.py
│ ├── test_mod2.py
│ └── test_mod3.py
├── README.md
├── test
│ ├── test_something.py
│ └── test_something_else.py
└── tox.ini
</code></pre></div>
<p>C’est ensuite <code>pyproject.toml</code> qui décrit comment les modules sont organisés (e.g. via <code>tool.setuptools.packages.find</code>). <code>module1</code> et <code>module2</code> sont deux modules différents (distribués au sein du même package Python), importer l’un depuis l’autre sera via un import absolu <code>import module1</code>. Utiliser un outil comme <code>tox</code> permet d’exécuter automatiquement la suite de tests dans un environnement virtuel où le package est installé. Dans les tests, les modules à tester sont importés comme s’il s’agissait de paquets externes, par exemple avec <code>import algorithms</code>.</p>
<hr>
<blockquote>
<p>La meilleure solution que j’utilise, c’est d’ajouter le path du dossier <code>algorithms</code> à ta variable d’environnement <code>PYTHONPATH</code>.</p>
</blockquote>
<p>Non, c’est la pire solution possible. <code>PYTHONPATH</code> est là pour pouvoir gérer <a href="https://docs.python.org/3/using/cmdline.html#envvar-PYTHONPATH">des besoins relativement exotiques</a> de déploiement sur des environnements non-standards (je n’en ai <em>jamais</em> eu le besoin!), c’est pas le truc qu’on utilise en routine et encore moins pour un truc aussi mondain que gérer son environnement de développement. </p>organisation projet python, message #2544152024-03-26T16:30:05+01:00Melcore/@Melcorehttps://zestedesavoir.com/forums/sujet/17384/organisation-projet-python/?page=1#p254415<p>Bonjour, j’ai été confronté à ce problème à chaque fois que je faisais des tests dans un projet, c’est assez classique, j’ai l’impression.</p>
<p>La meilleure solution que j’utilise, c’est d’ajouter le path du dossier <code>algorithms</code> à ta variable d’environnement <code>PYTHONPATH</code>.</p>organisation projet python, message #2544142024-03-26T16:17:48+01:00diony/@dionyhttps://zestedesavoir.com/forums/sujet/17384/organisation-projet-python/?page=1#p254414<p>Bonjour tout le monde,</p>
<p>Je viens demander de l’aide, car ça fait quelques heures que je suis sur un problème qui je pense aurait dû être trivial. A noter que j’ai fait pas mal de recherches sur le sujet, sur Stack Overflow, ou sur le forum de ZdS (dans lequel j’ai trouvé un sujet un peu annexe). Je me suis aussi aidé de l’article sur les modules et packages en Python.</p>
<p>Bref, rien n’y fait, je n’arrive pas à faire un import d’un package depuis un dossier test. <img src="/static/smileys/svg/triste.svg" alt=":(" class="smiley"> </p>
<p>Voici mon répertoire de projet :</p>
<div class="hljs-code-div hljs-code-text"><div class="hljs-line-numbers"><span data-count="1"></span><span data-count="2"></span><span data-count="3"></span><span data-count="4"></span><span data-count="5"></span><span data-count="6"></span><span data-count="7"></span><span data-count="8"></span><span data-count="9"></span><span data-count="10"></span><span data-count="11"></span><span data-count="12"></span></div><pre><code class="hljs language-text">project/
│
├── main.py
├── __init__.py
│
├── algorithms/
│ ├── __init__.py
│ └── algo.py
│
├── test/
│ ├── __init__.py
│ └── test_algo.py
</code></pre></div>
<p>J’aimerais faire en sorte de pouvoir importer algo.py depuis test_algo afin de pouvoir exécuter les tests. Donc j’ai testé plusieurs méthodes :</p>
<div class="custom-block custom-block-neutral"><div class="custom-block-heading">Ce que j’ai testé</div><div class="custom-block-body"><p>La méthode intuitive (dans test_algo.py) :</p><div class="hljs-code-div hljs-code-py"><pre><code class="hljs language-py"><span class="hljs-keyword">from</span> ..algorithms <span class="hljs-keyword">import</span> algo
</code></pre></div><p>Ensuite, j’ai cru comprendre qu’on pouvait pas faire de chemin relatif en dehors du package/sous package
Donc en modifiant project/algorithms/<strong>init</strong>.py</p><div class="hljs-code-div hljs-code-py"><pre><code class="hljs language-py"><span class="hljs-keyword">import</span> algo
</code></pre></div><p>et project/<strong>init</strong>.py</p><div class="hljs-code-div hljs-code-py"><div class="hljs-line-numbers"><span data-count="1"></span><span data-count="2"></span></div><pre><code class="hljs language-py"><span class="hljs-keyword">import</span> test
<span class="hljs-keyword">import</span> algorithms
</code></pre></div><p>et dans project/test/test_algo je fais</p><div class="hljs-code-div hljs-code-py"><pre><code class="hljs language-py"><span class="hljs-keyword">import</span> algorithms
</code></pre></div></div></div>
<p>Bref, rien ne fonctionne.</p>
<ul>
<li>Sauriez-vous ou je me suis trompé ?</li>
<li>Pensez-vous qu’une organisation de projet de la sorte est utile ou vaut-il mieux tout mettre dans le même répertoire ? (ça simplifierait effectivement les imports)</li>
</ul>
<p>Merci beaucoup <img src="/static/smileys/svg/hihi.svg" alt="^^" class="smiley"> </p>Programmation orientée objet sous Python, message #2102222019-10-23T18:17:34+02:00anonyme/@anonymehttps://zestedesavoir.com/forums/sujet/13085/programmation-orientee-objet-sous-python/?page=1#p210222<p>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. </p>Programmation orientée objet sous Python, message #2102182019-10-23T17:26:10+02:00DonKnacki/@DonKnackihttps://zestedesavoir.com/forums/sujet/13085/programmation-orientee-objet-sous-python/?page=1#p210218<figure><blockquote>
<p>Oui oui bien sûr que je peux avoir accès à l’ID à partir d’un nom <img src="/static/smileys/smile.png" alt=":)" class="smiley"></p>
<p>Mais de manière générale, j’ai du mal à comprendre l’intuition derrière ces méthodes de classes.</p>
<p>Je me répète, ET JE N’AI PAS ENCORE LU TOUT CE QUI EST <strong>getattr</strong> 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:</p>
<div class="hljs-code-div"><div class="hljs-line-numbers"><span></span></div><pre><code class="hljs language-python"><span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">find_by_name</span><span class="hljs-params">(self, name)</span>
</span></code></pre></div>
<p>Mais avec ce raisonnement-là j’implémente une nouvelle méthode par requête SQL, ce n’est pas possible…</p>
</blockquote><figcaption><a href="https://zestedesavoir.com/forums/sujet/13085/programmation-orientee-objet-sous-python/?page=1#p209956">Alexouu</a></figcaption></figure>
<p>Je pense que tu as mal compris la dernière suggestion de <a href="/membres/voir/Yarflam/" rel="nofollow" class="ping ping-link">@<span class="ping-username">Yarflam</span></a> <img src="/static/smileys/smile.png" alt=":)" class="smiley"></p>
<p>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<br>
Ainsi inutile de faire X méthode, 1 seul suffit : celle qui delete (c’est pareil pour update d’ailleurs) via l’ID</p>Programmation orientée objet sous Python, message #2099562019-10-17T23:41:47+02:00Alexouu/@Alexouuhttps://zestedesavoir.com/forums/sujet/13085/programmation-orientee-objet-sous-python/?page=1#p209956<p>Oui oui bien sûr que je peux avoir accès à l’ID à partir d’un nom <img src="/static/smileys/smile.png" alt=":)" class="smiley"></p>
<p>Mais de manière générale, j’ai du mal à comprendre l’intuition derrière ces méthodes de classes.</p>
<p>Je me répète, ET JE N’AI PAS ENCORE LU TOUT CE QUI EST <strong>getattr</strong> 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:</p>
<div class="hljs-code-div"><div class="hljs-line-numbers"><span></span></div><pre><code class="hljs language-python"><span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">find_by_name</span><span class="hljs-params">(self, name)</span>
</span></code></pre></div>
<p>Mais avec ce raisonnement-là j’implémente une nouvelle méthode par requête SQL, ce n’est pas possible…</p>Programmation orientée objet sous Python, message #2099552019-10-17T23:29:00+02:00anonyme/@anonymehttps://zestedesavoir.com/forums/sujet/13085/programmation-orientee-objet-sous-python/?page=1#p209955<blockquote>
<p>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</p>
</blockquote>
<p>Tu l’as forcément pour mettre à jour tes données. Ton application va afficher une liste ou un profil utilisateur avec l'<strong>objet métier</strong> de ton coureur. Tu peux y glisser l’ID dans ton bouton de suppression.</p>Programmation orientée objet sous Python, message #2099522019-10-17T23:14:47+02:00Alexouu/@Alexouuhttps://zestedesavoir.com/forums/sujet/13085/programmation-orientee-objet-sous-python/?page=1#p209952<p>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.</p>
<p>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.</p>
<p>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</p>Programmation orientée objet sous Python, message #2099512019-10-17T22:52:41+02:00anonyme/@anonymehttps://zestedesavoir.com/forums/sujet/13085/programmation-orientee-objet-sous-python/?page=1#p209951<figure><blockquote>
<p>1) Dans la classe AbstractDao (voir ci-dessous), il y a 5 méthodes définies mais dois-je en rajouter d’autres?</p>
<p>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.. ?</p>
</blockquote><figcaption><a href="https://zestedesavoir.com/forums/sujet/13085/programmation-orientee-objet-sous-python/?page=1#p209915">Alexouu</a></figcaption></figure>
<p>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).</p>
<p>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 (<a href="https://stackoverflow.com/a/10879860">getattr - en savoir plus</a>) mais tout ça se met dans une classe manager qui hérite de AbstractDao (<a href="https://fr.wikipedia.org/wiki/Objet_d'acc%C3%A8s_aux_donn%C3%A9es">voir un exemple de classe technique / DAO</a>).</p>
<figure><blockquote>
<p>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.</p>
</blockquote><figcaption><a href="https://zestedesavoir.com/forums/sujet/13085/programmation-orientee-objet-sous-python/?page=1#p209915">Alexouu</a></figcaption></figure>
<p>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.</p>
<figure><blockquote>
<p>Puis comment gérer le cas où l’id que j’entre n’existe pas? C’est à cela que correspond l’exception "raise NotImplementedError"?</p>
</blockquote><figcaption><a href="https://zestedesavoir.com/forums/sujet/13085/programmation-orientee-objet-sous-python/?page=1#p209915">Alexouu</a></figcaption></figure>
<p>Non … <code>raise NotImplementedError</code> 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.</p>
<figure><blockquote>
<p>3)Si je considère maintenant la méthode DELETE des classes DAO.</p>
<p>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' "?</p>
</blockquote><figcaption><a href="https://zestedesavoir.com/forums/sujet/13085/programmation-orientee-objet-sous-python/?page=1#p209915">Alexouu</a></figcaption></figure>
<p>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 ?! <img src="/static/smileys/smile.png" alt=":)" class="smiley"> donc ça va.</p>Programmation orientée objet sous Python, message #2099152019-10-17T11:04:50+02:00Alexouu/@Alexouuhttps://zestedesavoir.com/forums/sujet/13085/programmation-orientee-objet-sous-python/?page=1#p209915<p>Salut Yarflam,</p>
<p>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 !</p>
<p>Mais j’en ai encore plein d’autres ne t’en fais pas <img src="/static/smileys/smile.png" alt=":)" class="smiley"></p>
<p>1) Dans la classe AbstractDao (voir ci-dessous), il y a 5 méthodes définies mais dois-je en rajouter d’autres?</p>
<div class="hljs-code-div"><div class="hljs-line-numbers"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></div><pre><code class="hljs language-python"><span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">AbstractDao</span>:</span>
<span class="hljs-string">"""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"""</span>
connection = get_connection()
<span class="hljs-meta"> @abstractmethod</span>
<span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">find_by_id</span><span class="hljs-params">(self, id)</span>:</span>
<span class="hljs-string">"""Va chercher une élément de la base grâce à son id et retourne l'objet python associé"""</span>
<span class="hljs-keyword">return</span> NotImplementedError
<span class="hljs-meta"> @abstractmethod</span>
<span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">find_all</span><span class="hljs-params">(self)</span>:</span>
<span class="hljs-string">"""Retourne tous les éléments d'une table sous forme de liste d'objets python"""</span>
<span class="hljs-keyword">return</span> NotImplementedError
<span class="hljs-meta"> @abstractmethod</span>
<span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">update</span><span class="hljs-params">(self, business_object)</span>:</span>
<span class="hljs-string">"""Met à jour la ligne en base de donnée associé à l'objet métier en paramètre"""</span>
<span class="hljs-keyword">return</span> NotImplementedError
<span class="hljs-meta"> @abstractmethod</span>
<span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">create</span><span class="hljs-params">(self, business_object)</span>:</span>
<span class="hljs-string">"""Insère une ligne en base avec l'objet en paramètre. Retourne l'objet mise à jour avec son id de la base"""</span>
<span class="hljs-keyword">return</span> NotImplementedError
<span class="hljs-meta"> @abstractmethod</span>
<span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">delete</span><span class="hljs-params">(self, business_object)</span>:</span>
<span class="hljs-string">"""Supprime la ligne en base représentant l'objet en paramètre"""</span>
<span class="hljs-keyword">return</span> NotImplementedError
</code></pre></div>
<p>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.. ?</p>
<p>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:</p>
<div class="hljs-code-div"><div class="hljs-line-numbers"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></div><pre><code class="hljs language-python"> <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">find_by_id</span><span class="hljs-params">(self, id)</span>:</span>
<span class="hljs-string">"""
:return : le club d'id donné
:param id:
:return:
"""</span>
<span class="hljs-keyword">with</span> self.connection.cursor() <span class="hljs-keyword">as</span> cur:
cur.execute(
<span class="hljs-string">"select id_coureur, nom, prenom, annee_naissance, sexe, adresse, mail, telephone, num_licence, association,id_club from coureur where id_coureur=self.id"</span>
)
<span class="hljs-keyword">raise</span> NotImplementedError
</code></pre></div>
<p>J’aimerais être sûr que le coup du =self.id (ligne 9) fonctionne ici.</p>
<p>Puis comment gérer le cas où l’id que j’entre n’existe pas? C’est à cela que correspond l’exception "raise NotImplementedError"?</p>
<p>3)Si je considère maintenant la méthode DELETE des classes DAO.</p>
<p>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' "?</p>
<p>Je sais que j’ai de plus en plus de questions alors encore une fois merci de ton aide Yarflam !</p>
<p>Alexouu <img src="/static/smileys/smile.png" alt=":)" class="smiley"></p>Programmation orientée objet sous Python, message #2098272019-10-15T18:57:11+02:00anonyme/@anonymehttps://zestedesavoir.com/forums/sujet/13085/programmation-orientee-objet-sous-python/?page=1#p209827<figure><blockquote>
<p>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 <img src="/static/smileys/smile.png" alt=":)" class="smiley"></p>
<p></p>
</blockquote><figcaption><a href="https://zestedesavoir.com/forums/sujet/13085/programmation-orientee-objet-sous-python/?page=1#p209813">Alexouu</a></figcaption></figure>
<p>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).</p>
<p>Après une recherche sur Google, je suis tombé sur <a href="http://www.mikusa.com/pysimpledb/">PySimpleDb</a>, il semble que ton prof l’importe pour utiliser AbstractDao. Tu peux gagner un peu de temps là-dessus.</p>
<div class="custom-block custom-block-spoiler"><div class="custom-block-body"><div class="hljs-code-div"><div class="hljs-line-numbers"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></div><pre><code class="hljs language-python"><span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">AbstractDao</span><span class="hljs-params">(object)</span>:</span>
<span class="hljs-string">"""
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.
"""</span>
<span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">__init__</span><span class="hljs-params">(self, db, queries)</span>:</span>
<span class="hljs-string">"""
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.
"""</span>
self.sql = Sql(db, queries)
<span class="hljs-keyword">for</span> query <span class="hljs-keyword">in</span> queries.values():
<span class="hljs-keyword">if</span> <span class="hljs-keyword">not</span> query.has_key(<span class="hljs-string">'execType'</span>):
<span class="hljs-keyword">if</span> query.has_key(<span class="hljs-string">'rowMapper'</span>):
query[<span class="hljs-string">'execType'</span>] = query[<span class="hljs-string">'rowMapper'</span>].DEFAULT_EXEC_TYPE
<span class="hljs-keyword">else</span>:
query[<span class="hljs-string">'execType'</span>] = <span class="hljs-string">'update'</span>
<span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">__getattr__</span><span class="hljs-params">(self, attr)</span>:</span>
<span class="hljs-string">"""
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.
"""</span>
<span class="hljs-keyword">if</span> attr <span class="hljs-keyword">in</span> self.sql.queries:
val = self.sql.queries[attr]
<span class="hljs-keyword">if</span> val.has_key(<span class="hljs-string">'execType'</span>):
execType = val[<span class="hljs-string">'execType'</span>]
execFunc = getattr(self.sql, execType)
<span class="hljs-keyword">if</span> execType <span class="hljs-keyword">in</span> (<span class="hljs-string">'queryForMap'</span>,):
keyParam = <span class="hljs-literal">None</span>
<span class="hljs-keyword">if</span> val.has_key(<span class="hljs-string">'defaultKeyParam'</span>):
keyParam = val[<span class="hljs-string">'defaultKeyParam'</span>]
<span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">callable</span><span class="hljs-params">(keyParam=keyParam, **kwargs)</span>:</span>
<span class="hljs-keyword">return</span> execFunc(attr, keyParam=keyParam, **kwargs)
<span class="hljs-keyword">elif</span> execType <span class="hljs-keyword">in</span> (<span class="hljs-string">'queryForObject'</span>, <span class="hljs-string">'queryForList'</span>):
<span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">callable</span><span class="hljs-params">(**kwargs)</span>:</span>
<span class="hljs-keyword">return</span> execFunc(attr, **kwargs)
<span class="hljs-keyword">elif</span> execType <span class="hljs-keyword">in</span> (<span class="hljs-string">'update'</span>, <span class="hljs-string">'batch'</span>):
<span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">callable</span><span class="hljs-params">(obj=None)</span>:</span>
<span class="hljs-keyword">return</span> execFunc(attr, obj)
<span class="hljs-keyword">else</span>:
<span class="hljs-keyword">raise</span> TypeError(<span class="hljs-string">"Invalid Query Type %s"</span> % execType)
<span class="hljs-keyword">return</span> callable
<span class="hljs-keyword">else</span>:
<span class="hljs-keyword">raise</span> AttributeError(<span class="hljs-string">"Missing method %s called."</span> % attr)
</code></pre></div><p>D’après les commentaires, tu as même des fonctions pour t’aider à construire tes méthodes.</p></div></div>
<figure><blockquote>
<p>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?</p>
</blockquote><figcaption><a href="https://zestedesavoir.com/forums/sujet/13085/programmation-orientee-objet-sous-python/?page=1#p209813">Alexouu</a></figcaption></figure>
<p>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 <strong>INSERT INTO</strong> (ligne 12) et un <strong>SELECT</strong> (ligne 36). Ton prof t’a laissé le soin de continuer find_by_id (<code>raise NotImplementedError</code>).</p>
<figure><blockquote>
<p>Je parle par exemple des queryByEmail que tu évoquais</p>
<p>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?</p>
<div class="hljs-code-div"><div class="hljs-line-numbers"><span></span><span></span></div><pre><code class="hljs language-python"><span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">find_by_id</span><span class="hljs-params">(self, id)</span>:</span>
<span class="hljs-keyword">return</span> (SELECT * FROM.....)
</code></pre></div>
<p></p>
</blockquote><figcaption><a href="https://zestedesavoir.com/forums/sujet/13085/programmation-orientee-objet-sous-python/?page=1#p209813">Alexouu</a></figcaption></figure>
<p>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.</p>
<div class="hljs-code-div"><div class="hljs-line-numbers"><span></span><span></span></div><pre><code class="hljs language-python"><span class="hljs-keyword">with</span> self.connection.cursor() <span class="hljs-keyword">as</span> cur:
cur.execute(<span class="hljs-string">"SELECT * FROM ..."</span>)
</code></pre></div>
<p>Puis boucler sur les éléments retournés en instanciant les classes métiers.</p>
<p>Je réponds à ton commentaire sur le code source (je ne l’avais pas remarqué) :</p>
<blockquote>
<p>A votre avis pourquoi on fait pas select * from coureurs ?</p>
</blockquote>
<p>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 … <img src="/static/smileys/heureux.png" alt=":D" class="smiley"></p>Programmation orientée objet sous Python, message #2098132019-10-15T13:46:57+02:00Alexouu/@Alexouuhttps://zestedesavoir.com/forums/sujet/13085/programmation-orientee-objet-sous-python/?page=1#p209813<p>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 <img src="/static/smileys/smile.png" alt=":)" class="smiley"></p>
<p>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?</p>
<p>Je parle par exemple des queryByEmail que tu évoquais</p>
<p>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?</p>
<div class="hljs-code-div"><div class="hljs-line-numbers"><span></span><span></span></div><pre><code class="hljs language-python"><span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">find_by_id</span><span class="hljs-params">(self, id)</span>:</span>
<span class="hljs-keyword">return</span> (SELECT * FROM.....)
</code></pre></div>Programmation orientée objet sous Python, message #2097902019-10-14T20:51:27+02:00anonyme/@anonymehttps://zestedesavoir.com/forums/sujet/13085/programmation-orientee-objet-sous-python/?page=1#p209790<figure><blockquote>
<p>Encore une fois merci de ton temps Yarflam, je comprends petit à petit !</p>
</blockquote><figcaption><a href="https://zestedesavoir.com/forums/sujet/13085/programmation-orientee-objet-sous-python/?page=1#p209786">Alexouu</a></figcaption></figure>
<p>Avec plaisir Alexouu ! <img src="/static/smileys/langue.png" alt=":p" class="smiley"></p>
<figure><blockquote>
<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?</p>
</blockquote><figcaption><a href="https://zestedesavoir.com/forums/sujet/13085/programmation-orientee-objet-sous-python/?page=1#p209786">Alexouu</a></figcaption></figure>
<p>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.</p>
<p>Je vais détailler un peu …</p>
<p>L’héritage est présent dans la déclaration de ton manager <code>class DaoCoureur(AbstractDao):</code>, ça permet de dire à Python <strong>Ma nouvelle classe DaoCoureur hérite de AbstractDao</strong>. Dit autrement : tout ce que sait <strong>AbstractDao</strong>, ma classe <strong>DaoCoureur</strong> le sait aussi.</p>
<p>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 <em>Coureur</em>). Chacune des méthodes emprunte à la classe abstraite, l’instance de connexion <code>self.connection</code>. 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 …</p>
<p>En résumé :</p>
<ul>
<li>
<p>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 <a href="/membres/voir/staticmethod/" rel="nofollow" class="ping ping-link">@<span class="ping-username">staticmethod</span></a>) ; pour le cas de AbstractDao c’est possible que ce soit le cas, ça permet de lui passer la connexion à la BDD.</p>
</li>
<li>
<p>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.</p>
</li>
<li>
<p>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).</p>
</li>
<li>
<p>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.</p>
</li>
</ul>Programmation orientée objet sous Python, message #2097862019-10-14T19:44:59+02:00Alexouu/@Alexouuhttps://zestedesavoir.com/forums/sujet/13085/programmation-orientee-objet-sous-python/?page=1#p209786<p>Encore une fois merci de ton temps Yarflam, je comprends petit à petit !</p>
<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?</p>
<div class="hljs-code-div"><div class="hljs-line-numbers"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></div><pre><code class="hljs language-python"><span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">DaoCoureur</span><span class="hljs-params">(AbstractDao)</span>:</span>
<span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">create</span><span class="hljs-params">(self, coureur)</span>:</span>
<span class="hljs-string">"""
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
"""</span>
cur = self.connection.cursor()
<span class="hljs-keyword">try</span>:
cur.execute(
<span class="hljs-string">"INSERT INTO coureur (nom, prenom, annee_naissance,sexe,adresse,mail,"</span>
<span class="hljs-string">"telephone,num_licence,association,id_club) VALUES (%s, %s, %s, %s, %s, %s, %s, %s, %s, %s) RETURNING id_coureur;"</span>,
(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()[<span class="hljs-number">0</span>]
<span class="hljs-comment"># la transaction est enregistrée en base</span>
self.connection.commit()
<span class="hljs-keyword">except</span>:
<span class="hljs-comment"># la transaction est annulée</span>
self.connection.rollback()
<span class="hljs-keyword">raise</span>
<span class="hljs-keyword">finally</span>:
cur.close()
<span class="hljs-keyword">return</span> coureur
<span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">find_all</span><span class="hljs-params">(self)</span>:</span>
<span class="hljs-string">"""
:return: l'intégralité des coureurs en base
"""</span>
<span class="hljs-keyword">with</span> self.connection.cursor() <span class="hljs-keyword">as</span> cur:
cur.execute(
<span class="hljs-string">"select id_coureur, nom, prenom, annee_naissance, sexe, adresse, mail, telephone, num_licence, association,id_club from coureur"</span>) <span class="hljs-comment"># A votre avis pourquoi on fait pas select * from coureurs ?</span>
<span class="hljs-comment"># on récupère des tuples et les transforme en objects Coureur</span>
result = [Coureur(id_coureur=item[<span class="hljs-number">0</span>], nom=item[<span class="hljs-number">1</span>], prenom=item[<span class="hljs-number">2</span>], annee_naissance=item[<span class="hljs-number">3</span>], sexe=item[<span class="hljs-number">4</span>],
adresse=item[<span class="hljs-number">5</span>],mail=item[<span class="hljs-number">6</span>],telephone=item[<span class="hljs-number">7</span>],num_licence=item[<span class="hljs-number">8</span>],association=item[<span class="hljs-number">9</span>],id_club=item[<span class="hljs-number">10</span>])
<span class="hljs-keyword">for</span> item <span class="hljs-keyword">in</span> cur.fetchall()]
<span class="hljs-keyword">return</span> result
<span class="hljs-string">"""
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.
"""</span>
<span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">find_by_id</span><span class="hljs-params">(self, id)</span>:</span>
<span class="hljs-string">"""
:return : le club d'id donné
:param id:
:return:
"""</span>
<span class="hljs-keyword">raise</span> NotImplementedError
<span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">update</span><span class="hljs-params">(self, business_object)</span>:</span>
<span class="hljs-keyword">raise</span> NotImplementedError
</code></pre></div>
<p>Merci,</p>
<p>ALexouu <img src="/static/smileys/smile.png" alt=":)" class="smiley"></p>Programmation orientée objet sous Python, message #2095772019-10-11T19:25:04+02:00anonyme/@anonymehttps://zestedesavoir.com/forums/sujet/13085/programmation-orientee-objet-sous-python/?page=1#p209577<blockquote>
<p>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 ?</p>
</blockquote>
<p>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.</p>
<blockquote>
<p>Puis au niveau des attributs et des méthodes, ça se passe comment ?</p>
</blockquote>
<p>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 <code>getBestRunnersKm()</code> qui exécutera la requête <code>SELECT * FROM coureurs WHERE moy_vitesse > 32</code>. En retour, tu recevras un tableau d’objets instanciant la classe Coureur avec les données de tes champions.</p>
<blockquote>
<p>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 ?</p>
</blockquote>
<p>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 <a href="https://stackoverflow.com/a/10879860"><strong>getattr</strong></a>, ç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).</p>
<blockquote>
<p>J’admets que c’est encore flou dans ma tête actuellement.</p>
</blockquote>
<p>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 <code>usain = new Coureur('Usain', 'Bolt', 1986, 'Jamaïque')</code>, 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 <code>CoureursManager.insert(usain)</code>. 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).</p>
<p>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 <code>CoureursManager.queryByCourse(127)</code> 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 <code>usain.setPoint(400)</code> et je synchronise l’objet avec la base en appelant de nouveau mon manager <code>CoureursManager.update(usain)</code>.</p>
<p>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 ! <img src="/static/smileys/smile.png" alt=":)" class="smiley"></p>
<p>Schématiquement ça revient à effectuer cet échange :</p>
<p>Contrôleur —(Joueurs de la course <em>127</em> ?)—> CoureursManager.queryByCourse —(<code>SELECT * FROM coureurs WHERE course = 127</code>)—> BDD —(BDD.Objects)—> CoureursManager.queryByCourse —(Array<Coureur>)—> Contrôleur.</p>Programmation orientée objet sous Python, message #2095552019-10-11T00:16:16+02:00Alexouu/@Alexouuhttps://zestedesavoir.com/forums/sujet/13085/programmation-orientee-objet-sous-python/?page=1#p209555<p>Salut Yarflam,</p>
<p>Tout d’abord merci pour ta réponse détaillée ! <img src="/static/smileys/smile.png" alt=":)" class="smiley"></p>
<p>Néanmoins je n’ai pas tout compris, dans le détail comment ça marche une classe manager?</p>
<p>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?</p>
<p>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?</p>
<p>J’admets que c’est encore flou dans ma tête actuellement <img src="/static/smileys/smile.png" alt=":)" class="smiley"></p>
<p>D’autant plus que de l’autre côté, il va rester quoi comme méthodes dans les classes métier? </p>Programmation orientée objet sous Python, message #2095492019-10-10T22:23:34+02:00anonyme/@anonymehttps://zestedesavoir.com/forums/sujet/13085/programmation-orientee-objet-sous-python/?page=1#p209549<p>Bonjour,</p>
<p>C’est bien de rappeler que <strong>DAO</strong> correspond à <strong>D</strong>ata <strong>A</strong>ccess <strong>O</strong>bject. Sur le coup ça me parlait pas, pourtant je l’utilise quotidiennement via une <strong>ORM</strong> (<strong>O</strong>bject <strong>R</strong>elationnal <strong>M</strong>apping). 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.</p>
<p>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. <img src="/static/smileys/clin.png" alt=";)" class="smiley"></p>
<p>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).</p>
<p>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 :</p>
<div class="hljs-code-div"><div class="hljs-line-numbers"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></div><pre><code class="hljs language-js"><span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">UtilisateurManager</span> <span class="hljs-keyword">extends</span> <span class="hljs-title">Connecteur</span> </span>{
insert(item) {
<span class="hljs-comment">/* Création de l'utilisateur */</span>
<span class="hljs-keyword">return</span> <span class="hljs-keyword">this</span>.query(
<span class="hljs-string">'INSERT INTO utilisateur (prenom, nom, email) '</span>+
<span class="hljs-string">'VALUES ("'</span>+item.prenom+<span class="hljs-string">'", "'</span>+item.nom+<span class="hljs-string">'", "'</span>+item.email+<span class="hljs-string">'")'</span>
);
}
queryByEmail(email) {
<span class="hljs-comment">/* Récupération d'un utilisateur via son email */</span>
<span class="hljs-keyword">let</span> items = <span class="hljs-keyword">this</span>.query(
<span class="hljs-string">'SELECT prenom, nom, email FROM utilisateur WHERE email = "'</span>+email+<span class="hljs-string">'" LIMIT 1'</span>
);
<span class="hljs-comment">/* Transcription */</span>
<span class="hljs-keyword">if</span>(items.length == <span class="hljs-number">1</span>) {
<span class="hljs-keyword">return</span> <span class="hljs-keyword">new</span> Utilisateur(items[<span class="hljs-number">0</span>].prenom, items[<span class="hljs-number">0</span>].nom, items[<span class="hljs-number">0</span>].email);
}
<span class="hljs-comment">/* Sans gestion d'erreurs */</span>
<span class="hljs-keyword">return</span> <span class="hljs-keyword">new</span> Utilisateur();
}
}
<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">Utilisateur</span> </span>{
<span class="hljs-keyword">constructor</span> (prenom='', nom='', email='') {
<span class="hljs-keyword">this</span>.prenom = prenom;
<span class="hljs-keyword">this</span>.nom = nom;
<span class="hljs-keyword">this</span>.email = email;
}
}
<span class="hljs-comment">/* Création d'un utilisateur */</span>
<span class="hljs-keyword">let</span> martin = <span class="hljs-keyword">new</span> Utilisateur(<span class="hljs-string">'Martin'</span>, <span class="hljs-string">'GUITARE'</span>, <span class="hljs-string">'mguitare@gmail.com'</span>);
UtilisateurManager.insert(martin);
<span class="hljs-comment">/* Récupération d'un utilisateur */</span>
<span class="hljs-keyword">let</span> test = UtilisateurManager.queryByEmail(<span class="hljs-string">'mguitare@gmail.com'</span>);
<span class="hljs-built_in">console</span>.log(test.prenom); <span class="hljs-comment">// affiche 'Martin'</span>
</code></pre></div>
<p>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 <code>this.query</code>) - 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.</p>
<p>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 <code>martin.email = 'martintin@yahoo.fr';</code> et de passer l’objet dans le manager <code>UtilisateurManager.update(martin);</code> pour que celui-ci exécute la bonne requête.</p>
<p>En espérant que ce soit suffisamment compréhensible. <img src="/static/smileys/smile.png" alt=":)" class="smiley"> 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.</p>Programmation orientée objet sous Python, message #2095362019-10-10T19:37:37+02:00Alexouu/@Alexouuhttps://zestedesavoir.com/forums/sujet/13085/programmation-orientee-objet-sous-python/?page=1#p209536<p>Bonjour à tous,</p>
<p>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.</p>
<p>Voici en quoi doit consister l’application:</p>
<p>Elle doit gérer les temps et les classements de courses d’athlétisme, je m’explique:</p>
<p>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é).</p>
<p>D’autre part cette appli s’adresse également aux "organisateurs" de courses (besoin de se connecter).
Les organisateurs sont les seuls à:</p>
<p>-pouvoir inscrire (le un coureur à une course (c’est mal fait mais un coureur ne peut s’inscrire lui même)</p>
<p>-pouvoir créer/modifier/supprimer une nouvelle course (libellé/date et heure/lieu/distance)</p>
<p>-un organisateur peut créer autant de courses qu’il le souhaite mais ne peut pas toucher à celles des autres.</p>
<p>Ce que l’on me donne:</p>
<p>-Un ficher de quelques courses au format csv (je peux en ajouter ou en supprimer à ma guise)</p>
<p>-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</p>
<p>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).</p>
<p>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…</p>
<p>Si quelqu’un pouvait m’éclairer sur ce sujet,</p>
<p>Merci d’avance,</p>
<p>Alexouu <img src="/static/smileys/smile.png" alt=":)" class="smiley"></p>