Archive

Archives pour 02/2009

MySQL et les modèles Django

24/02/2009 admin Comments off

Le problème

D’un côté, une base de donnée MySQL maintenue par MySQLWorkbench. De l’autre, Django et ses modèles.

Entre les deux… pas grand chose.

Les acquis

MySQL et MySQLWorkbench

MySQLWorkbench est un outil excellent pour pouvoir maintenir une base de donnée:

  1. Gestion des commentaires sur les tables et champs
  2. Affichage des relations entre les tables d’une base de données sous forme d’un schéma
  3. Facilité de modification d’une base de donnée en production par l’export de scripts SQL

Django et les modèles

Django, de son côté, nécéssite une représentation de la base de donnée par des modèles. Ces modèles sont des classes codées en Python:

  1. Facilité de suivi des modifications d’un modèle par le biais d’un contrôle de version (Git, Mercurial, SVN …) accompagné de commentaires pour chaque commit
  2. Facilité de documentation: un paramètre help_text pour chaque champ, un docstring pour chaque modèle
  3. Abstraction de certaines tables et champs générés automatiquement (comme les primary key ou les tables ManyToMany)

Les outils

Comme indiqué plus haut, MySQLWorkbench permet d’exporter des scripts SQL de création et/ou de modification. Il est aussi possible de reverse engineer le dump SQL d’une base de donnée, et ainsi créer toutes les tables dans l’interface graphique, et y visualiser les relations sous forme de schéma.

Django permet l’export de scripts SQL de création, mais aussi l’inspection d’une base de données (par la commande python manage.py inspectdb), ou encore l’exécution de scripts SQL custom à chaque création/modification d’une table (par la commande python manage.py syncdb).

Les méthodes

La méthode basique

… qui casse complètement le principe DRY (Don’t Repeat Yourself): maintenir les deux parties en parallèle.

Lorsqu’il faut rajouter une table dans la base, ou y apporter une modification, faire la modification dans MySQLWorkbench, puis répliquer l’ajout/la modification dans les modèles Django.

Pour: Méthode simple, pas de procédure ou méthode à mettre en place.
Contre: Méthode manuelle, sujette à l’erreur humaine (faute de typo, oubli…).

MySQLWorkbench => Django

  1. Maintenance dans l’outil MySQLWorkbench, puis export des scripts de création/modification.
  2. Execution de ce script sur la base de données pour la mettre à jour
  3. Utilisation de inspectdb avec Django pour mettre à jour les modèles

Pour: Méthode automatique, donc pas de risque de typo ou d’oubli
Contre: Méthode très complexe à mettre en place. En effet, inspectdb est loin de créer des modèles fidèles, et il manque de nombreuses informations (un futur billet sera écrit sur ce sujet).

Django => MySQLWorkbench

  1. Ajout ou modification d’un modèle dans Django
  2. Mise à jour de la base de donnée en utilisant python manage.py syncdb (et des scripts custom si nécéssaire)
  3. Utilisation de MySQLWorkbench pour reverse engineer les scripts de création de la base de donnée

Pour: Méthode automatique, donc pas de risque de typo ou d’oubli. De plus, contrairement à la méthode précédente, la représentation des tables est 100% fidèle à la structure de la base de donnée créée par les Django.
Contre: Toutes les informations de commentaires (et d’autres, comme nous le verrons dans un futur billet) sont perdues à chaque modification.

Conclusion

Je n’ai malheureusement pas trouvé à l’heure actuelle de méthode miracle. Il y a des pistes, mais il semble qu’il n’y ai rien de concret pour le moment.

Pour certains cas particuliers, l’une ou l’autre méthode sera préférée:

  1. Création d’une nouvelle table: la méthode Django => MySQLWorkbench sera facile à mettre en place, et il n’y aura rien de perdu vu qu’il n’y a pas d’existant
  2. Modification d’une table par le rajout d’un nouveau champ: l’une des deux méthodes automatique peuvent faire l’affaire
  3. Modification d’un champ d’une table: la méthode manuelle sera en général la plus rapide et la plus simple à mettre en place
  4. Modifications complexes, multiples, perte de donnée possible…: dans ce cas, point de salut, il ne reste que la méthode manuelle, et une grande concentration!

