diff --git a/chapters/contexts-processors.tex b/chapters/contexts-processors.tex index 8a29dc9..6acefbe 100644 --- a/chapters/contexts-processors.tex +++ b/chapters/contexts-processors.tex @@ -3,6 +3,17 @@ Mise en pratique: un \emph{context processor} sert \emph{grosso-modo} à peupler Un context processor est un peu l'équivalent d'un middleware, mais entre les données et les templates, là où le middleware va s'occuper des données relatives aux réponses et requêtes elles-mêmes. Un \emph{context processor} sert \emph{grosso-modo} à peupler l'ensemble des données transmises des vues aux templates avec des données communes. Un context processor est un peu l'équivalent d'un middleware, mais est situé entre les données et les templates, là où le middleware va s'occuper des données relatives aux réponses et requêtes elles-mêmes. +Nous vous proposons deux exemples ci-dessous: + +\begin{enumerate} + \item + Le premier récupère le numéro de version de \texttt{Git}. + Ce n'est sans doute pas le plus adéquat à réaliser en production, puisqu'il suffirait que Git ne soit pas installé, que le code ait été déployé d'une autre manière ou d'un soucis de performances. + \item + Le second qui récupère la liste des catégories et sous-catégories d'une base de données fictives. + Ces deux informations pourraient être affichées pour chaque utilisateur et chaque requête faite vers l'application. + Dans l'immédiat, ce n'est pas trop lourd, mais vu que ces informations ne dépendent pas de l'utilisateur, nous pouvons les glisser dans le cache pour faire gagner énormément de temps. +\end{enumerate} \begin{minted}{python} # core/context_processors.py @@ -16,14 +27,32 @@ Un \emph{context processor} sert \emph{grosso-modo} à peupler l'ensemble des do ["git", "describe", "--always"] ).strip(), "git_date": subprocess.check_output( - ["git", "show", "-s", r"--format=%cd", r"--date=format:%d-%m-%Y"] + [ + "git", + "show", + "-s", + r"--format=%cd", + r"--date=format:%d-%m-%Y" + ] ), } \end{minted} - Ceci aura pour effet d'ajouter les deux variables \texttt{git\_describe} et \texttt{git\_date} dans tous les contextes de tous les templates de l'application. +\begin{minted}{python} + from product.models import SubCategory, Category + + + def add_variable_to_context(request): + return { + 'subCategories': SubCategory.objects.order_by('id').all(), + 'categories': Category.objects.order_by("id").all(), + } +\end{minted} + +Il convient ensuite d'ajouter ces \textit{context processors} au niveau des options de configuration des templates: + \begin{minted}{python} TEMPLATES = [ { @@ -32,11 +61,14 @@ Ceci aura pour effet d'ajouter les deux variables \texttt{git\_describe} et \tex 'APP_DIRS': True, 'OPTIONS': { 'context_processors': [ - 'django.template.context_processors.debug', - 'django.template.context_processors.request', - 'django.contrib.auth.context_processors.auth', - 'django.contrib.messages.context_processors.messages', - "core.context_processors.git_describe" + "django.template.context_processors.debug", + "django.template.context_processors.request", + "django.contrib.auth.context_processors.auth", + "django.contrib.messages.context_processors.messages", + "..." + "core.context_processors.git_describe", + "core.context_processors.add_variable_to_context", + "...", ], }, }, diff --git a/chapters/debian.tex b/chapters/debian.tex index f5cc235..95de2e5 100644 --- a/chapters/debian.tex +++ b/chapters/debian.tex @@ -498,6 +498,7 @@ server { \subsubsection{Let's Encrypt} +Certificats externes + communication par socket interne. \section{Mise à jour} diff --git a/chapters/gdpr.tex b/chapters/gdpr.tex new file mode 100644 index 0000000..390f2dc --- /dev/null +++ b/chapters/gdpr.tex @@ -0,0 +1,34 @@ +\chapter{Politique de protection des données} + +\begin{quote} + Le règlement général sur la protection des données (RGPD) responsabilise les organismes publics et privés qui traitent leurs données. + Vous collectez ou traitez des données personnelles ? Adoptez les bons réflexes ! + + -- \url{https://www.cnil.fr/fr/comprendre-le-rgpd} +\end{quote} + +Par exemple: https://discord.com/privacy + +\url{https://www.cnil.fr/sites/default/files/atoms/files/bpi-cnil-rgpd_guide-tpe-pme.pdf} + +\section{TL;DR} + +Aka "Les six bons conseils" de la CNIL: + +\begin{enumerate} + \item \textbf{Ne collectez que les données vraiment nécessaires} + \item \textbf{Soyez transparents} + \item \textbf{Pensez au droit des personnes} + \item \textbf{Gardez la maîtrise de vos données} + \item \textbf{Identifiez les risques} + \item \textbf{Sécurisez vos données} +\end{enumerate} + +\section{Donnée personnelle, traitement de données} + + +\subsection{Notions clés} + + +\section{Les bons réflexes} + diff --git a/chapters/infrastructure.tex b/chapters/infrastructure.tex index 35bec11..b3e8837 100644 --- a/chapters/infrastructure.tex +++ b/chapters/infrastructure.tex @@ -148,37 +148,8 @@ Au niveau logiciel (la partie mise en subrillance ci-dessus), la requête arrive \subsubsection{Supervision des processus} -La récupération de métriques augmente la confiance que l'on peut avoir dans la solution. -L'analyse de ces métriques garantit un juste retour d'informations, sous réserve qu'elles soient exploitées correctement. -La première étape consiste à agréger ces données dans un dépôt centralisé, tandis que la seconde étape exigera une structuration correcte des données envoyées. +https://circus.readthedocs.io/en/latest/, https://uwsgi-docs.readthedocs.io/en/latest/, statsd -La collecte des données doit récupérer des données des couches métiers, applicatives et d'environnement. -Ces données couvrent les évènements, les journaux et les métriques - indépendamment de leur source - le pourcentage d'utilisation du processeur, la mémoire utilisée, les disques systèmes, l'utilisation du réseau, \ldots - -\begin{enumerate} - \item - \textbf{Métier}: Le nombre de ventes réalisées, le nombre de nouveaux utilisateurs, les résultats de tests A/B, \ldots - \item - \textbf{Application}: Le délai de réalisation d'une transaction, le temps de réponse par utilisateur, \ldots - \item - \textbf{Infrastructure}: Le trafic du serveur Web, le taux d'occupation du CPU, \ldots - \item - \textbf{Côté client}: Les erreurs applicatives, les transactions côté utilisateur, \ldots - \item - \textbf{Pipeline de déploiement}: Statuts des builds, temps de mise à disposition d'une fonctionnalité, fréquence des déploiements, statuts des environnements, \ldots -\end{enumerate} - -Bien utilisés, ces outils permettent de prévenir des incidents de manière empirique. - -\begin{quote} - Monitoring is so important that our monitoring systems need to be more available and scalable than the systems being monitored. - - -- Adrian Cockcroft \cite[p. 200]{devops_handbook} -\end{quote} - -Histoire de schématiser, toute équipe se retrouve à un moment où un autre dans la situation suivante: personne ne veut appuyer sur le gros bouton rouge issu de l'automatisation de la chaîne de production et qui dit "Déploiement". -Et pour cause: une fois que nous aurons trouvé une joyeuse victime qui osera braver le danger, il y aura systématiquement des imprévus, des petits détails qui auront été oubliés sur le côté de la route, et qui feront lâchement planter les environnements. -Et puisque nous n'aurons pas (encore) de télémétrie, le seul moment où nous comprendrons qu'il y a un problème, sera quand un utilisateur viendra se plaindre. \subsubsection{Journaux} @@ -246,24 +217,65 @@ Pour utiliser nos loggers, il suffit de copier le petit bout de code suivant: \href{https://docs.djangoproject.com/en/stable/topics/logging/\#examples}{Par exemples}. -\subsubsection{Exploitation des journaux} +\subsubsection{Exploitation des journaux, télémétrie} + +Des erreurs sur un environnement de production arriveront, tôt ou tard, et seront sans doute plus compliquée à corriger qu'un morceau de code dans un coin du code. +L'exploitation des journaux permettra de comprendre, analyser, voire corriger, certains incidents. + +Comme nous l'avons vu, en fonction de l'infrastructure choisie, il existe plusieurs types de journaux: \begin{enumerate} - \item - Sentry via sentry\_sdk - \item - Nagios - \item - LibreNMS - \item - Zabbix + \item Les journaux applicatifs: ie. le flux d'évènements généré par votre application Django + \item Les journaux du serveur: Nginx, Gunicorn, \ldots + \item Les journaux des autres composants: base de données, service de mise en cache, ... \end{enumerate} +Une manière de faire consiste à se connecter physiquement ou à distance à la machine pour analyser les logs. +En pratique, c'est impossible: entre les répartiteurs de charge, les différents serveurs, \ldots, il vous sera physiquement impossible de récupérer une information cohérente. +La solution consiste à agréger vos journaux à un seul et même endroit: + +\includegraphics{images/infrastructure/mattsegal-logging.png} + +CC https://mattsegal.dev/django-monitoring-stack.html + +\subsubsection{Sumologic} + +\subsubsection{Alternatives} + Il existe également \href{https://munin-monitoring.org}{Munin}, \href{https://www.elastic.co}{Logstash, ElasticSearch et Kibana (ELK-Stack)} ou \href{https://www.fluentd.org}{Fluentd}. +La récupération de métriques augmente la confiance que l'on peut avoir dans la solution. +L'analyse de ces métriques garantit un juste retour d'informations, sous réserve qu'elles soient exploitées correctement. +La première étape consiste à agréger ces données dans un dépôt centralisé, tandis que la seconde étape exigera une structuration correcte des données envoyées. -\subsubsection{Zabbix, Nagios, \ldots} +La collecte des données doit récupérer des données des couches métiers, applicatives et d'environnement. +Ces données couvrent les évènements, les journaux et les métriques - indépendamment de leur source - le pourcentage d'utilisation du processeur, la mémoire utilisée, les disques systèmes, l'utilisation du réseau, \ldots -\subsubsection{Sentry} +\begin{enumerate} + \item + \textbf{Métier}: Le nombre de ventes réalisées, le nombre de nouveaux utilisateurs, les résultats de tests A/B, \ldots + \item + \textbf{Application}: Le délai de réalisation d'une transaction, le temps de réponse par utilisateur, \ldots + \item + \textbf{Infrastructure}: Le trafic du serveur Web, le taux d'occupation du CPU, \ldots + \item + \textbf{Côté client}: Les erreurs applicatives, les transactions côté utilisateur, \ldots + \item + \textbf{Pipeline de déploiement}: Statuts des builds, temps de mise à disposition d'une fonctionnalité, fréquence des déploiements, statuts des environnements, \ldots +\end{enumerate} -\subsubsection{Greylog} +Bien utilisés, ces outils permettent de prévenir des incidents de manière empirique. + +\begin{quote} + Monitoring is so important that our monitoring systems need to be more available and scalable than the systems being monitored. + + -- Adrian Cockcroft \cite[p. 200]{devops_handbook} +\end{quote} + +Histoire de schématiser, toute équipe se retrouve à un moment où un autre dans la situation suivante: personne ne veut appuyer sur le gros bouton rouge issu de l'automatisation de la chaîne de production et qui dit "Déploiement". +Et pour cause: une fois que nous aurons trouvé une joyeuse victime qui osera braver le danger, il y aura systématiquement des imprévus, des petits détails qui auront été oubliés sur le côté de la route, et qui feront lâchement planter les environnements. +Et puisque nous n'aurons pas (encore) de télémétrie, le seul moment où nous comprendrons qu'il y a un problème, sera quand un utilisateur viendra se plaindre. + +\subsubsection{Statsd} + +https://www.datadoghq.com/blog/statsd/ diff --git a/chapters/python.tex b/chapters/python.tex index c7217b7..876e155 100644 --- a/chapters/python.tex +++ b/chapters/python.tex @@ -355,10 +355,15 @@ en apposant un \emph{tag} indiquant que vous avez pris connaissance de la remarque, que vous en avez tenu compte, et que vous choisissez malgré tout de faire autrement. +\begin{listing}[H] + \includegraphics{images/calvin/time-machine.jpg} + \caption{Calvin \& Hobbes se rendent dans le futur pour récupérer les devoirs que son moi-du-futur aura fait (et pour ne pas avoir à les faire lui-même).} +\end{listing} + Pour vous donner une idée, voici ce que cela pourrait donner avec un code pas très propre et qui ne sert à rien: -\begin{listing}[!ht] +\begin{listing}[H] \begin{minted}{python} from datetime import datetime """On stocke la date du jour dans la variable ToD4y""" diff --git a/chapters/security.tex b/chapters/security.tex index 542b1cd..107c221 100644 --- a/chapters/security.tex +++ b/chapters/security.tex @@ -22,3 +22,6 @@ Il conviendra d'ajouter au dépôt de source: Au niveau du dépôt de sources, i \item Les paquets à utiliser et à compiler (NTP pour synchroniser les horloges, les paramètres d'OpenSSL, \ldots), les configurations d'Nginx/Apache, \ldots \end{enumerate} + +\section{Certificats} + diff --git a/chapters/templates.tex b/chapters/templates.tex index ac10615..7631984 100644 --- a/chapters/templates.tex +++ b/chapters/templates.tex @@ -51,7 +51,7 @@ En reprenant l'exemple de la page HTML définie ci-dessus, nous pouvons l'agrém \scalebox{1.0}{\includegraphics[max size={\textwidth}{\textheight}]{images/html/my-first-wishlists.png}} \end{figure} -\section{Entête, héritage et extensions} +\section{Entête, héritage} Plutôt que de réécrire à chaque fois le même entête, nous pouvons nous simplifier la vie en implémentant un héritage au niveau des templates. Pour cela, il suffit de définir des blocs de contenu, et d'\emph{étendre} une page de base, puis de surcharger ces mêmes blocs. @@ -93,9 +93,166 @@ Nous avons à présent un bloc \texttt{title} et un bloc \texttt{body}, qui peuv {% endblock% %} \end{minted} +\section{Extensions} + +Attention: il est primordial que les extensions/tags ne fassent aucune requête en base de données. +Il est vraiment important que les données (re)travaillées soient disponibles dès que l'appel sera réalisé, sans quoi cela dégradera énormément les performances à l'affichage de la page. + +Chronologie T -> T+1 -> T+2 +Données affichées: X -> X, Y -> X,Y,Z +Et ça sera dégueulasse. + +\subsection{Extensions natives} + +Django vient avec un ensemble de *tags* ou *template tags*. +On a vu la boucle \texttt{for} ci-dessus, mais il existe \href{https://docs.djangoproject.com/fr/1.9/ref/templates/builtins/}{beaucoup d'autres tags nativement présents}. +Les principaux sont par exemple: + +\begin{itemize} + \item Les conditions, qui permettent de vérifier un contexte et de n'afficher le contenu d'un bloc que si la condition est vérifiée + \begin{itemize} + \item \texttt{if} \ldots \texttt{elif} \ldots \texttt{endif} + \end{itemize} + \item Les opérateurs de comparaisons: + \begin{itemize} + \item \texttt{<} + \item \texttt{>} + \item \texttt{==} + \item \texttt{in} + \item \texttt{not in} + \end{itemize} +\end{itemize} + + + Regroupements avec le tag `{% regroup ... by ... as ... %}`. +* `{% url %}` pour construire facilement une URL à partir de son nom +* `urlize` qui permet de remplacer des URLs trouvées dans un champ de type CharField ou TextField par un lien cliquable. +* ... + +Chacune de ces fonctions peut être utilisée autant au niveau des templates qu'au niveau du code. +Il suffit d'aller les chercher dans le package \texttt{django.template.defaultfilters}. +Par exemple: + +\begin{minted}{python} + from django.db import models + from django.template.defaultfilters import urlize + + + class Suggestion(models.Model): + """Représentation des suggestions. + """ + subject = models.TextField(verbose_name="Sujet") + + def urlized_subject(self): + """ + Voir https://docs.djangoproject.com/fr/3.0/howto/custom-template-tags/ + """ + return urlize(self.subject, autoescape=True) +\end{minted} + +\subsubsection{Regroup by} + +\subsubsection{Inclusion} + +\subsection{Extensions non-natives} + +En plus des quelques tags survolés ci-dessus, il est également possible de construire ses propres tags. La structure est un peu bizarre, car elle consiste à ajouter un paquet dans une de vos applications, à y définir un nouveau module et à y définir un ensemble de fonctions. +Chacune de ces fonctions correspondra à un tag appelable depuis vos templates. + +Il existe trois types de tags *non-builtins*: + +\begin{enumerate} + \item *Les filtres* - on peut les appeler grâce au *pipe* `|` directement après une valeur dans le template. + \item *Les tags simples* - ils peuvent prendre une valeur ou plusieurs en paramètre et retourne une nouvelle valeur. Pour les appeler, c'est *via* les tags \texttt{\{\% nom\_de\_la\_fonction param1 param2 \ldots \%\}}. + \item *Les tags d'inclusion*: ils retournent un contexte (ie. un dictionnaire), qui est ensuite passé à un nouveau template. Type \texttt{\{\% include '...' ... \%\}}. +\end{enumerate} + +Pour l'implémentation: + + 1. On prend l'application `wish` et on y ajoute un répertoire `templatetags`, ainsi qu'un fichier `\_\_init\_\_.py`. + 2. Dans ce nouveau paquet, on ajoute un nouveau module que l'on va appeler `tools.py` + 3. Dans ce module, pour avoir un aperçu des possibilités, on va définir trois fonctions (une pour chaque type de tags possible). + +\subsubsection{Filtres} + +\begin{minted}{python} + # wish/tools.py + + from django import template + + from wish.models import Wishlist + + register = template.Library() + + @register.filter(is_safe=True) + def add_xx(value): + return '%sxx' % value +\end{minted} + +\subsubsection{Tags simples} + +Un \textbf{tag simple} reçoit une valeur ou un objet en entrée et génère une valeur de retour simple (un objet, un type natif, ...). + +\begin{minted}{python} + # wish/tools.py + + from django import template + + from wish.models import Wishlist + + + register = template.Library() + + + @register.simple_tag + def current_time(format_string): + return datetime.datetime.now().strftime(format_string) +\end{minted} + +\subsubsection{Tags d'inclusion} + +Les \textit{tags d'inclusion} sont des tags associés à un (morceau de) template. +C'est-à-dire qu'une fois qu'ils auront réalisés le traitement qui leur est demandé, il généreront un canevas HTML qui sera \textbf{inclus} à l'endroit où le tag aura été appelé. + +\begin{minted}{python} + # wish/tools.py + + from django import template + + from wish.models import Wishlist + + + register = template.Library() + + + @register.inclusion_tag('wish/templatetags/wishlists_list.html') + def wishlists_list(): + return { 'list': Wishlist.objects.all() } +\end{minted} + +\subsection{Pagination} + \section{Structure et configuration} Il est conseillé que les templates respectent la structure de vos différentes applications, mais dans un répertoire à part. Par convention, nous les placerons dans un répertoire \texttt{templates}. La hiérarchie des fichiers devient alors celle-ci: +Par défaut, Django cherchera les templates dans les répertoirer d'installation. +Vous devrez vous éditer le fichier \texttt{gwift/settings.py} et ajouter, dans la variable \texttt{TEMPLATES}, la clé \texttt{DIRS} de la manière suivante: + +\begin{minted}{python} + TEMPLATES = [ + { + ... + 'DIRS': [ 'templates' ], + ... + }, + ] +\end{minted} + +\subsection{Fichiers statiques} + +\section{Dynamisme - HTMX} + + diff --git a/chapters/urls.tex b/chapters/urls.tex index 4db51a6..36fd032 100644 --- a/chapters/urls.tex +++ b/chapters/urls.tex @@ -1,7 +1,9 @@ \chapter{URLs et espaces de noms} +La gestion des URLs consiste \textbf{grosso modo} à assigner un chemin à une fonction Python. + +\section{Configuration et correspondances} -La gestion des URLs permet \textbf{grosso modo} d'assigner une adresse paramétrée ou non à une fonction Python. La manière simple consiste à modifier le fichier \texttt{gwift/settings.py} pour y ajouter nos correspondances. Par défaut, le fichier ressemble à ceci: @@ -16,7 +18,6 @@ urlpatterns = [ ] \end{minted} - La variable \texttt{urlpatterns} associe un ensemble d'adresses à des fonctions. Dans le fichier \textbf{nu}, seul le \textbf{pattern} \texttt{admin} est défini, et inclut toutes les adresses qui sont définies dans le fichier \texttt{admin.site.urls}. @@ -60,8 +61,9 @@ Note sur les namespaces. De là, découle une autre bonne pratique: l'utilisation de \emph{breadcrumbs} (\url{https://stackoverflow.com/questions/826889/how-to-implement-breadcrumbs-in-a-django-template}) ou de guidelines de navigation. +\section{Tests} -\section{Reverse} +\subsection{Reverse} En associant un nom ou un libellé à chaque URL, il est possible de récupérer sa \textbf{traduction}. Cela implique par contre de ne plus toucher à ce libellé par la suite\ldots\hspace{0pt} @@ -88,4 +90,10 @@ De la même manière, on peut également récupérer l'URL de destination pour n from django.core.urlresolvers import reverse_lazy wishlists_url = reverse_lazy('wishlists') -\end{minted} \ No newline at end of file +\end{minted} + +\subsection{Resolve} + +La résolution d'une adresse consiste à retrouver la fonction à partir d'une URL. +Si nous donnons le chemin \texttt{/wishlists/wish/123}, nous nous attendons à recevoir la fonction en retour. +Au niveau des tests, cela permettra de nous assurer que c'est la bonne fonction qui est récupérée par une adresse connue. diff --git a/chapters/views.tex b/chapters/views.tex index 457c8da..0af0ca2 100644 --- a/chapters/views.tex +++ b/chapters/views.tex @@ -1,2 +1,11 @@ \chapter{Vues} +Les vues sont la plaque tournantes entre nos données (\textit{via} le modèle) et le rendus d'un template. + +\section{Fonctions}\label{FBV} + +Les fonctions + + +\section{Classes}\label{CBV} + diff --git a/images/calvin-hobbes-time-machine.jpg b/images/calvin/time-machine.jpg similarity index 100% rename from images/calvin-hobbes-time-machine.jpg rename to images/calvin/time-machine.jpg diff --git a/images/infrastructure/mattsegal-logging.png b/images/infrastructure/mattsegal-logging.png new file mode 100644 index 0000000..66ac174 Binary files /dev/null and b/images/infrastructure/mattsegal-logging.png differ diff --git a/main.tex b/main.tex index dcd73b2..a99a053 100644 --- a/main.tex +++ b/main.tex @@ -36,11 +36,6 @@ \include{chapters/introduction.tex} \tableofcontents -\listoffigures -\listoftables -%\listoflistings -\clearpage -\addcontentsline{toc}{chapter}{\listlistingname}{\listoflistings} \mainmatter @@ -85,6 +80,7 @@ \include{chapters/trees.tex} \include{chapters/i18n.tex} \include{chapters/deployment-processes.tex} +\include{chapters/gdpr.tex} \chapter{Conclusions} @@ -108,6 +104,12 @@ \include{resources.tex} +\listoffigures +\listoftables +%\listoflistings +\clearpage +\addcontentsline{toc}{chapter}{\listlistingname}{\listoflistings} + \include{chapters/thanks.tex} \end{document} diff --git a/source/part-2-deployment/logging.adoc b/source/part-2-deployment/logging.adoc index a631c84..a73c45e 100644 --- a/source/part-2-deployment/logging.adoc +++ b/source/part-2-deployment/logging.adoc @@ -65,3 +65,5 @@ logger.debug('helloworld') ---- https://docs.djangoproject.com/en/stable/topics/logging/#examples[Par exemples]. + +Nous verrons plus loin (cf. ) comment configurer gunicorn pour intercepter correctement toutes ces informations. diff --git a/source/part-3-data-model/templates.adoc b/source/part-3-data-model/templates.adoc index fd36456..c6eb9ba 100644 --- a/source/part-3-data-model/templates.adoc +++ b/source/part-3-data-model/templates.adoc @@ -5,27 +5,6 @@ ==== Répertoires de découverte des templates -[source,bash] ----- -$ tree templates/ -templates/ -└── wish - └── list.html ----- - -Par défaut, Django cherchera les templates dans les répertoirer d'installation. -Vous devrez vous éditer le fichier `gwift/settings.py` et ajouter, dans la variable `TEMPLATES`, la clé `DIRS` de la manière suivante: - -[source,python] ----- -TEMPLATES = [ - { - ... - 'DIRS': [ 'templates' ], - ... - }, -] ----- ==== Fichiers statiques @@ -33,34 +12,11 @@ TEMPLATES = [ === Builtins -Django vient avec un ensemble de *tags* ou *template tags*. On a vu la boucle `for` ci-dessus, mais il existe https://docs.djangoproject.com/fr/1.9/ref/templates/builtins/[beaucoup d'autres tags nativement présents]. Les principaux sont par exemple: - -* `{% if ... %} ... {% elif ... %} ... {% else %} ... {% endif %}`: permet de vérifier une condition et de n'afficher le contenu du bloc que si la condition est vérifiée. -* Opérateurs de comparaisons: `<`, `>`, `==`, `in`, `not in`. -* Regroupements avec le tag `{% regroup ... by ... as ... %}`. -* `{% url %}` pour construire facilement une URL à partir de son nom -* `urlize` qui permet de remplacer des URLs trouvées dans un champ de type CharField ou TextField par un lien cliquable. -* ... - -Chacune de ces fonctions peut être utilisée autant au niveau des templates qu'au niveau du code. Il suffit d'aller les chercher dans le package `django.template.defaultfilters`. Par exemple: [source,python] ---- -from django.db import models -from django.template.defaultfilters import urlize -class Suggestion(models.Model): - """Représentation des suggestions. - """ - subject = models.TextField(verbose_name="Sujet") - - def urlized_subject(self): - """ - Voir https://docs.djangoproject.com/fr/3.0/howto/custom-template-tags/ - """ - return urlize(self.subject, autoescape=True) - ---- ==== Regroup By @@ -69,19 +25,7 @@ class Suggestion(models.Model): === Non-builtins -En plus des quelques tags survolés ci-dessus, il est également possible de construire ses propres tags. La structure est un peu bizarre, car elle consiste à ajouter un paquet dans une de vos applications, à y définir un nouveau module et à y définir un ensemble de fonctions. Chacune de ces fonctions correspondra à un tag appelable depuis vos templates. -Il existe trois types de tags *non-builtins*: - -1. *Les filtres* - on peut les appeler grâce au *pipe* `|` directement après une valeur dans le template. -2. *Les tags simples* - ils peuvent prendre une valeur ou plusieurs en paramètre et retourne une nouvelle valeur. Pour les appeler, c'est *via* les tags `{% nom_de_la_fonction param1 param2 ... %}`. -3. *Les tags d'inclusion*: ils retournent un contexte (ie. un dictionnaire), qui est ensuite passé à un nouveau template. Type `{% include '...' ... %}`. - -Pour l'implémentation: - - 1. On prend l'application `wish` et on y ajoute un répertoire `templatetags`, ainsi qu'un fichier `__init__.py`. - 2. Dans ce nouveau paquet, on ajoute un nouveau module que l'on va appeler `tools.py` - 3. Dans ce module, pour avoir un aperçu des possibilités, on va définir trois fonctions (une pour chaque type de tags possible). [source,bash] ---- @@ -91,62 +35,6 @@ Pour l'implémentation: Pour plus d'informations, la https://docs.djangoproject.com/en/stable/howto/custom-template-tags/#writing-custom-template-tags[documentation officielle est un bon début]. -==== Filtres - -[source,python] ----- -# wish/tools.py - -from django import template - -from wish.models import Wishlist - -register = template.Library() - -@register.filter(is_safe=True) -def add_xx(value): - return '%sxx' % value ----- - - -==== Tags simples - -[source,python] ----- -# wish/tools.py - -from django import template - -from wish.models import Wishlist - - -register = template.Library() - - -@register.simple_tag -def current_time(format_string): - return datetime.datetime.now().strftime(format_string) ----- - - -==== Tags d'inclusion - -[source,python] ----- -# wish/tools.py - -from django import template - -from wish.models import Wishlist - - -register = template.Library() - - -@register.inclusion_tag('wish/templatetags/wishlists_list.html') -def wishlists_list(): - return { 'list': Wishlist.objects.all() } ----- === Contexts Processors @@ -160,14 +48,7 @@ L'idée est d'ajouter une fonction à un module Python à notre projet, puis de [source,python] ---- -from product.models import SubCategory, Category - -def add_variable_to_context(request): - return { - 'subCategories': SubCategory.objects.order_by('id').all(), - 'categories': Category.objects.order_by("id").all(), - } ---- [source,python] @@ -175,7 +56,7 @@ def add_variable_to_context(request): 'OPTIONS': { 'context_processors': [ .... - 'core.context_processors.add_variable_to_context', + .... ], }, diff --git a/source/part-4-services-oriented-applications/localization.adoc b/source/part-4-services-oriented-applications/localization.adoc deleted file mode 100644 index da47307..0000000 --- a/source/part-4-services-oriented-applications/localization.adoc +++ /dev/null @@ -1,9 +0,0 @@ -== i18n / l10n - -La localisation (_l10n_) et l'internationalization (_i18n_) sont deux concepts proches, mais différents: - -* Internationalisation: _Preparing the software for localization. Usually done by developers._ -* Localisation: _Writing the translations and local formats. Usually done by translators._ - -L'internationalisation est donc le processus permettant à une application d'accepter une forme de localisation. -La seconde ne va donc pas sans la première, tandis que la première ne fait qu'autoriser la seconde.