Django 1.7, « le framework web pour les perfectionnistes sous pression »

33
5
mar.
2015
Python

D'après Wikipedia : « Django est un framework open-source de développement web en Python. Il a pour but de rendre le développement web 2.0 simple et rapide. Pour cette raison, le projet a pour slogan « Le framework web pour les perfectionnistes sous pression ». Développé au départ pour les sites de la ville de Lawrence (Kansas), Django a été publié sous licence BSD à partir de juillet 2005. »

Django

La version 1.7 du framework web Django est sortie le 2 septembre 2014. La principale nouvelle fonctionnalité est la gestion intégrée des migrations de schéma. On notera la publication de la version 1.7.5 le 25 février 2015, et en même temps, la première bêta de la version 1.8.

Nouveautés

Migrations des schémas de base de données

Django inclut, enfin, une gestion des migrations des bases de données. Jusqu'ici, la plupart des projets utilisaient South. L'auteur de cette application, Andrew Godwin, a pu financer l'intégration d'une refonte de South au sein de Django via une campagne Kickstarter.

Pour les curieux, Andrew Godwin a présenté lors de la dernière PyCon US les choix opérés lors du développement. La vidéo est disponible sur YouTube.

Refactoring du chargement des applications

Le chargement des applications a été réécrit. Cette réécriture permet d'améliorer les points suivants :

  • Une application qui n'utilise pas de modèle n'est plus obligée d'inclure un fichier models.py vide.
  • Une application peut définir sa configuration. Cette configuration permet notamment de donner un nom verbeux à l'application (ce nom est utilisé par l'interface d'administration) et une méthode ready() qui est appelée au lancement.
  • L'ordre des imports est plus déterministe et permet de mieux diagnostiquer les problèmes d'imports circulaires. Ce changement peut entraîner des incompatibilités avec du code existant.

Simplification des QuerySetManager