Et vous, quelle est votre méthode?

Categories: django Tags:

Apprendre à faire, et faire

22/02/2009 admin 2 commentaires

Quelle est la meilleure méthode d’apprentissage?

Scénario: « moi, j’apprends »

J’apprends.

  1. Je commence par le tutoriel, que je lis de fond en comble
  2. Je continue par les concepts annexes
  3. J’approfondis quelques sujets qui me paraissent importants, utiles, bons à connaître

Malheureusement, très rapidement

  1. Je prends du retard sur mon projet
  2. Je m’éparpille, je me documente sur des concepts qui ne me serviront peut-être jamais
  3. J’approfondis trop, au détriment d’une vue d’ensemble
  4. Et surtout, je commence à oublier ce que j’avais appris au début, je perds le fil, je me rappelle à peine du tutoriel…

Scénario: « moi, je fais »

Je me lance.

  1. J’essaie de comprendre les concepts de base, peut-être le début du tutoriel
  2. Je me lance, je débute mon projet, et je défriche au fur et à mesure
  3. Quand j’ai besoin d’un nouveau concept, j’épluche rapidemment la documentation, et j’applique mes acquis

Malheureusement, très rapidement

  1. Je passe à côté de concepts très utiles et intéressants
  2. Je ne possède pas assez de bases pour développer mon projet intelligemment
  3. Je ne connaît pas les outils qui me simplifieraient la tâche
  4. Et surtout, je me rends compte que mon projet mériterait un refactoring, alors qu’il débute à peine

Scénario: le juste milieu?

J’essaie de trouver le juste milieu entre « j’apprends » et « je fais ».

  1. Je commence par le tutoriel, que je fais
  2. A chaque étape du tutoriel, je met en pratique ce que je viens d’apprendre en faisant quelques modifications/améliorations
  3. Je me fais ensuite (rapidement) une vue d’ensemble des différents concepts et outils disponibles

Ensuite, j’entamme le projet. Le déroulement d’une journée de travail pourrait être :

  1. Je démarre le projet par la partie la plus simple
  2. Dès que j’en ai besoin, j’approfondis un concept/outil nécessaire à l’étape en court du projet
  3. Quand je rencontre un concept/outil adjacent, qui pourrait m’intéresser dans l’avenir, je le marque comme « à lire »
  4. Une fois rentré chez moi, ou avant de commencer le travail le lendemain, je passe un peu de temps à me documenter sur ce que j’ai marqué comme « à lire »

Ma méthode

J’essaie de suivre au mieux le scénario « le juste milieu » : Apprendre chaque jour un peu, et faire.

Voici un ordre d’idée du déroulement d’une journée de travail, une fois que le projet est démarré:

  1. Je me réserve une heure le matin, pour me documenter sur des sujets généraux, des outils…
  2. puis j’avance sur le projet, et ne me documente que si nécessaire pour résoudre un problème précis
  3. si je rencontre un sujet que j’aimerais approfondir, j’ouvre un onglet dans mon navigateur pour le lendemain matin

Quelques ordres de grandeur :

  • Un tutoriel doit prendre quelques heures (au maximum une journée)
  • Le temps passé à le mettre en pratique en faisant quelques modifications/amélioration ne doit pas dépasser le temps passé à le suivre (donc durée totale « tutoriel + mise en pratique » < 2 journées)
  • Une fois le projet démarré, se documenter sur un outil/concept doit prendre (en moyenne) moins de 50% du temps de travail (ce ratio doit évaluer en faveur du développement au fur et à mesure de la maîtrise du sujet)

