Retrouvez tous les articles liés au tag Optimisation via le
flux rss dédié à ce tag.
Sur ma liste des choses à faire, il y avait cette migration concrète de mon site&blog vers Django 1.3. Il faut savoir que j'ai développé à l'origine avec la version 1.1, en ajoutant à l'occasion quelques petites choses de Django 1.2 lorsque j'ai mis à jour mon serveur.
Aujourd'hui, mes dernières applications sont développées avec Django 1.3, et il y a quelques petites choses en plus que j'apprécie bien. Je vous propose donc un retour d'expérience, avec un tour d'horizon de ce que j'ai modifié et eu à modifier.
Nommer les urls
Je n'avais pas touché à mon fichier urls.py depuis longtemps, et j'ai donc commencé par là : remettre certains trucs à plat, et rendre le tout plus propre et plus facile à maintenir.
Premier truc : utiliser la fonction url, qui permet entre autre chose de nommer ses urls, ce qui est très pratiques pour la suite, comme, par exemple, ne pas s'embêter à écrire des urls en dur dans les templates.
Exemple de template (avec Django 1.3) :
{% load url from future %}
<p>Lire l'article : <a href="{% url 'entry' entry.url %}">{{entry.title}}</a></p>
Vous noterez l'usage de {% load url from future %}, car le template-tag url a un comportement modifié pour les futures version de Django.
Du côté de la view, cela permet aussi de se simplifier la vie :
def redirect_to_tag(request, section_url):
"""Section now redirect to tag."""
return redirect('tag', permanent=True, tag_url=section_url)
J'ai simplement nommé l'une de mes urls "tag", et la fonction redirect s'occupe du reste. Si je change la forme des url des tags, je n'aurai pas à changer le code de cette view.
Séparer media et static
Apparu avec Django 1.3, la gestion des fichiers "statiques" a été grandement amélioré par l'intégration de django-staticfiles directement dans le projet Django en tant que django.contrib.staticfiles.
J'ai pu découvrir son fonctionnement avec mon dernier projet (un truc secret pour le moment...), et c'est vraiment très pratique. Le principe ? Au lieu d'avoir un répertoire de médias, intégrant les fichiers de l'application (css, js, etc.) et les fichiers téléversés par les utilisateurs, il y a maintenant deux espaces différents.
Je ne vais pas détailler ici tout ce que j'ai eu à faire mais voici un rapide résumé :
- ajout de l'application
django.contrib.staticfiles dans la liste des INSTALLED_APP
- ajout des directives
STATIC_ROOT, STATIC_URL, STATICFILES_DIRS et STATICFILES_FINDERS
- utilisation de
{{STATIC_URL}} dans mes templates à la place de {{MEDIA_URL}}
Ensuite, pour lancer la collecte, la commande suivante fonctionne très bien :
python manage.py collectstatic
Au passage, j'ai aussi simplifié un peu ma configuration Apache, mais ça, c'est un autre sujet.
Et d'autres petites choses...
S'il n'y avait que ça, ce serait trop facile. Et bien, en réalité : il n'y avait que ça.
J'avais déjà fait une première passe pour simplifier grandement le code, faire le ménage, et retirer ce qui n'était pas bien fait et/ou bien pensé (avec un code qui a presque deux ans, c'est normal, j'ai beaucoup appris pendant ce temps). Du coup, une migration assez simple, avec, au déploiement, pas un seul soucis.
J'ajoute, au passage, que c'est une bonne expérience, qui permet de revenir sur ce qui a été fait avant, et sur ce que je fais aujourd'hui - et donc, de pouvoir mesurer l'écart.
Halala... le array_unique est définitivement une fonction trompeuse de php. Pourquoi ? Parce que son mode de dédoublonnage par défaut est SORT_STRING, et pas SORT_REGULAR. D'ailleurs, c'est d'autant plus un problème que ce fut le cas en PHP 5.2.9, puis retour arrière en PHP 5.2.10.
Note : documentation php de array_unique.
Le #loldev du vendredi nous vient donc de cette petite fonction. En voici l'exemple :
$res = array(
array('id' => 2),
array('id' => 3907),
array('id' => 7814),
array('id' => 2),
);
$unique = array_unique($res);
var_dump($unique);
// Résultat :
// array(0 => array('id' => 2));
Ce qui est un problème, puisque le but, c'est seulement de virer la deuxième ligne, comme il a été fait dans l'exemple suivant :
$id = array();
foreach ($res as $idRes) {
$id[] = $idRes['id'];
}
$id = array_values(array_unique($id));
Donc, oui, cela fonctionne et on obtient bien un tableau dédoublonné par la valeur de la clé 'id' de chaque tableau. Heureusement, il y a mieux aujourd'hui.
Ce code provient d'une application pré-5.2.9, où le flag "SORT..." n'existait tout simplement pas. Ce n'est donc pas la faute du pauvre développeur qui a dû trouver à l'époque une solution qui fonctionne ! Je n'aurais pas aimer être à sa place.
Car aujourd'hui, il suffit d'écrire ceci :
$res = array_unique($res, SORT_REGULAR);
Pour dédoublonner le tableau. Simple, efficace. Mais il aura fallu attendre PHP 5.2.9 pour pouvoir le faire !
Remarque : suite à des remarques, voici le pourquoi du comment SORT_STRING pose problème :
Par défaut array_unique transforme en string le contenu de chaque élément du tableau, donc (string) array(...) ça donne "Array". Donc il trouve que le tableau de tableau, c'est un array de 'Array', 'Array', 'Array', ... qui sont donc des éléments identiques, et ne retourne que le premier élément.
Logique, imparable, mais particulièrement pénible.
Il est parfois difficile de dire d'un code qu'il est bon ou mauvais, qu'il est moche ou élégant.
Parfois, les deux se confondent dans un doute profond sur la nature d'une idée, et sur son implémentation.
Beautiful is better than ugly.
Heureusement, parfois, il y a du code php/java/python/javascript/ruby/perl/autre bien sale et c'est très facile à repérer.
Notez l'effort pour ne pas troller toujours sur le même langage.
En ce moment, je répète souvent à mes collègues (et à d'autres personnes) la maxime suivante :
Moins de code, plus de fonctionnalité.
C'est devenu mon leitmotiv en ces jours sombres où je dois travailler avec des gens qui n'ont pas encore acquis toutes les compétences nécessaires sur le projet. Et vu mon niveau d'exigence, autant dire que c'est vaguement difficile à gérer pour moi (mais je me soigne).
Pour paraphraser cette pensée, je dirais qu'il ne sert à rien d'écrire beaucoup de lignes de code pour apporter de nombreuses fonctionnalités.
L'important, notamment et surtout dans le cas d'utilisation de framework, c'est de proposer un code simple, facile à étendre, et qui s'intègre bien à son environnement. C'est ma façon à moi de rappeler les principes KISS et DRY : Keep It Simple & Stupid, Don't Repeat Yourself.
Je parle du cas où l'utilisation d'un framework est obligatoire (parce qu'il s'impose de lui-même ou parce qu'il a été arbitrairement imposé), car c'est exactement dans ces moments là qu'il faut rester vigilant.
Dans le cas d'un framework, écrire "moins de code" ne veut pas dire littéralement écrire moins de code, mais plutôt "intégrer parfaitement le moindre code dans le contexte du framework".
Si le framework impose d'écrire une classe, ou plusieurs, pour écrire un "plugin", alors il vaut mieux le faire, plutôt que d'écrire quelque part codé en dur dans le code, la modification voulue.
C'est peut-être paradoxal, mais cela fonctionne mieux. C'est peut-être frustrant, mais cela évite bien d'autres frustrations.
Croyez-moi, il vaut mieux que tout fonctionne toujours parfaitement avec le framework, que tout fonctionne maintenant comme vous le voulez contre le framework.
Parce que le framework lui, il a déjà écrit beaucoup, beaucoup, beaucoup de lignes de code.
Petite précaution avant d'entamer la lecture de cet article : il est technique, certes, mais ne concerne pas spécifiquement ni symfony ni doctrine. Le sujet de l'article est un problème technique très concret, qui se retrouve dans tous les langages, et avec n'importe quelle base de données.
En lisant le livre "Pratical symfony", plus spécifiquement le chapitre 6, je suis tombé sur un cas très classique : afficher une liste d'élément par catégorie, comme dans l'exemple qui suit.
- Catégorie 1 :
- Article du dd-mm-yy
- Article du dd-mm-yy
- Article du dd-mm-yy
- Catégorie 2 :
- Article du dd-mm-yy
- Article du dd-mm-yy
- Article du dd-mm-yy
- Article du dd-mm-yy
- Catégorie 3 :
- Article du dd-mm-yy
- Article du dd-mm-yy
L'approche ici - mais j'ai déjà vu ça ailleurs très souvent - est de récupérer la liste des catégories, puis, pour chaque catégorie, de récupérer la liste des offres de ladite catégorie.
Sauf qu'il y a un problème : voyez-vous lequel ?