Dans Django, lorsque l'on souhaite définir des requêtes réutilisables, il est recommandé d'étendre la classe QuerySet et d'y définir ses propres méthodes. Cette méthode fonctionne bien, mais elle pose problème quand l'on définit un QuerySetManager (raccourci dans l'écriture de requêtes). En effet, si l'on souhaite réutiliser les méthodes d'un QuerySet sur un QuerySetManager, il est nécessaire de redéfinir chaque méthode.

Par exemple :

class MouleQuerySet(models.QuerySet):

    def pas_fraiches(self):
        return self.filter(dlc_gte=datetime.datetime.utcnow())

    def bretonnes(self):
        return self.filter(bassin='XXX')


class MouleQuerySetManager(models.QuerySetManager):

    def get_queryset(self):
        return MouleQuerySet(self)

    def pas_fraiches(self):
        return self.get_queryset().pas_fraiches()

    def bretonnes(self):
        return self.get_queryset().bretonnes()


class Moule(models.Model):

    bassin = models.CharField(max_length=50)
    dlc = models.DateTimeField()

    objects = MouleQuerySetManager()

# utilisation

# Moules bretonnes pas fraiches
Moule.objects.bretonnes().pas_fraiches()

Avec Django 1.7, il n'est plus nécessaire d'écrire de classe héritant de QuerySetManager. À la place, il suffit d'appeler la méthode QuerySet.as_manager() :

# la classe MouleQuerySet ne change pas

class Moule(models.Model):

    bassin = models.CharField(max_length=50)
    dlc = models.DateTimeField()

    objects = MouleQuerySet.as_manager()

# l'utilisation ne change pas
Moule.objects.bretonnes().pas_fraiches()

Framework de vérification

Un nouveau framework est disponible pour détecter les problèmes. Il remplace la simple validation des modèles (avec la commande validate) et permet de détecter :

  • les modèles invalides
  • l'utilisation de fonctionnalités dépréciées
  • les settings manquants ou invalides
  • les traductions à mettre à jour.

Ce framework est extensible et chaque application peut définir ses propres contrôles.

Moteur de templates

Pas de grands changements :

  • Un nouveau filtre truncatechars\_html qui tronque une chaîne en tenant compte des balises HTML.
  • Les exceptions de type TypeError ne sont plus ignorées silencieusement.
  • D'autres modifications…

Versions de Python supportées

Le support de Python 2.6 a été retiré. Django est compatible avec les versions 2.7, 3.3 et suivantes. Plusieurs modules inclus pour assurer la compatibilité avec Python 2.6 sont dépréciés :

  • django.utils.dictconfig
  • django.utils.importlib
  • django.utils.unittest
  • django.utils.datastructures.SortedDict

Autres changements

La commande runserver exploite pyinotify sous Linux au lieu d'un polling bête et méchant. Le rechargement du serveur est donc plus rapide et consomme moins de ressources. De même, il se relance automatiquement après la compilation d'une traduction.

JSON

La nouvelle sous-classe JsonResponse facilite la création de réponses renvoyant du JSON.
Les erreurs d'un formulaire peuvent être converties en JSON avec la méthode as_json().

  • # Inutile

    Posté par (page perso) . Évalué à -10.

    Il a fait une mise au point, Godwin.

  • # Je m'en sert tous les jours...

    Posté par . Évalué à 3.

    Et c'est un truc qui rocks les pingouins….

    … +1 donc …

  • # Préciser "Simplification des QuerySetManager"

    Posté par . Évalué à 5.

    Je ne connais pas Django, mais ça m'intéresse de savoir se qui se fait ailleurs, et je n'ai pas compris le passage sur QuerySetManager.

    La dépêche dit que si l'on voulait créer son propre QuerySetManager, il était alors pénible de l'interfacer avec le QuerySet associé. La solution proposée est de ne pas créer son propre QuerySetManager, mais d'en générer un à la volée. Stricto senso, ça ne répond pas au problème initial, à moins que les classes -SetManager soient toujours des surcouches neutres sur les -Set.

    À première vue, ça semble surtout très mal ficelé : dès qu'on voudra enrichir ce SetManager, il faudra changer la structure du code et se taper l'ancienne syntaxe ? Mais j'ai sans doute mal compris.

    • [^] # Re: Préciser "Simplification des QuerySetManager"

      Posté par . Évalué à 4. Dernière modification le 06/03/15 à 15:57.

      Bonjour,

      Bon, je me coltine la description technique…

      Un Queryset est ce qui permet de décrire une requête dans l'ORM.

      Typiquement, ça ressemble à un truc comme ça :

      # dans models.py : 
      class Machin(models.Model):
          foo = models.IntegerField()
          bar = models.CharField(max_length=255)
          rel_1 = models.ForeignKey('app.FirstRelatedTable') # n::1
          rel_2 = models.Many2Many('app.SecondRelatedTable') # n::n
          rel_2 = models.One2One('app.ThirdRelatedTable') # 1::1
      
      
      qset = (models.Machin.objects
              .filter(foo__exact=1)
              .filter(bar__icontains='hello')
              .select_related()
              .prefetch_related('rel_2'))

      Depuis le modèle Machin (représenté par la table machin dans la base de données), je prends tout puis je sélectionne :

      • ceux qui ont le champ foo qui vaut 1 (WHERE foo = 1)
      • ceux qui ont le champ '''bar''' contiens la chaine 'hello', non sensible à la casse ( WHERE bar like '%hello%')
      • je charge l'ensemble des relations de premier niveau avec select_related() : les objets FirstRelatedTable et ThirdRelatedTable sont instanciés directement.
      • je charge les objets qui ont une relation M2M avec prefetch_related('rel_2')

      Il est important que comprendre avec django, c'est que les querysets sont fainétants : il ne font rien tant qu'on ne les parcourt pas explicitement.

      # dans le template
      <ul>
      {% for item in qset %}
          <li>{{ item.foo }} : {{ item.rel_1 }} / 
          {% for related in item.rel_2.all %} # Le 'all' permet le parcours du manager de la relation rel_2 
            {{ related }}
          {% endfor %}
          </li>
      {% endfor %}
      </ul>

      OK ?

      L'histoire des QuerysetManager, c'est pour 2 choses :

      • un QuerysetManager est créé implicitement par django :

        • Si on a un m2m, django créé le manager tout seul, et on peut récupérer les données depuis les 2 côtés de la relation
        • Si FK : en spécifiant un manager à la main dans la destimation de la FK, on a le même comportement : on peut récupérer des objets via la relation inverse.
      • On peut aussi définir des classes avec des méthodes ad hoc pour ne par réécrire encore et encore les même querysets, et c'est à ça que ça sert les querysets…

      J'espère que j'ai été clair…

      Bon week-end !

      • [^] # Re: Préciser "Simplification des QuerySetManager"

        Posté par . Évalué à 1.

        Merci pour la réponse, même si elle ne répond pas du tout à la question ;-)

        Du coup, j'ai fait l'effort d'aller lire la doc, laquelle semble vraiment bien fichue. Je ne connais pas Django, je n'aurais pas parié un kopeck sur mes chances, et pourtant j'ai trouvé et compris en 10 minutes.

        La nouvelle méthode citée dans la dépêche est documentée dans ce paragraphe et mon interrogation fait l'objet du paragraphe suivant.

        En bref, quand on ne se satisfait pas de l'objet QuerySetManager renvoyé automatiquement par as_manager(), il faut utiliser from_queryset() qui va renvoyer une sous-classe de QuerySetManager que l'on peut alors enrichir. Un exemple de code est donné dans la doc, cf lien ci-dessus. Le mécanisme de portage automatique de l'API de QuerySet à QuerySetManager est aussi expliqué et illustré.

        • [^] # Re: Préciser "Simplification des QuerySetManager"

          Posté par (page perso) . Évalué à 2.

          La documentation est en effet vraiment bien fichue. Un autre avantage de cette doc est de savoir longtemps à l'avance ce qui va apparaître ou disparaître, ce qui laisse le temps de s'organiser. J'ai pu passer du code écrit pour Django 1.2 à du 1.4 puis du Django 1.7 sans trop de souci, malgré une grande quantité de changements côté Django, simplement en lisant les notes de version au paragraphe sur les incompatibilités.

  • # Réécriture != refactoring

    Posté par . Évalué à 8. Dernière modification le 05/03/15 à 23:43.

    Refactoring du chargement des applications

    Le chargement des applications a été réécrit. Cette réécriture permet d'améliorer les points suivants :

    Grrr, une réécriture n'est pas du refactoring!

    Le refactoring c'est une suite de changements simples et sans risques (ou presque) faits de manière incrémentale et qui ne modifie pas le comportement observable du code.
    Une réécriture, c'est abandonner l'ancien code et le réécrire de zéro (ou presque), en oubliant les anciens bugs et en en créant de nouveaux, ce qui bien sur modifie le comportement observable du code.

    Ça n'a franchement rien a voir!
    Dans un cas, évolution du code, dans l'autre, jetage de bébé avec l'eau du bain!

  • # Documentation en français

    Posté par . Évalué à 6.

    Il vaudrait peut-être la peine de mentionner aussi la présence d'une documentation presque entièrement traduite en français.
    https://docs.djangoproject.com/fr/1.7/

    Ce n'est peut-être pas très utile pour le vrai développeur pro qui maîtrise sans doute parfaitement l'anglais, mais je pense qu'il existe un public cible pour qui la possibilité de lire une documentation dans sa langue maternelle est important.

  • # Les migrations, enfin !

    Posté par (page perso) . Évalué à 2.

    L'absence de migration était un gros manque de Django, même s'il pouvait être compensé par South.
    Globalement, Django arrive à petit à petit supprimer ses problèmes de jeunesse. J'espère qu'on aura un jour une solution propre et bien intégrée pour faire de websocket (Django est d'abord pensé pour des applis web pas trop dynamiques).

Suivre le flux des commentaires

Note : les commentaires appartiennent à ceux qui les ont postés. Nous n'en sommes pas responsables.