Compare commits

...

210 Commits

Author SHA1 Message Date
Fred Pauchet 32517aa7b1 Starting references to Release It by Michael T Nygard 5 months ago
Fred Pauchet ae881a52fe Update notes 7 months ago
Fred Pauchet 17b3d7336a Switch from Svelte to NextJS 9 months ago
Fred Pauchet 8ddd800da6 Describe a simple CI pipeline 9 months ago
Fred Pauchet 71b4c7a7fd Improve Python chapter with introspection 10 months ago
Fred Pauchet a439c57757 Include a part about dictionaries 10 months ago
Fred Pauchet 7aef6eebd7 Siren's call (cats on trees :-)) 10 months ago
Fred Pauchet 0c9a3fc59d Describe a simple tree diagram 11 months ago
Fred Pauchet 450f0cee18 Reorder sections 11 months ago
Fred Pauchet e926ca4373 Change permissions on files 11 months ago
Fred Pauchet c8ec00c7ba Need to fill OData section 11 months ago
Fred Pauchet dc1d8bed30 Fix LaTeX build 11 months ago
Fred Pauchet 14fbb68952 Add links to htmx 1 year ago
Fred Pauchet a5210a8353 Rework sentences in architecture.tex 1 year ago
Fred Pauchet a14e622318 Switch graphics to new environment 1 year ago
Fred Pauchet 52c6916a6b Convert includegraphic to graphic environment 1 year ago
Fred Pauchet a238fd4eed Create a new environment to include strechted graphics 1 year ago
Fred Pauchet 8a839b2d7b Add descriptive links for Git 1 year ago
Fred Pauchet 745291ac99 Quit PDF generation on first encountered error 1 year ago
Fred Pauchet 43590682bc Delete old asciidoc sources 1 year ago
Fred Pauchet 1a97286bb0 Implement color boxes 1 year ago
Fred Pauchet 564c8d2264 Delete ASCIIDOC docker compilation 1 year ago
Fred Pauchet a64032fd48 Changing files permissions (due to ... ?) 1 year ago
Fred Pauchet db8bafa349 Fix docker file to correctly build image 1 year ago
Fred Pauchet af8b7c41b0 Add link to Conventional Commits 1 year ago
Fred Pauchet f6821ff222 Resize images on chapter 2 1 year ago
Fred Pauchet 0578564c37 Resize images to fit page layout 1 year ago
Fred Pauchet 862d2d01f6 Rework introduction 1 year ago
Fred Pauchet e7b66702dc Finalize integration of Roads and Bridges 1 year ago
Fred Pauchet 9847f652a1 Itemize several sentences 1 year ago
Fred Pauchet e2ea706de3 Complete introduction on Python 1 year ago
Fred Pauchet 81da4e3069 Add references to Roads and Bridges 1 year ago
Fred Pauchet de1e298225 About dependencies... 1 year ago
Fred Pauchet 804ada2bea PEP Review 1 year ago
Fred Pauchet 0158ba537b API + Models services 1 year ago
Fred Pauchet a41d71d0a8 Structure Discord GDPR page content 1 year ago
Fred Pauchet 224ed5a0c2 Services... 1 year ago
Fred Pauchet 09a1cd0506 Continuing Alex Krupp's work review 1 year ago
Fred Pauchet dde67920d6 Continue GDPR description from Discord analysis 1 year ago
Fred Pauchet 16f522d5ea Add the construct_change_message description 1 year ago
Fred Pauchet e8547083c5 Copy output file to Nextcloud :p 1 year ago
Fred Pauchet e1c7f7ce46 Annexes, new chapters and typo fixes 1 year ago
Fred Pauchet 31b8c1dc7d Add a note on Fly.io 1 year ago
Fred Pauchet 0c2aeee91e Add appendices 1 year ago
Fred Pauchet 9e8b04ef5e Corrections de texte, ajouts minimes, ... Et Intégration de Calvin & Hobbes ! 1 year ago
Fred Pauchet 8ad6e92bc5 Fix Heroku notes 1 year ago
Fred Pauchet f901628255 Working on Debian/Ubuntu 1 year ago
Fred Pauchet f5ae5cdac4 Work on Debian/Ubuntu deployments methods 1 year ago
Fred Pauchet ad625a1b40 ... to \ldots 1 year ago
Fred Pauchet bc7c7f74d4 Télémétrie et composants 1 year ago
Fred Pauchet 9e0ef60e44 Integration de morceaux du DevOps Handbook 1 year ago
Gregory Trullemans 4f1afe7678 Reformatage de fichier, de code. Je mets de pour des commentaires à l'intérieur des fichiers. 1 year ago
Gregory Trullemans a88f1854a0 Code review and rewriting 1 year ago
Fred Pauchet c15e05349d Writing, again and again 1 year ago
Fred Pauchet 2f4718c002 Rephrase peripheral components 1 year ago
Fred Pauchet 10ec49c302 Add a lot of things :D 1 year ago
Fred Pauchet 5de564b21f Including templates into LaTeX document 1 year ago
Fred Pauchet 6f868de04b Ignore minted module temporary files 1 year ago
Fred Pauchet a23dfec354 Retrieve architecture image from data intensive apps 1 year ago
Fred Pauchet b39fcf9bf7 Move deployment on part 2 1 year ago
Fred Pauchet f995792784 Easter notes on administration 1 year ago
Fred Pauchet 39f05bd5c3 Easter notes for context processors 1 year ago
Fred Pauchet e0b0a862cd Easter notes for authentication 1 year ago
Fred Pauchet 7d81286462 Easter notes on administration ,forms, migrations and models 1 year ago
Fred Pauchet c8f88779ff Complete models.tex based on easter notes 1 year ago
Fred Pauchet 63f2cf23f4 Fix models.tex 1 year ago
Fred Pauchet f577872744 Review Easter holidays notes (part 2) 1 year ago
Fred Pauchet e5012f7066 Review from Easter holidays 1 year ago
Fred Pauchet 50310ea9f0 Finalize switch to latex 1 year ago
Fred Pauchet 6d7b03e59f Include David Revoy latest picture 1 year ago
Fred Pauchet bc92b559b3 Migrate SOA 1 year ago
Fred Pauchet 373a39a22b Integrate Debian, forms, ... 1 year ago
Fred Pauchet b154a4b302 Add forms 1 year ago
Fred Pauchet ee76783f86 Working a little bit on migrations, models, etc. 1 year ago
Fred Pauchet 208ea90e2f Working on environment isolation 1 year ago
Fred Pauchet 9cd685bddd Write down useful tools 1 year ago
Fred Pauchet 083069d812 Create external tools 1 year ago
Fred Pauchet 2fd26218da Start new project 1 year ago
Fred Pauchet c4a7d94926 Rework structure 1 year ago
Fred Pauchet 9c051318d4 Just finished SOLID 1 year ago
Fred Pauchet 33e950334a SRP, Open-Closed & Liskov Substitution 1 year ago
Fred Pauchet b45386ef48 Fix citations references 1 year ago
Fred Pauchet 261fa2c7c8 12 factors are on the run! 1 year ago
Fred Pauchet 02930bd52f Structure 12 factors 1 year ago
Fred Pauchet 7e6ea730de Conclude first chapter 1 year ago
Fred Pauchet c4ebdaa48b Fix references & complete Python chapter 1 year ago
Fred Pauchet 32e8fc9ada Continue switch to latex 2 years ago
Fred Pauchet 80b6ccb26f Move images to the root folder 2 years ago
Fred Pauchet bd31267ff4 Switching to LaTeX 2 years ago
Fred Pauchet e4839e2048 Switching to latex (definitively) 2 years ago
Fred Pauchet b65a23c66b Add automatic LaTeX conversion 2 years ago
Fred Pauchet a6290ae866 Add LaTeX source folder 2 years ago
Fred Pauchet ac78de067c Change subsection level of linters 2 years ago
Fred Pauchet 64045b66a2 Add a docker-start sh to run latex 2 years ago
Fred Pauchet 44a819ce01 Convert text to docbook 2 years ago
Fred Pauchet 2d4ef253f5 Sphinx is not needed anymore. 2 years ago
Fred Pauchet e8dda4c60d Talk about squashing migrations :) 2 years ago
Fred Pauchet 850c0381aa Add the dependency graph within migrations 2 years ago
Fred Pauchet 3370f5b20c Update SOA, ideas, chapters, ... 2 years ago
Fred Pauchet acc6831726 Copy-paste from my existing doc on Heroku 2 years ago
Fred Pauchet bd9417f65a Continuing... :) 2 years ago
Fred Pauchet ea1f1e8925 Make font-size smaller in citations 2 years ago
Fred Pauchet 9e08136f50 Prepare to add a cover 2 years ago
Fred Pauchet 920341511d Add legacy applications and rework the introduction 2 years ago
Fred Pauchet 14e0f6c73a Finish (I think) SOLID principles 2 years ago
Fred Pauchet 79d30a2902 Add a memo for starting a new docker container 2 years ago
Fred Pauchet 718679964c Integrate notes on tests from the devops handbook 2 years ago
Fred Pauchet 7167745bb3 Integrate devops handbook notes 2 years ago
Fred Pauchet f26ef70f59 Improve models and create SOA 2 years ago
Fred Pauchet 43ee80f983 Restart data model, by integrating Clean Code practices 2 years ago
Fred Pauchet c75fd76775 Create new parts for datamodel, SOA and go-live 2 years ago
Fred Pauchet acb10f07e3 Continue gwift-book 2 years ago
Fred Pauchet 8b66c2b9e9 Integrate IaaS 2 years ago
Fred Pauchet 551f0f5dad Add a bibliography and some resources 2 years ago
Fred Pauchet a78a5c2383 Add a CONTRIBUTION guide 2 years ago
Fred Pauchet c28d0b4cdb Complete references after reviewing each page 2 years ago
Fred Pauchet 059d7c1f99 Rework some quotes 2 years ago
Fred Pauchet e43f9fcf89 Fix quotation 2 years ago
Fred Pauchet 964d95a8ab Add asciidoctor-bibtex for correct references 2 years ago
Fred Pauchet 3dd773d6d8 Add poetry instructions 2 years ago
Fred Pauchet 633352fe4c Add Calvin & Hobbes reference 2 years ago
Fred Pauchet 34058f9dcd Integrate a citation from the devops handbook 2 years ago
Fred Pauchet ba05ecf90b Include part title in the footer section 2 years ago
Fred Pauchet 4c0f262f0c Integrate source reference from the devops handbook 2 years ago
Fred f2e84b4828 Mise à jour de 'source/part-2-deployment/_main.adoc' 2 years ago
Fred 37b7b3fe61 Mise à jour de 'source/part-3-django-concepts/templates.adoc' 2 years ago
Fred 3671359c65 Mise à jour de 'source/part-3-django-concepts/tests.adoc' 2 years ago
Fred a404a3fa4e Ajouter 'source/part-3-django-concepts/tests.adoc' 2 years ago
Fred 4405edc50f Mise à jour de 'source/part-3-django-concepts/_index.adoc' 2 years ago
Fred b17a169762 Mise à jour de 'source/part-3-django-concepts/queryset.adoc' 2 years ago
Fred bc725d59dd Mise à jour de 'source/part-3-django-concepts/models.adoc' 2 years ago
Fred bd3cd12959 Mise à jour de 'source/part-3-django-concepts/migrations.adoc' 2 years ago
Fred 0c0f3346bb Mise à jour de 'source/part-3-django-concepts/models.adoc' 2 years ago
Fred 51f052ed26 Mise à jour de 'source/part-3-django-concepts/models.adoc' 2 years ago
Fred 496a94d4ae Mise à jour de 'source/part-1-workspace/maintainable-applications/clean_architecture.adoc' 2 years ago
Fred 618980ce31 Mise à jour de 'source/part-3-django-concepts/urls.adoc' 2 years ago
Fred c951e9f12d Mise à jour de 'source/part-3-django-concepts/_index.adoc' 2 years ago
Fred d93f0c6b9e Ajouter 'source/part-3-django-concepts/settings.adoc' 2 years ago
Fred 81f1a546ab Mise à jour de 'source/part-3-django-concepts/views.adoc' 2 years ago
Fred 3aae64ad54 Mise à jour de 'source/part-1-workspace/django/_index.adoc' 2 years ago
Fred 7f14bf4ff6 Mise à jour de 'source/part-1-workspace/django/_index.adoc' 2 years ago
Fred 4c38c1d4fe Mise à jour de 'source/part-1-workspace/maintainable-applications/clean_architecture.adoc' 2 years ago
Fred e5a89c77cd Mise à jour de 'source/part-1-workspace/maintainable-applications/clean_architecture.adoc' 2 years ago
Fred 370ba5e264 Mise à jour de 'source/part-1-workspace/django/_index.adoc' 2 years ago
Fred ff537f1c0a Ajouter 'source/part-4-go-live/integrations.adoc' 2 years ago
Fred 9bb3b243a8 Mise à jour de 'source/part-1-workspace/maintainable-applications/clean_architecture.adoc' 2 years ago
Fred b188df0326 Mise à jour de 'source/part-1-workspace/maintainable-applications/maintainable-applications.adoc' 2 years ago
Fred 87a7e073bf Mise à jour de 'source/part-1-workspace/_main.adoc' 2 years ago
Fred 9936bfe125 Mise à jour de 'source/part-2-deployment/_main.adoc' 2 years ago
Fred 7cfc8b060b Mise à jour de 'source/part-1-workspace/maintainable-applications/clean_architecture.adoc' 2 years ago
Fred 64eb83306c Mise à jour de 'source/part-1-workspace/maintainable-applications/maintainable-applications.adoc' 2 years ago
Fred 052592c983 Mise à jour de 'source/part-1-workspace/maintainable-applications/solid.adoc' 2 years ago
Fred 810ceb6b81 Mise à jour de 'source/part-1-workspace/maintainable-applications/_index.adoc' 2 years ago
Fred 7a538b8283 Mise à jour de 'source/part-1-workspace/maintainable-applications/clean_architecture.adoc' 2 years ago
Fred 0224c6c35b Mise à jour de 'source/part-1-workspace/maintainable-applications/clean_architecture.adoc' 2 years ago
Fred be9179b851 Mise à jour de 'source/part-1-workspace/maintainable-applications/_index.adoc' 2 years ago
Fred 198ba0ad16 Mise à jour de 'source/part-1-workspace/_main.adoc' 2 years ago
Fred 817cab3e69 Mise à jour de 'source/main.adoc' 2 years ago
Fred 175134b400 Mise à jour de 'source/part-1-workspace/environment/_index.adoc' 2 years ago
Fred 160709be3c Mise à jour de 'source/part-1-workspace/_main.adoc' 2 years ago
Fred a9c767915a Mise à jour de 'source/part-1-workspace/django/_index.adoc' 2 years ago
Fred 6127ad491b Mise à jour de 'source/part-1-workspace/django/_index.adoc' 2 years ago
Fred 89173d83c8 Mise à jour de 'source/part-1-workspace/django/_index.adoc' 2 years ago
Fred c2808d57eb Mise à jour de 'source/part-1-workspace/maintainable-applications/_index.adoc' 2 years ago
Fred 1abf05bc57 Mise à jour de 'source/part-1-workspace/environment/_index.adoc' 2 years ago
Fred 7153e8df0f Mise à jour de 'source/part-1-workspace/_main.adoc' 2 years ago
Fred 8528a1a0c2 Mise à jour de 'source/main.adoc' 2 years ago
Fred 2a4ef7b8b9 Mise à jour de 'source/part-1-workspace/_main.adoc' 2 years ago
Fred cf720c58e6 Mise à jour de 'source/part-1-workspace/_main.adoc' 2 years ago
Fred c3eff557bd Mise à jour de 'source/part-1-workspace/maintainable-applications/clean_architecture.adoc' 2 years ago
Fred 293725cd1e Mise à jour de 'source/part-1-workspace/maintainable-applications/clean_architecture.adoc' 2 years ago
Fred 63f445c4c4 Mise à jour de 'source/part-1-workspace/maintainable-applications/clean_architecture.adoc' 2 years ago
Fred ed8decec0c Mise à jour de 'source/part-1-workspace/_main.adoc' 2 years ago
Fred 0487813aff Mise à jour de 'source/part-1-workspace/_main.adoc' 2 years ago
Fred 9a0fa7d0c8 Mise à jour de 'source/part-1-workspace/django/_index.adoc' 2 years ago
Fred 3f556c57bd Mise à jour de 'source/part-1-workspace/maintainable-applications/clean_architecture.adoc' 2 years ago
Fred 0d8e6f8ac4 Ajouter 'source/part-1-workspace/maintainable-applications/clean_architecture.adoc' 2 years ago
Fred ee6703d303 Mise à jour de 'source/part-1-workspace/maintainable-applications/_index.adoc' 2 years ago
Fred Pauchet 3a84830f3c Fix layout for TOC 2 years ago
Fred Pauchet b40d41d183 Review dependencies, pdf conversion process and known issues 2 years ago
Fred Pauchet 1af4d4c8f2 Delete latex (too complicated for my little brain) 2 years ago
Fred a6519ed11a Switch to TeX ? :) 2 years ago
Fred 136b053cee On -> Nous 2 years ago
Fred Pauchet e994fcc628 Working on the administration panel 2 years ago
Fred Pauchet a96955f6f1 Working on migrations and models 2 years ago
Fred 525fd98ac0 Working on models 2 years ago
Fred b0212568d9 Describe migrations and add a basic schema 3 years ago
Fred d0dc39e036 Merge branch 'master' of grimbox.be:Fred/gwift-book 3 years ago
Fred 831fb93783 Working on tests 3 years ago
Fred dc68c23ec1 Ajouter 'ideas/tests.asciidoc' 3 years ago
Fred 16d0a344bb Ced' has left a ';' in a Python block code :-) 3 years ago
Fred 333fa33135 Review logging 3 years ago
Fred 739c7e7dc0 Redémarrage gracieux du process master de gunicorn 3 years ago
Fred 157776b35d Move files and folder for coherence 3 years ago
Fred f08f54049a Fix some asciidoctor errors 3 years ago
Fred 85befe2b7e Add some todos :-) 3 years ago
Fred 716983c2a4 Add Expert Python Programming to resources 3 years ago
Fred c2e51df127 Finalize Vagrant configuration file 3 years ago
Fred 873676fb61 Remove unknown parameters 3 years ago
Fred 65001f1a12 Add a basic 'setup.cfg' configuration file 3 years ago
Fred 507501c555 A little reminder to fetch database backup 3 years ago
Fred 30512fce2e Add a lot about Heroku 3 years ago
Fred 1027f5fc44 Describe Mypy 3 years ago
Fred 4655ab2a57 Add resources 3 years ago
Fred e324810894 Describe how changes are threated by Git 3 years ago
Fred 659a3b24ba Change UTF8 user icon 3 years ago
Fred ec7d5825de Explains a basic Django cycle 3 years ago
Fred Pauchet efdcda2ec2 Add a global 'how it works' schema 3 years ago
Fred Pauchet fdea1e535c Describe tox.ini file 3 years ago
Fred Pauchet a82558a141 Wrong [source,<type>] 3 years ago
  1. 1
      .drone.yml
  2. 2
      .gitignore
  3. 7
      .gitlab-ci.yml
  4. 36
      CONTRIBUTING.md
  5. 14
      Dockerfile
  6. 15
      Makefile
  7. 27
      README.md
  8. 55
      annexes/gilded-roses.tex
  9. 71
      annexes/grafana.tex
  10. 5
      annexes/snippets.tex
  11. 2
      annexes/sonar.tex
  12. 316
      chapters/administration.tex
  13. 396
      chapters/api.tex
  14. 957
      chapters/architecture.tex
  15. 233
      chapters/authentication.tex
  16. 79
      chapters/contexts-processors.tex
  17. 50
      chapters/continuous-integration.tex
  18. 562
      chapters/debian.tex
  19. 5
      chapters/deployment-processes.tex
  20. 18
      chapters/deployment-tools.tex
  21. 33
      chapters/docker-compose.tex
  22. 123
      chapters/filters.tex
  23. 175
      chapters/forms.tex
  24. 430
      chapters/gdpr.tex
  25. 402
      chapters/gwift.tex
  26. 262
      chapters/heroku.tex
  27. 14
      chapters/i18n.tex
  28. 317
      chapters/infrastructure.tex
  29. 150
      chapters/introduction.tex
  30. 2
      chapters/js-framework.tex
  31. 35
      chapters/khana.tex
  32. 3
      chapters/kubernetes.tex
  33. 27
      chapters/licence.tex
  34. 337
      chapters/maintenability.tex
  35. 521
      chapters/migrations.tex
  36. 887
      chapters/models.tex
  37. 741
      chapters/new-project.tex
  38. 1457
      chapters/python.tex
  39. 9
      chapters/resources.tex
  40. 31
      chapters/security.tex
  41. 268
      chapters/templates.tex
  42. 233
      chapters/tests.tex
  43. 5
      chapters/thanks.tex
  44. 304
      chapters/tools.tex
  45. 331
      chapters/trees.tex
  46. 106
      chapters/urls.tex
  47. 30
      chapters/views.tex
  48. 616
      chapters/working-in-isolation.tex
  49. 46
      code_samples/shapes.py
  50. 1
      diagrams/books-foreign-keys-example
  51. BIN
      diagrams/books-foreign-keys-example.drawio.png
  52. 9
      docker-miktex.sh
  53. 64
      glossary.tex
  54. 78
      gwiftemplate.sty
  55. BIN
      ideas/21-10-01 21-30-54 1890.jpg
  56. BIN
      ideas/21-10-01 21-31-20 1891.jpg
  57. 0
      ideas/libs.rst
  58. 0
      ideas/resources.md
  59. 1
      ideas/tests.asciidoc
  60. BIN
      images/12factors/attached-resources.png
  61. BIN
      images/12factors/execution-process-memory.drawio.png
  62. BIN
      images/12factors/process-type-chronology.png
  63. BIN
      images/12factors/process-types.png
  64. BIN
      images/12factors/release.png
  65. BIN
      images/12factors/separate-run-steps.png
  66. BIN
      images/12factors/unique-codebase-deploys.png
  67. BIN
      images/amazon-s3-arch.png
  68. BIN
      images/arch-comp-modules.png
  69. 1
      images/arch-structure
  70. BIN
      images/calvin/time-machine.jpg
  71. BIN
      images/commistrip/versions.jpg
  72. 1
      images/db/link-book-category-fk.drawio
  73. BIN
      images/db/link-book-category-fk.drawio.png
  74. BIN
      images/db/link-book-category-m2m.drawio.png
  75. BIN
      images/db/migrations-0001-to-0002.png
  76. BIN
      images/db/migrations-0002-many-to-many.png
  77. BIN
      images/db/migrations_auth_admin_contenttypes_sessions.png
  78. 1
      images/db/~$link-book-category-fk.drawio.dtmp
  79. BIN
      images/deployment/disallowed_hosts.png
  80. BIN
      images/deployment/gwift-cloud-s3.png
  81. BIN
      images/deployment/heroku-app-created.png
  82. BIN
      images/deployment/heroku-new-app.png
  83. BIN
      images/deployment/heroku-vars-reveal.png
  84. BIN
      images/deployment/heroku.png
  85. BIN
      images/deployment/iaas_focus-paas-saas-diagram.png
  86. BIN
      images/deployment/scaleway-api-key.png
  87. BIN
      images/deployment/scaleway-object-storage-bucket.png
  88. 0
      images/diagrams/12-factors-1
  89. 0
      images/diagrams/12-factors-1.png
  90. 0
      images/diagrams/12-factors-7
  91. 0
      images/diagrams/12-factors-7.png
  92. BIN
      images/diagrams/architecture.png
  93. BIN
      images/diagrams/basic-automation.drawio.png
  94. BIN
      images/diagrams/deploy-without-hassle.drawio.png
  95. 1
      images/diagrams/django-how-it-works.drawio
  96. BIN
      images/diagrams/django-how-it-works.png
  97. 1
      images/diagrams/django-migrations-db-schema.drawio
  98. 0
      images/diagrams/django-process.png
  99. 0
      images/diagrams/django-project-vs-apps-gwift.drawio
  100. 0
      images/diagrams/django-project-vs-apps-khana.drawio