Selon le sujet à apprendre, ces ordres de grandeur varient:

  1. Plus d’apprentissage pour un nouveau langage de programmation (par exemple, Python)
  2. Plus de mise en pratique pour un nouvel outil (par exemple, Mercurial)
  3. Environ 50/50 pour un framework (par exempe, Django)

Et vous?

quelle est votre méthode? Avez-vous des astuces à partager?

Categories: Uncategorized Tags:

Django FileField et ImageField, upload_to et shell python

19/02/2009 admin 2 commentaires

Le paramètre upload_to des FileField et ImageField

Le champ upload_to permet d’indiquer où sauver un fichier de type django.core.files.File par rapport au MEDIA_ROOT spécifié dans les settings.

Ce champ peut être une chaîne de caractères, ou un callable.

Chaîne de caractères pour upload_to

Pour notre example, prenons le modèle suivant:

class MonModele(models.Model):
    fichier = models.FileField(upload_to="chemin")

Dans ce cas simple, tous les fichiers seronts sauvés dans le répertoire <MEDIA_ROOT>/chemin/.

Si on upload un fichier nommé MonFichier.txt, le chemin complet (sans le MEDIA_ROOT) sera chemin/MonFichier.txt.

Il est par ailleurs possible d’utiliser une syntaxe strftime comme indiqué dans la documentation du module time.

Pour stocker un fichier avec la date et l’heure:

class MonModele(models.Model):
    fichier = models.FileField(upload_to="chemin/%Y%m%d_%H%M%S")

Et le résultat sera chemin/2009-02-19_18:40:03/MonFichier.txt, ce qui n’est pas vraiment ce à quoi on s’attendait.

En effet, cela créera un répertoire par fichier, au lieu de stocker la date et l’heure dans le nom du fichier.

Pour arriver à nos fins, il nous faut utiliser une fonction pour le calcul du chemin de stockage du fichier.

Fonction pour upload_to

Utiliser une fonction pour le calcul du chemin de stockage permet:

  1. de modifier le nom du fichier lui-même, et pas seulement son répertoire de stockage
  2. d’utiliser des informations spécifiques à l’instance du modèle pour lequel on stocke le fichier

Il nous faut par contre respecter les contraintes suivantes:

  1. la fonction aura deux paramètres: self, l’instance, et filename, le nom du fichier uploadé
  2. il n’est plus possible d’utiliser directement la syntaxe strftime

Voici le code qui va stocker un fichier avec la date et l’heure (et prendra la date et l’heure automatiquement ajoutée au champ DateTimeField avec le paramètre auto_add_now):

class MonModele(models.Model):
    date = models.DateTimeField(auto_now_add=True)
    def upload_path(self, filename):
        return 'chemin/%s_%s' % (self.date.strftime("%Y%m%d_%H%M%S"), filename)
    fichier = models.FileField(upload_to=upload_path)

Ce coup-ci, on obtient bien un fichier chemin/2009-02-19_18:40:03_MonFichier.txt, comme prévu.

Tester un modèle avec FileField dans l’interpréteur Python

Pour pouvoir tester un modèle avec un FileField dans un intepréteur Python (python manage.py shell), il y a quelques précautions à prendre, comme commencer par « sauver » le File, comme indiqué sur la page suivante.

En effet, pour que le modèle soit correctement instancié, il faut:

  1. créer une instance du modèle
  2. la sauver (dans notre cas, afin que la date soit automatiquement stockée, et devienne accessible dans upload_path)
  3. ouvrir le fichier à « uploader »
  4. créer un django.core.files.File à partir de ce fichier
  5. sauver ce fichier pour pouvoir ensuite le tester
>>> from mysite.models import *
>>> mm = MonModele()
>>> mm.save()
>>> mm.date
datetime.datetime(2009, 2, 19, 19, 4, 49, 538465)
>>> from django.core.files import File
>>> f = File(open("MonFichier.txt"))
>>> mm.fichier.save(f.name, f, save=False)
>>> mm.fichier
<FieldFile: chemin/20090219_190449_settings.py>

