Corrections de texte, ajouts minimes, ... Et Intégration de Calvin & Hobbes !
continuous-integration/drone/push Build is failing Details

This commit is contained in:
Fred Pauchet 2022-05-18 21:52:38 +02:00
parent 8ad6e92bc5
commit 9e8b04ef5e
15 changed files with 326 additions and 189 deletions

View File

@ -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",
"...",
],
},
},

View File

@ -498,6 +498,7 @@ server {
\subsubsection{Let's Encrypt}
Certificats externes + communication par socket interne.
\section{Mise à jour}

34
chapters/gdpr.tex Normal file
View File

@ -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}

View File

@ -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/

View File

@ -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"""

View File

@ -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}

View File

@ -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}

View File

@ -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}
\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.

View File

@ -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}

View File

Before

Width:  |  Height:  |  Size: 57 KiB

After

Width:  |  Height:  |  Size: 57 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 118 KiB

View File

@ -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}

View File

@ -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.

View File

@ -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',
....
],
},

View File

@ -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.