1
.drone.yml

@ -1,4 +1,5 @@
kind: pipeline
type: docker
name: default
steps:

2
.gitignore

@ -7,3 +7,5 @@ build
*.log
*.vscode/
*.asciidoctor/
*.aux
_minted-main/

7
.gitlab-ci.yml

@ -1,7 +0,0 @@
compile_pdf:
image: ddidier/sphinx-doc
script:
- make latexpdf
artifacts:
paths:
- build/latex/Gwift.pdf

36
CONTRIBUTING.md

@ -0,0 +1,36 @@
# Contibuer à Gwift
(Grossièrement traduit et adapté de [ProGit](https://raw.githubusercontent.com/progit/progit2/main/CONTRIBUTING.md))
## Licence
Quand vous ouvrez une *pull request*, vous acceptez d'appliquer la même licence que le projet à votre travail.
Aussi, vous acceptez de céder votre travail sous cette même licence.
Si vos modifications devaient apparaitre dans une version publiée, vous apparaitrez dans la [liste des contributeurs](book/contributors.adoc).
## Signaler un problème
Vérifiez avant tout s'il n'existe pas déjà un problème similaire, avant de créer un nouveau ticket.
Aussi, vérifiez si ce même problème n'a pas déjà été corrigé dans les fichiers sources, mais n'aurait pas encore été pris en compte dans une version ultérieure du fichier PDF.
## Petites corrections
Les errata et clarifications basiques seront acceptés si nous sommes d'accord sur le fait qu'ils améliorent le contenu.
Vous pouvez ouvrir un ticket, de manière à ce que nous discutions de la manière dont il faut adresser le changement.
Si vous n'avez jamais réalisé ceci, le [flow guide](https://guides.github.com/introduction/flow/) peut être utile au début.
## Gros changements
Ouvrez d'abord une discussion, de manière à démarrer.
Une grosse modification tend à être très subjective, et ne vise souvent qu'un petit nombre de lecteurs/utilisateurs.
## Images et schéma
Les images de ce livre sont générées en utilisant [Draw.io](draw.io).
## Traductions
Il n'y en a pas pour le moment 😉.

14
Dockerfile

@ -0,0 +1,14 @@
FROM miktex/miktex
LABEL Description="Docker container from MiKTeX, Ubuntu 20.04, with Pygments" Version="1.0"
RUN rm /etc/apt/sources.list.d/miktex.list
RUN apt-get update
RUN apt-get install python3-pip -y
RUN pip install pygments
WORKDIR /miktex/work
CMD ["bash"]

15
Makefile

@ -1,5 +1,5 @@
.PHONY: help pdf
.PHONY: pdf
help:
@echo "Please use \`make <target>' where <target> is one of"
@ -8,10 +8,11 @@ help:
clean:
rm -rf $(BUILDDIR)/*
html:
$(SPHINXBUILD) -b html $(ALLSPHINXOPTS) $(BUILDDIR)/html
@echo
@echo "Build finished. The HTML pages are in $(BUILDDIR)/html."
pdf:
asciidoctor-pdf -a pdf-themesdir=resources/themes -a pdf-theme=book.yml source/main.adoc -t
asciidoctor-pdf \
-r asciidoctor-bibtex \
-a pdf-themesdir=resources/themes \
-a pdf-theme=book.yml \
-a pdf-fontsdir="resources/fonts" \
source/main.adoc -t \
--trace

27
README.md

@ -2,28 +2,13 @@
[![Build Status](https://drone.grimbox.be/api/badges/fred/gwift-book/status.svg)](https://drone.grimbox.be/fred/gwift-book)
Ce livre peut être compilé avec [AsciiDoctor](...).
Ce livre peut être compilé avec une image Docker personnalisée, basée sur Miktex.
Pygments est également installé.
Elle peut être compilée avec Docker ou Podman, grâce à l'une des commandes suivantes:
* `docker build -t miktex-pygments .`
## Dépendances
Pour compiler le manuel en PDF:
```bash
$ apt install plantuml ruby-asciidoctor-plantuml
$ gem install asciidoctor-pdf --pre
$ gem install rouge
$ gem install asciidoctor-diagram
```
* `./docker-miktex.sh`
## Conversion en PDF
```bash
asciidoctor -a rouge-style=monokai -a pdf-themesdir=resources/themes -a pdf-theme=gwift main.adoc -t -r asciidoctor-diagram
asciidoctor-pdf -a pdf-themesdir=resources/themes -a pdf-theme=gwift main.adoc -t -r asciidoctor-diagram
```
## Configuration de l'espace utilisateur
```bash
source /usr/share/powerline/bindings/bash/powerline.sh
```

55
annexes/gilded-roses.tex

@ -0,0 +1,55 @@
\chapter{Gilded Roses}
\url{https://github.com/emilybache/GildedRose-Refactoring-Kata}
\begin{listing}[H]
\begin{minted}[tabsize=4]{python}
# -*- coding: utf-8 -*-
class GildedRose(object):
def __init__(self, items):
self.items = items
def update_quality(self):
for item in self.items:
if item.name != "Aged Brie" and item.name != "Backstage passes to a TAFKAL80ETC concert":
if item.quality > 0:
if item.name != "Sulfuras, Hand of Ragnaros":
item.quality = item.quality - 1
else:
if item.quality < 50:
item.quality = item.quality + 1
if item.name == "Backstage passes to a TAFKAL80ETC concert":
if item.sell_in < 11:
if item.quality < 50:
item.quality = item.quality + 1
if item.sell_in < 6:
if item.quality < 50:
item.quality = item.quality + 1
if item.name != "Sulfuras, Hand of Ragnaros":
item.sell_in = item.sell_in - 1
if item.sell_in < 0:
if item.name != "Aged Brie":
if item.name != "Backstage passes to a TAFKAL80ETC concert":
if item.quality > 0:
if item.name != "Sulfuras, Hand of Ragnaros":
item.quality = item.quality - 1
else:
item.quality = item.quality - item.quality
else:
if item.quality < 50:
item.quality = item.quality + 1
class Item:
def __init__(self, name, sell_in, quality):
self.name = name
self.sell_in = sell_in
self.quality = quality
def __repr__(self):
return "%s, %s, %s" % (self.name, self.sell_in, self.quality)
\end{minted}
\end{listing}

71
annexes/grafana.tex

@ -0,0 +1,71 @@
\chapter{Monitoring Stack}
InfluxDB ? https://www.influxdata.com/
\section{Visualisation}
\begin{quote}
Grafana allows you to query, visualize, alert on and understand your metrics no matter where they are stored.
Create, explore, and share beautiful dashboards with your team and foster a data driven culture.
\end{quote}
\section{Métriques}
\begin{quote}
Graphite is an enterprise-ready monitoring tool that runs equally well on cheap hardware or Cloud infrastructure.
Teams use Graphite to track the performance of their websites, applications, business services, and networked servers.
It marked the start of a new generation of monitoring tools, making it easier than ever to store, retrieve, share, and visualize time-series data.
Graphite was originally designed and written by Chris Davis at Orbitz in 2006 as side project that ultimately grew to be their foundational monitoring tool.
In 2008, Orbitz allowed Graphite to be released under the open source Apache 2.0 license.
Numerous large companies have deployed Graphite to production where it helps them to monitor their production e-commerce services and plan for growth.
\end{quote}
Graphite does two things:
\begin{enumerate}
\item
Store numeric time-series data
\item
Render graphs of this data on demand
\end{enumerate}
What Graphite does not do is collect data for you, however there are some tools out there that know how to send data to graphite. Even though it often requires a little code, sending data to Graphite is very simple.
Graphite consists of 3 software components:
\begin{enumerate}
\item
\textbf{carbon} - a Twisted daemon that listens for time-series data
\item
\textbf{whisper} - a simple database library for storing time-series data (similar in design to RRD)
\item
\textbf{graphite webapp} - A Django webapp that renders graphs on-demand using Cairo
\end{enumerate}
Feeding in your data is pretty easy, typically most of the effort is in collecting the data to begin with. As you send datapoints to Carbon, they become immediately available for graphing in the webapp. The webapp offers several ways to create and display graphs including a simple URL API for rendering that makes it easy to embed graphs in other webpages.
\section{Logs}
\begin{quote}
Loki brings together logs from all your applications and infrastructure in a single place.
By using the exact same service discovery and label model as Prometheus, Grafana Logs can systematically guarantee your logs have consistent metadata with your metrics, making it easy to move from one to the other.
\end{quote}
Loki est l'équivalent (développé directement par Grafana) de Prometheus.
Il sera donc toujours nécessaire d'accumuler des logs au travers d'exporters.
\begin{quote}
Loki se comporte comme Prometheus : c'est un logiciel que vous allez installer sur votre machine qui sert pour le monitoring de votre infrastructure et le laisser vivre sa vie. Comme son mentor, ou presque, il va falloir lui associer des exporters pour le gaver de données : Promtail.
-- https://www.dadall.info/article698/loki-jouer-avec-ses-logs-dans-grafana
\end{quote}
\section{Traces}
Unlike other tracing tools, Grafana Traces does not index the traces which makes it possible to store orders of magnitude more trace data for the same cost, and removes the need for sampling.
Stores orders of magnitude more trace data for the same cost, and removes the need for sampling.
Reduces the TCO by an order of magnitude and makes the system overall much easier to use.
Grafana Traces is available as a containerized application, and you can run it on any orchestration engine like Kubernetes, Mesos, etc. The various services can be horizontally scaled depending on the workload on the ingest/query path. You can also use cloud native object storage, such as Google Cloud Storage, Amazon S3, or Azure Blob Storage.

5
annexes/snippets.tex

@ -0,0 +1,5 @@
\chapter{Snippets}
\section{Nettoyage de chaînes de caractères}
Basically, NFC is how you normalize text that's meant to be displayed to a user on the web, and NFKC is how you normalize text that's used to for searching and guaranteeing uniqueness. \cite{django_for_startup_founders}

2
annexes/sonar.tex

@ -0,0 +1,2 @@
\chapter{SonarQube}

316
chapters/administration.tex

@ -0,0 +1,316 @@
\chapter{Administration}
Cette partie est tellement puissante et performante, qu'elle pourrait laisser penser qu'il est possible de réaliser une application complète rien qu'en configurant l'administration.
C'est faux.
L'administration est une sorte de tour de contrôle évoluée, un \emph{back office} sans transpirer; elle se base sur le modèle de données programmé et construit dynamiquement les formulaires qui lui est associé.
Elle joue avec les clés primaires, étrangères, les champs et types de champs par \href{https://fr.wikipedia.org/wiki/Introspection}{introspection}, et présente tout ce qu'il faut pour avoir du \href{https://fr.wikipedia.org/wiki/CRUD}{CRUD} \index{CRUD} \footnote{\emph{Create-Read-Update-Delete}, c'est-à-dire le fonctionnement par défaut de beaucoup d'applications}, c'est-à-dire tout ce qu'il faut pour ajouter, lister, modifier ou supprimer des informations.
Son problème est qu'elle présente une courbe d'apprentissage asymptotique.
Il est \textbf{très} facile d'arriver rapidement à un bon résultat, au travers d'un périmètre de configuration relativement restreint.
Quoi que vous fassiez, il y a un moment où la courbe de paramétrage sera tellement ardue que vous aurez plus facile à développer ce que vous souhaitez ajouter en utilisant les autres concepts de Django.
Cette interface doit rester dans les mains d'administrateurs ou de gestionnaires, et dans leurs mains à eux uniquement: il n'est pas question de donner des droits aux utilisateurs finaux (même si c'est extrêment tentant durant les premiers tours de roues).
Indépendamment de la manière dont vous allez l'utiliser et la configurer, vous finirez par devoir développer une "vraie" application, destinée aux utilisateurs classiques, et répondant à leurs besoins uniquement.
Une bonne idée consiste à développer l'administration dans un premier temps, en \textbf{gardant en tête qu'il sera nécessaire de développer des concepts spécifiques}.
Dans cet objectif, l'administration est un outil exceptionel, qui permet de valider un modèle, de créer des objets rapidement et de valider les liens qui existent entre eux.
C'est aussi un excellent outil de prototypage et de preuve de concept.
Elle se base sur plusieurs couches que l'on a déjà (ou on va bientôt) aborder (suivant le sens de lecture que vous préférez):
\begin{enumerate}
\item
Le modèle de données
\item
Les validateurs
\item
Les formulaires
\item
Les widgets
\end{enumerate}
\section{Le modèle de données}
Comme expliqué ci-dessus, le modèle de données est constité d'un ensemble de champs typés et de relations.
L'administration permet de décrire les données qui peuvent être modifiées, en y associant un ensemble (basique) de permissions.
Si vous vous rappelez de l'application que nous avions créée dans la première partie, les URLs reprenaient déjà la partie suivante:
\begin{minted}[tabsize=4]{python}
from django.contrib import admin
from django.urls import path
from gwift.views import wish_details
urlpatterns = [
path('admin/', admin.site.urls),
[...]
]
\end{minted}
Cette URL signifie que la partie \texttt{admin} est déjà active et accessible à l'URL \texttt{\textless{}mon\_site\textgreater{}/admin}.
C'est le seul prérequis pour cette partie.
Chaque application nouvellement créée contient par défaut un fichier \texttt{admin.py}, dans lequel il est possible de déclarer les ensembles de données seront accessibles ou éditables.
Ainsi, si nous partons du modèle basique que nous avions détaillé plus tôt, avec des souhaits et des listes de souhaits:
\begin{minted}[tabsize=4]{python}
# gwift/wish/models.py
from django.db import models
class WishList(models.Model):
name = models.CharField(max_length=255)
class Item(models.Model):
name = models.CharField(max_length=255)
wishlist = models.ForeignKey(WishList, on_delete=models.CASCADE)
\end{minted}
Nous pouvons facilement arriver au résultat suivant, en ajoutant
quelques lignes de configuration dans ce fichier \texttt{admin.py}:
\begin{minted}[tabsize=4]{python}
from django.contrib import admin
from .models import Item, WishList
admin.site.register(Item)
admin.site.register(WishList)
\end{minted}
\begin{itemize}
\item
Nous importons les modèles que nous souhaitons gérer dans l'admin
\item
Et nous les déclarons comme gérables. Cette dernière ligne implique aussi qu'un modèle pourrait ne pas être disponible du tout, ce qui n'activera simplement aucune opération de lecture ou modification.
\end{itemize}
Il nous reste une seule étape à réaliser: créer un nouvel utilisateur.
Pour cet exemple, notre gestion va se limiter à une gestion manuelle; nous aurons donc besoin d'un \emph{super-utilisateur}, que nous pouvons créer grâce à la commande \texttt{python\ manage.py\ createsuperuser}.
\begin{verbatim}
$ python manage.py createsuperuser
Username (leave blank to use 'fred'): fred
Email address: fred@root.org
Password: ******
Password (again): ******
Superuser created successfully.
\end{verbatim}
\begin{figure}[H]
\centering
\includegraphics{images/django/django-site-admin.png}
\caption{Connexion au site d'administration}
\end{figure}
\begin{figure}[H]
\centering
\includegraphics{images/django/django-site-admin-after-connection.png}
\caption{Administration}
\end{figure}
Ceci nous permet déjà d'ajouter des éléments (Items), des listes de souhaits, de visualiser les actions récentes, voire de gérer les autorisations attribuées aux utilisateurs, comme les membres du staff ou les administrateurs.
\section{Quelques conseils de base}
\begin{enumerate}
\item
Surchargez la méthode \texttt{str(self)} pour chaque classe que vous aurez définie dans le modèle.
Cela permettra de construire une représentation textuelle pour chaque instance de votre classe.
Cette information sera utilisée un peu partout dans le code, et donnera une meilleure idée de ce que l'on manipule.
En plus, cette méthode est également appelée lorsque l'administration historisera une action (et comme cette étape sera inaltérable, autant qu'elle soit fixée dans le début).
\item
La méthode \texttt{get\_absolute\_url(self)} retourne l'URL à laquelle on peut accéder pour obtenir les détails d'une instance. Par exemple:
\end{enumerate}
\begin{minted}[tabsize=4]{python}
def get_absolute_url(self):
return reverse('myapp.views.details', args=[self.id])
\end{minted}
\begin{enumerate}
\def\labelenumi{\arabic{enumi}.}
\item
Les attributs \texttt{Meta}:
\end{enumerate}
\begin{minted}[tabsize=4]{python}
class Meta:
ordering = ['-field1', 'field2']
verbose_name = 'my class in singular'
verbose_name_plural = 'my class when is in a list!'
\end{minted}
\begin{enumerate}
\item
Le titre:
\begin{itemize}
\item
Soit en modifiant le template de l'administration
\item
Soit en ajoutant l'assignation suivante dans le fichier \texttt{urls.py} : \texttt{admin.site.site\_header\ =\ "SuperBook\ Secret\ Area}.
\end{itemize}
\item
Prefetch
\end{enumerate}
\url{https://hackernoon.com/all-you-need-to-know-about-prefetching-in-django-f9068ebe1e60?gi=7da7b9d3ad64}
\url{https://medium.com/@hakibenita/things-you-must-know-about-django-admin-as-your-app-gets-bigger-6be0b0ee9614}
En gros, le problème de l'admin est que si on fait des requêtes imbriquées, on va flinguer l'application et le chargement de la page.
La solution consiste à utiliser la propriété \texttt{list\_select\_related} de la classe d'Admin, afin d'appliquer une jointure par défaut et et gagner en performances.
\subsection{admin.ModelAdmin}
La classe \texttt{admin.ModelAdmin} que l'on retrouvera principalement dans le fichier \texttt{admin.py} de chaque application contiendra la définition de ce que l'on souhaite faire avec nos données dans l'administration. Cette classe (et sa partie Meta)
\subsection{L'affichage}
Comme l'interface d'administration fonctionne (en trèèèès) gros comme un CRUD auto-généré, on trouve par défaut la possibilité de :
\begin{enumerate}
\item
Créer de nouveaux éléments
\item
Lister les éléments existants
\item
Modifier des éléments existants
\item
Supprimer un élément en particulier.
\end{enumerate}
Les affichages sont donc de deux types: en liste et au détail.
Pour les affichages en liste, le plus simple consiste à jouer sur la propriété \texttt{list\_display}.
Par défaut, la première colonne va accueillir le lien vers le formulaire d'édition.
On peut donc modifier ceci, voire créer de nouveaux liens vers d'autres éléments en construisant des URLs dynamiquement.
Voir aussi comment personnaliser le fil d'Ariane ?
\section{Filtres}
Chaque liste permet de spécifier des filtres spécifiques; ceux-ci peuvent être:
\begin{enumerate}
\item \textbf{Appliqués à la liste} (\texttt{list\_filter})
\item \textbf{Horizontaux} (\texttt{filter\_horizontal})
\item \textbf{Verticaux} (\texttt{filter\_vertical})
\item \textbf{Temporels} (\texttt{date\_hierarchy}
\end{enumerate}
\subsection{Appliqués à la liste}
\subsection{Horizontaux}
\subsection{Verticaux}
\subsection{Temporels}
\section{Permissions}
On l'a dit plus haut, il vaut mieux éviter de proposer un accès à l'administration à vos utilisateurs.
Il est cependant possible de configurer des permissions spécifiques pour certains groupes, en leur autorisant certaines actions de visualisation/ajout/édition ou suppression.
Cela se joue au niveau du \texttt{ModelAdmin}, en implémentant les méthodes suivantes:
\begin{minted}[tabsize=4]{python}
def has_add_permission(self, request):
return True
def has_delete_permission(self, request):
return True
def has_change_permission(self, request):
return True
\end{minted}
On peut accéder aux informations de l'utilisateur actuellement connecté au travers de l'objet \texttt{request.user}.
\section{Relations}
\subsection{Relations 1-N}
Les relations 1-n sont implémentées au travers de formsets (que l'on a normalement déjà décrits plus haut). L'administration permet de les définir d'une manière extrêmement simple, grâce à quelques propriétés.
L'implémentation consiste tout d'abord à définir le comportement du type d'objet référencé (la relation -N), puis à inclure cette définition au niveau du type d'objet référençant (la relation 1-).
\begin{minted}[tabsize=4]{python}
class WishInline(TabularInline):
model = Wish
class Wishlist(admin.ModelAdmin):
...
inlines = [WishInline]
...
\end{minted}
Et voilà : l'administration d'une liste de souhaits (\emph{Wishlist}) pourra directement gérer des relations multiples vers des souhaits.
\subsection{Autocomplétion}
Parler de l'intégration de select2.
\section{Forms}
\section{Présentation}
Parler ici des \texttt{fieldsets} et montrer comment on peut regrouper des champs dans des groupes, ajouter un peu de JavaScript, ...
\section{Actions sur des sélections}
Les actions permettent de partir d'une liste d'éléments, et autorisent
un utilisateur à appliquer une action sur une sélection d'éléments. Par
défaut, il existe déjà une action de \textbf{suppression}.
Les paramètres d'entrée sont :
\begin{enumerate}
\def\labelenumi{\arabic{enumi}.}
\item
L'instance de classe
\item
La requête entrante
\item
Le queryset correspondant à la sélection.
\end{enumerate}
\begin{minted}[tabsize=4]{python}
def double_quantity(self, request, queryset):
for obj in queryset.all():
obj.field += 1
obj.save()
double_quantity.short_description = "Doubler la quantité des souhaits."
\end{minted}
Et pour informer l'utilisateur de ce qui a été réalisé, on peut aussi
lui passer un petit message:
\begin{minted}[tabsize=4]{python}
if rows_updated = 0:
self.message_user(request, "Aucun élément n'a été impacté.")
else:
self.message_user(request, "{} élément(s) mis à jour".format(rows_updated))
\end{minted}
\section{Documentation}
Nous l'avons dit plus haut, l'administration de Django a également la possibilité de rendre accessible la documentation associée à un modèle de données.
Pour cela, il suffit de suivre les bonnes pratiques, puis \href{https://docs.djangoproject.com/en/stable/ref/contrib/admin/admindocs/}{d'activer la documentation à partir des URLs}:

396
chapters/api.tex

@ -0,0 +1,396 @@
\chapter{Application Programming Interface}
\url{https://news.ycombinator.com/item?id=30221016\&utm_term=comment} vs
Django Rest Framework vs Marshmallow
Expliquer pourquoi une API est intéressante/primordiale/la première chose à réaliser/le cadet de nos soucis:
\begin{itemize}
\item
Intéressante: ouverture
\item
Primordiale: services
\item
La première chose à réaliser: mobile-first
\item
Le cadet de nos soucis: monolithique (cf. Rework)
\end{itemize}
Voir peut-être aussi
\url{https://christophergs.com/python/2021/12/04/fastapi-ultimate-tutorial/}
Remarque : quatre statuts = le minimum syndical. \cite[p. 297]{restful_web_apis} :
\begin{enumerate}
\item
\textbf{200 (OK)}.
Tout va bien.
Le document qui se trouve dans le corps de la réponse, s'il y en a un, est la représentation d'une ressource.
\item
\textbf{301 (Moved Permanently)}.
Reçu lorsque la ressource n'est plus disponible à cette URI.
\item
\textbf{400 (Bad Request)}.
Indique qu'il y a eu un problème côté client.
Le document qui se trouve dans le corps de la réponse, s'il existe, est un message d'erreur.
Avec un peu de chance, le client a la possibilité d'interpréter ce message d'erreur, afin de corriger le problème.
\item
\textbf{500 (Internal Server Error)}.
Il y a un problème côté serveur. Le document présent dans le corps de la réponse, toujours s'il existe, indique le problème.
Comme celui-ci se situe au niveau du serveur, le client ne pourra rien faire pour le résoudre.
\end{enumerate}
Au niveau du modèle, nous allons partir de quelque chose de très simple: des personnes, des contrats, des types de contrats, et un service d'affectation.
Quelque chose comme ceci:
\begin{minted}{python}
# models.py
from django.db import models
class People(models.Model):
CIVILITY_CHOICES = (
("M", "Monsieur"),
("Mme", "Madame"),
("Dr", "Docteur"),
("Pr", "Professeur"),
("", "")
)
last_name = models.CharField(max_length=255)
first_name = models.CharField(max_length=255)
civility = models.CharField(
max_length=3,
choices=CIVILITY_CHOICES,
default=""
)
def __str__(self):
return "{}, {}".format(self.last_name, self.first_name)
class Service(models.Model):
label = models.CharField(max_length=255)
def __str__(self):
return self.label
class ContractType(models.Model):
label = models.CharField(max_length=255)
short_label = models.CharField(max_length=50)
def __str__(self):
return self.short_label
class Contract(models.Model):
people = models.ForeignKey(People, on_delete=models.CASCADE)
date_begin = models.DateField()
date_end = models.DateField(blank=True, null=True)
contract_type = models.ForeignKey(ContractType, on_delete=models.CASCADE)
service = models.ForeignKey(Service, on_delete=models.CASCADE)
def __str__(self):
if self.date_end is not None:
return "A partir du {}, jusqu'au {}, dans le service {} ({})".format(
self.date_begin,
self.date_end,
self.service,
self.contract_type
)
return "A partir du {}, à durée indéterminée, dans le service {}({})".format(
self.date_begin,
self.service,
self.contract_type
)
\end{minted}
\includegraphics{images/rest/models.png}
\section{Mise en place}
La configuration des points de terminaison de notre API peut être relativement touffue.
Pour cette raison, il convient de s'infliger à suivre une structure qui soit similaire pour chaque point de terminaison \cite[Predictability, Rule \#1]{django_for_startup_founders}.
Il convient de:
\begin{enumerate}
\item
\textbf{Spécifier les permissions}
\item
\textbf{Copier et assainir les éléments communiqués en entrée vers des variables locales}
\item
\textbf{Valider les données d'entrée}
\item
\textbf{Enforce business requirements}
\item
\textbf{Perform busines logic}
\item
\textbf{Retourner une réponse HTTP}
\end{enumerate}
-> Répartir les responsabilités selon les composants ci-dessous
\begin{enumerate}
\item
Configurer les sérialiseurs, càd. les champs que nous souhaitons exposer au travers de l'API,
\item
Configurer les vues, càd le comportement de chacun des points de terminaison,
\item
Configurer les points de terminaison eux-mêmes, càd les URLs permettant d'accéder aux ressources.
\item
Et finalement ajouter quelques paramètres au niveau de notre application.
\end{enumerate}
\section{Django Rest Framework}
\subsection{Serialiseurs}
Les sérialiseurs agissent litérallement comme des \texttt{forms}, mais au niveau de l'API.
Ils se basent sur un modèle, définit au niveau de la \texttt{class Meta}, permettent de choisir les champs qui seront sérialisés, définissent différentes méthodes d'accès à des propriétés spécifiques et des méthodes de validation.
Tout comme les forms.
Par exemple, avec Django Rest Framework:
\begin{minted}{python}
# serializers.py
from django.contrib.auth.models import User, Group
from rest_framework import serializers
from .models import People, Contract, Service
class PeopleSerializer(serializers.HyperlinkedModelSerializer):
class Meta:
model = People
fields = ("last_name", "first_name", "contract_set")
class ContractSerializer(serializers.HyperlinkedModelSerializer):
class Meta:
model = Contract
fields = ("date_begin", "date_end", "service")
class ServiceSerializer(serializers.HyperlinkedModelSerializer):
class Meta:
model = Service
fields = ("name",)
\end{minted}
\subsection{Vues}
\begin{minted}{python}
# views.py
from django.contrib.auth.models import User, Group
from rest_framework import viewsets
from rest_framework import permissions
from .models import People, Contract, Service
from .serializers import PeopleSerializer, ContractSerializer, ServiceSerializer
class PeopleViewSet(viewsets.ModelViewSet):
queryset = People.objects.all()
serializer_class = PeopleSerializer
permission_class = [permissions.IsAuthenticated]
class ContractViewSet(viewsets.ModelViewSet):
queryset = Contract.objects.all()
serializer_class = ContractSerializer
permission_class = [permissions.IsAuthenticated]
class ServiceViewSet(viewsets.ModelViewSet):
queryset = Service.objects.all()
serializer_class = ServiceSerializer
permission_class = [permissions.IsAuthenticated]
\end{minted}
\subsection{URLs}
\begin{minted}{python}
# urls.py
from django.contrib import admin
from django.urls import path, include
from rest_framework import routers
from core import views
router = routers.DefaultRouter()
router.register(r"people", views.PeopleViewSet)
router.register(r"contracts", views.ContractViewSet)
router.register(r"services", views.ServiceViewSet)
urlpatterns = [
path("api/v1/", include(router.urls)),
path('admin/', admin.site.urls),
]
\end{minted}
\begin{minted}{python}
# settings.py
INSTALLED_APPS = [
...
"rest_framework",
...
]
...
REST_FRAMEWORK = {
'DEFAULT_PAGINATION_CLASS':
'rest_framework.pagination.PageNumberPagination',
'PAGE_SIZE': 10
}
\end{minted}
\subsection{Résultat}
En nous rendant sur l'URL \texttt{http://localhost:8000/api/v1}, nous obtiendrons ceci:
\includegraphics{images/rest/api-first-example.png}
\subsection{Modéles et relations}
Plus haut, nous avons utilisé une relation de type \texttt{HyperlinkedModelSerializer}. C'est une bonne manière pour autoriser des relations entre vos instances à partir de l'API, mais il faut reconnaître que cela reste assez limité. Pour palier à ceci, il existe {[}plusieurs manières de représenter ces
\url{https://www.django-rest-framework.org/api-guide/relations/}:
\begin{enumerate}
\item Soit \textbf{via} un hyperlien, comme ci-dessus,
\item Soit en utilisant les clés primaires, soit en utilisant l'URL canonique permettant d'accéder à la ressource.
\end{enumerate}
La solution la plus complète consiste à intégrer la relation directement au niveau des données sérialisées, ce qui nous permet de passer de ceci (au niveau des contrats):
\begin{minted}{js}
{
"count": 1,
"next": null,
"previous": null,
"results": [
{
"last_name": "Bond",
"first_name": "James",
"contract_set": [
"http://localhost:8000/api/v1/contracts/1/",
"http://localhost:8000/api/v1/contracts/2/"
]
}
]
}
\end{minted}
à ceci:
\begin{minted}{js}
{
"count": 1,
"next": null,
"previous": null,
"results": [
{
"last_name": "Bond",
"first_name": "James",
"contract_set": [
{
"date_begin": "2019-01-01",
"date_end": null,
"service": "http://localhost:8000/api/v1/services/1/"
},
{
"date_begin": "2009-01-01",
"date_end": "2021-01-01",
"service": "http://localhost:8000/api/v1/services/1/"
}
]
}
]
}
\end{minted}
La modification se limite à \textbf{surcharger} la propriété, pour
indiquer qu'elle consiste en une instance d'un des sérialiseurs
existants. Nous passons ainsi de ceci
\begin{minted}{python}
class ContractSerializer(serializers.HyperlinkedModelSerializer):
class Meta:
model = Contract
fields = ("date_begin", "date_end", "service")
class PeopleSerializer(serializers.HyperlinkedModelSerializer):
class Meta:
model = People
fields = ("last_name", "first_name", "contract_set")
\end{minted}
à ceci:
\begin{minted}{python}
class ContractSerializer(serializers.HyperlinkedModelSerializer):
class Meta:
model = Contract
fields = ("date_begin", "date_end", "service")
class PeopleSerializer(serializers.HyperlinkedModelSerializer):
contract_set = ContractSerializer(many=True, read_only=True)
class Meta:
model = People
fields = ("last_name", "first_name", "contract_set")
\end{minted}
Nous ne faisons donc bien que redéfinir la propriété \texttt{contract\_set} et indiquons qu'il s'agit à présent d'une instance de \texttt{ContractSerializer}, et qu'il est possible d'en avoir plusieurs. C'est tout.
\subsection{Conclusions}
Django-Rest-Framework est une librarie complète qui ajoute énormément de possibilités.
Cependant \cite{django_for_startup_founders}:
\begin{enumerate}
\item
La documentation est \textbf{réellement} compliquée.
Tout nouveau développeur doit appréhender, comprendre et assimiler cette documentation et tous les concepts sous-jacents.
Ceci inclut notamment le fait que tous les verbes HTTP ont été "traduits" (GET -> retrieve, POST -> create, ...).
Ceci a du sens par rapport à la définition d'une interface REST-compliant, mais ajoute une complexité mentale relativement lourde.
\item
Certains concepts de réutilisation sont tellement compliqués qu'ils prennent plus de temps à mettre en place qu'à écrire une ligne de code Python classique
\item
Les sérialiseurs peuvent rapidement devenir difficiles à lire ou relire, spécifiquement lorsque nous utilisons des \textit{nested serializers} ou lorsque les concepts de désérialisation sont abordés.
\end{enumerate}
\section{Marshmallow}
\textit{Marshmallow} est une alternative plus légère à Django-Rest-Framework, et qui présente une interface plus claire, ainsi qu'une documentation plus concise et facile à comprendre.
Une solution plus facile que les sérializeurs de DRF consistera à
\begin{enumerate}
\item
Gérer la validation de données en utilisant Marshmallow
\item
Sérialiser les données en utilisant du code Python \cite{django_for_startup_founders}.
\end{enumerate}
\section{Ninja}
...
\section{OData}
...
\section{Bonnes pratiques}
\subsection{Authentification}
\subsection{Validation des données}
\subsection{Utilisation d'une API Gateway}
\subsection{Rate limiting}
\subsection{Partage des données nécessaires uniquement}

957
chapters/architecture.tex

@ -0,0 +1,957 @@
\chapter{Eléments d'architecture}
\begin{quote}
Un code mal pensé entraîne nécessairement une perte d'énergie et de temps.
Il est plus simple de réfléchir, au moment de la conception du programme, à une architecture permettant une meilleure maintenabilité que de devoir corriger un code "sale" \emph{a posteriori}.
C'est pour aider les développeurs à rester dans le droit chemin que les principes SOLID ont été énumérés. \cite{gnu_linux_mag_hs_104}
\end{quote}
Les principes SOLID, introduit par Robert C. Martin dans les années 2000 pour orienter le développement de modules, sont les suivants:
\begin{enumerate}
\item
\textbf{SRP} - Single responsibility principle - Principe de Responsabilité Unique
\item
\textbf{OCP} - Open-closed principle
\item
\textbf{LSP} - Liskov Substitution
\item
\textbf{ISP} - Interface ségrégation principle
\item
\textbf{DIP} - Dependency Inversion Principle
\end{enumerate}
Des équivalents à ces directives existent au niveau des composants, puis au niveau architectural:
\begin{enumerate}
\item
Reuse/release équivalence principle,
\item
\textbf{CCP} - Common Closure Principle,
\item
\textbf{CRP} - Common Reuse Principle.
\end{enumerate}
\begin{figure}[H]
\centering
\scalebox{1.0}{\includegraphics[max size={\textwidth}{\textheight}]{images/arch-comp-modules.png}}
\end{figure}
\section{Modules}
\subsection{Single Responsility Principle} \label{SRP}
Le principe de responsabilité unique conseille de disposer de concepts ou domaines d'activité qui ne s'occupent chacun que d'une et une seule chose.
Ceci rejoint (un peu) la \href{https://en.wikipedia.org/wiki/Unix_philosophy}{Philosophie Unix}, documentée par Doug McIlroy et qui demande de "\emph{faire une seule chose, mais de le faire bien}" \cite{unix_philosophy}.
Selon ce principe, une classe ou un élément de programmation ne doit donc pas avoir plus d'une seule raison de changer.
Plutôt que de centraliser le maximum de code à un seul endroit ou dans une seule classe par convenance ou commodité \footnote{Aussi appelé \emph{God-Like object}}, le principe de responsabilité unique suggère que chaque classe soit responsable d'un et un seul concept.
Une manière de voir les choses consiste à différencier les acteurs ou les intervenants: imaginez disposer d'une classe représentant des données de membres du personnel; ces données pourraient être demandées par trois acteurs:
\begin{enumerate}
\item Le CFO (Chief Financial Officer)
\item Le CTO (Chief Technical Officer)
\item Le COO (Chief Operating Officer)
\end{enumerate}
Chacun d'entre eux aura besoin de données et d'informations relatives à ces membres du personnel, et provenant donc d'une même source de données centralisée.
Mais chacun d'entre eux également besoin d'une représentation différente ou de traitements distincts. \cite{clean_architecture}
Nous sommes d'accord qu'il s'agit à chaque fois de données liées aux employés; celles-ci vont cependant un cran plus loin et pourraient nécessiter des ajustements spécifiques en fonction de l'acteur concerné et de la manière dont il souhaite disposer des données.
Dès que possible, identifiez les différents acteurs et demandeurs, en vue de prévoir les modifications qui pourraient être demandées par l'un d'entre eux.
Dans le cas d'un élément de code centralisé, une modification induite par un des acteurs pourrait ainsi avoir un impact sur les données utilisées par les autres.
Vous trouverez ci-dessous une classe \texttt{Document}, dont chaque instance est représentée par trois propriétés: son titre, son contenu et sa date de publication.
Une méthode \texttt{render} permet également de proposer (très grossièrement) un type de sortie et un format de contenu: \texttt{XML} ou \texttt{Markdown}.
\begin{listing}[H]
\begin{minted}[tabsize=4]{Python}
class Document:
def __init__(self, title, content, published_at):
self.title = title
self.content = content
self.published_at = published_at
def render(self, format_type):
if format_type == "XML":
return """<?xml version = "1.0"?>
<document>
<title>{}</title>
<content>{}</content>
<publication_date>{}</publication_date>
</document>""".format(
self.title,
self.content,
self.published_at.isoformat()
)
if format_type == "Markdown":
import markdown
return markdown.markdown(self.content)
raise ValueError(
"Format type '{}' is not known".format(format_type)
)
\end{minted}
\caption{Un convertisseur de document un peu bateau}
\end{listing}
Lorsque nous devrons ajouter un nouveau rendu (Atom, OpenXML, \ldots) il sera nécessaire de modifier la classe \texttt{Document}.
Ceci n'est:
\begin{enumerate}
\item Ni intuitif: \emph{ce n'est pas le document qui doit savoir dans quels formats il peut être converti}
\item Ni conseillé: \emph{lorsque nous aurons quinze formats différents à gérer, il sera nécessaire d'avoir autant de conditions dans cette méthode}.
\end{enumerate}
En suivant le principe de responsabilité unique, une bonne pratique consiste à créer une nouvelle classe de rendu pour chaque type de format à gérer:
\begin{listing}[H]
\begin{minted}[tabsize=4]{Python}
class Document:
def __init__(self, title, content, published_at):
self.title = title
self.content = content
self.published_at = published_at
class DocumentRenderer:
def render(self, document):
if format_type == "XML":
return """<?xml version = "1.0"?>
<document>
<title>{}</title>
<content>{}</content>
<publication_date>{}</publication_date>
</document>""".format(
self.title,
self.content,
self.published_at.isoformat()
)
if format_type == "Markdown":
import markdown
return markdown.markdown(self.content)
raise ValueError(