Pour « sauver » le fichier, il faut fournir à la méthode save le nom du fichier (f.name), le fichier lui-même (f), et un paramètre spécifiant si on veut sauvegarder le fichier dans la base de donnée ou pas.

Si nous ne voulions pas accéder à date qui est stockée automatiquement, il aurait été inutile de sauvegarder l’instance de MonModele (mm), et il aurait alors fallut remplacer le code de upload_path de la sorte

def upload_path(self, filename):
    return 'chemin/%s_%s' % (self.date.strftime("%Y%m%d_%H%M%S"), filename)

par

def upload_path(self, filename):
    import time
    return 'chemin/%s_%s' % (time.strftime("%Y%m%d_%H%M%S"), filename)
Categories: django Tags:

Django svn et mod_wsgi, attention au piège!

17/02/2009 admin 2 commentaires

Scénario

Notre cher utilisateur biboul se décide à installer la version de développement de django, que nous appellerons django-trunk, comme indiqué sur la page How To Install Django.

Il lance donc les commandes suivantes:

biboul@laptop:~$ svn co http://code.djangoproject.com/svn/django/trunk/ django-trunk
biboul@laptop:~$ ln -s `pwd`/django-trunk/django /usr/lib/python2.5/site-packages/django
biboul@laptop:~$ ln -s `pwd`/django-trunk/django/bin/django-admin.py /usr/local/bin

Il a bien entendu

  1. configuré son serveur apache pour utiliser le module WSGI
  2. testé avec le script wsgi « hello world » que la configuration était bonne
  3. modifié le script wsgi de manière à utiliser son application django mysite

Le problème

Lors de la première requête à son application mysite, une belle « 500 Internal Server Error » s’affiche, avec les messages d’erreurs suivants dans le fichier /var/log/apache2/error_log:

...  mod_wsgi (pid=5803): Target WSGI script '/opt/tcs/tcs.wsgi' cannot be loaded as Python module.
...  mod_wsgi (pid=5803): Exception occurred processing WSGI script '/opt/tcs/tcs.wsgi'.
...  Traceback (most recent call last):
...       File "/opt/tcs/tcs.wsgi", line 6, in <module>
...           import django.core.handlers.wsgi
...  ImportError: No module named django.core.handlers.wsgi

Mais pourquoi donc, alors qu’un import django.core.handlers.wsgi fonctionne correctement, que ce soit dans l’interpréteur ou dans le shell django?

La réponse

Tout simplement parce que le répertoire django-trunk (dont notre cher utilisateur biboul a fait un lien symbolique dans le répertoire site-packages) n’est pas accessible à un utilisateur différent, si il n’est pas dans le même group.
Or, par défaut et sur la plupart des distributions Linux, les processus apache sont lancé avec un utilisateur limité (www-data, www ou encore apache).

La solution

Un simple chmod 755 des répertoires parents au répertoire django-trunk est suffisant pour régler ce problème.
Une autre solution, plus propre et sécurisée, serait de placer le répertoire django-trunk dans le répertoire /opt, et de modifier les liens symboliques pour utiliser ce nouvel emplacement.

Categories: django, mod_wsgi Tags:

30 ans, et toutes mes dents

15/02/2009 admin 2 commentaires

30 ans aujourd’hui.

Il reste encore une petite heure dans cette première journée de mes 30 ans. Et je commence un blog.

Je jette dans ce tout premier billet les motivations pour ce blog (qui restent pour l’heure très vagues):

  1. Avoir un aide-mémoire
  2. Structurer mes pensées et divagations
  3. Ajouter ma pierre à l’édifice

Quelques règles pour les futurs billets:

  1. Travailler sur la concision et la clarté
  2. Une écriture claire, simple et directe
  3. Pas de billets inutiles

A bientôt pour la suite.

Categories: Uncategorized Tags: