Catégorie : python

Django 2.1 : les autorisations de l’administration grandement facilitées

Voici le (petit) souci que tout le monde a forcément dès qu’on commence à vouloir un peu pousser la customization de l’administration Django : on aimerait avoir la possibilité d’accéder à certains modèles, mais uniquement en lecture seule. J’ai des tonnes d’exemples simples, mais très souvent, ce sont des modèles avec des choix « fixes » qui ne risquent pas d’évoluer dans le temps (exemple : sexe « féminin »/ »masculin »). Seulement (1) on ne veut pas le voir apparaître dans l’admin (2) si on ne veut pas le voir apparaître, il ne faut pas le déclarer, et Django ne veut pas accéder à un modèle qui n’est pas déclaré (= erreur), donc on en arrive toujours à (3) on déclare le modèle, on applique une solution « bidouille » que je ne décris pas ici mais même si le modèle n’apparaît pas dans l’interface, en tapant à la main la bonne URL (facile à deviner qui plus est), on peut tout de même modifier ce modèle. Heureusement la version 2.1 de Django pallie à ce manque !

Eh oui, dans la classe interne «Meta», ils ont fait évoluer default_permissions, qui est écrit en base. Donc en passant à Django 2.1 il vous faudra faire le fameux makemigrations migrate.

Là ou cela devient intéressant, c’est que dans les options vous avez default_permissions et elles sont ('add', 'change', 'delete', 'view'). Le plus important et le nouveau est donc 'view' qui pallie parfaitement au problème expliqué en haut !

Il vous suffit de déclarer votre modèle, de faire la classe dérivée de ModelAdmin et de préciser uniquement ('view',).
Attention, je n’ai pas testé, si quelqu’un veut tester et confirmer que ce code fonctionne (ou pas), je suis preneur !

class SexeAdmin(models.ModelAdmin):
    class Meta:
        default_permissions = ('view',)

Plein de code en moins, plus aucune bidouille ici, plus de lisibilité, bref, de mieux en mieux !

Django Async Roadmap

Petite parenthèse sur l’évolution de Django. Aujourd’hui, ce qui commence à importer plus que la vitesse, c’est la parallélisation des choses.
Donc avoir un serveur Web asynchrone, c’est censé être mieux qu’un serveur Web synchrone (= bloquant).

  • Django 2.1 : rien d’asynchrone. Aucun travail dessus.
  • Django 2.2 : travail initial pour ajouter la possibilité de faire de l’asynchrone sur l’ORM et les vues, sachant que c’est une option et que tout sera synchrone par défaut. Le support async sera principalement basé sur des pools de threads.
  • Django 3.0 : Ré-écriture de la gestion interne des requêtes pour être 100% asynchrone, ajout de la gestion asynchrone des middleware, forms, cache, sessions, auth. Début du process de dépréciation pour toutes les APIs prévues pour être uniquement asynchrones.
  • Django 3.1 : amélioration du support async, peut-être changement du templating
  • Django 3.2 : Fin du process de dépréciation et Django sera presque entièrement asynchrone.

Django : autorisations sur mesure – custom authorizations

Voici comment créer manuellement des groupes + des autorisations personnalisées :

from django.contrib.auth.models import Group, Permission
from django.contrib.contenttypes.models import ContentType
from django.contrib.auth.models import User
new_group, created = Group.objects.get_or_create(
    name ='co_branding_trainer')
ct = ContentType.objects.get_for_model(User)
permission_co_branding_trainer = Permission.objects.create(
    codename='co_branding_trainer',
    name='Co-branding trainer', content_type=ct)
new_group.permissions.add(permission_co_branding_trainer)

Une fois cela fait, on a deux possibilités :

  • aller dans le template et se servir de
    {% if perms.app_label.can_do_something %}
    {% endif %}
  • ou modifier le code vue dans le fichier Python. Dans ce cas, on n’a rien dans le template et si la personne connectée n’a pas les droits, la vue fait un forbidden

Comme je préfère la seconde (qui évite d’avoir dans le template des « if » un peu partout), voici comment faire : on crée un mixin qui se chargera de vérifier si, dans la vue, on a les droits : il suffit de chercher PermissionRequiredMixin dans le code de Django, et vous verrez que tout est simple, à partir de là vous pourrez faire descendre vos vues génériques de PermissionRequiredMixin.

Django Channels : astuces

Vous êtes sous Windows ?
Vous avez Python36 ?
Vous voulez faire tourner Django Channels ?
Installez Python 3.5 dans C:\Python35, puis à partir de là il faut tout faire sous Python 3.5.
Vous verrez que lorsque vous aurez tout installé, tout configuré, eh bien… daphne (le serveur Web asynchrone pour Django Channels) ne fonctionnera pas.
En fait, il vous faut modifier le path Windows pour être sûr qu’il pointe d’abord sur Python35 puis sur Python35/Scripts

Sous PyCharm, j’ai (enfin !) trouvé l’astuce : allez dans :
Project » Settings » Tools » Terminal

De là, modifiez Shell path par :
cmd.exe "/k set PATH=C:\Python35;C:\Python35\Scripts;%PATH%"
ou
"cmd.exe" /k ""set PATH=C:\Python35;C:\Python35\Scripts;%PATH%""
(une des deux solutions va fonctionner)

Et vous aurez accès à daphne en ligne de commande !

Django Channels sous Windows : attention !

Bah oui !

Sous Linux, Django Channels est compatible Python 3.5 et plus : c’est vrai.
Pof, comme d’habitude, sudo apt-get install blabla, pip3 install blabla et tout fonctionne.
Sous Windows, ce n’est pas le cas (cela changera sûrement dans les semaines à venir et cet article va rapidement ne plus être vrai), mais pour l’instant, Django Channels ne fonctionne pas sous Windows avec Python 3.6, mais uniquement avec Python 3.5.

De plus, vous devrez télécharger des Gigas et des Gigas de données Microsoft pour avoir le compilateur C++ 14 (si ma mémoire est bonne).

Parce que mon ami Microsoft © a retiré les liens (et si vous les avez je suis preneur) du compilateur en ligne C++, et vous devez télécharger Visual Studio et toutes ses immondices qui vont autour, « téléchargez la version pro », « payez un abonnement », ou le pire : « vous devez être enregistré pour pouvoir lancer Visual Studio » (ce qui, à mon sens, est totalement inadmissible et fait partie des wagons (trains entiers ?) de choses qui font que je prône Linux).

2,8 Go chez moi pour Visual Studio Community ainsi que 5,3 Go pour Microsoft SDKs.

Bref, sous Windows :

  • Installez Python 3.5 (toujour à la racine « C:\Python35« ) pour éviter les problèmes de droits quand il est dans « Program Files« 
  • Installez via la ligne de commande classique « channels » (sans passer par PyCharm) : python.exe -m pip install -U channels

Comment détecter le père Noël / deep learning, Python, and a Raspberry Pi

Comment détecter le père Noël (détection d’image en Python) ?

C’est un article à la fois super marrant, mais très poussé techniquement, qui apprend au Raspberry Pi comment détecter le père Noël.

Attention, c’est en anglais !

How to build Santa/Not Santa detector with deep learning, Python, and a Raspberry Pi

Comment transférer des fichiers très rapidement entre deux PCs distants ?

Petite astuce très pratique : vous voulez transférer des fichiers d’un PC à un autre, sans forcément partager le répertoire, vous faire suer avec les problèmes de droits, etc. ?

Rien de plus simple, lancez un serveur local Python qui servira les fichiers de manière statique :

  • Python 2 : python -m SimpleHTTPServer
  • Python 3 : python3 -m http.server

Et à distance, tapez l’URL du PC sur lequel tourne le serveur, et hop, vous pouvez récupérer ce que vous voulez !

Exemple de python webserver
Exemple de python webserver

Enfin des guides de packaging sur python.org

On peut chercher partout sur le Web, ça part toujours un peu dans tous les sens et beaucoup ont des solutions différentes.
C’est un peu comme les outils pour gérer le travail en équipe… ou la mise en production de Django : plein de solutions, et aucun n’est officiellement supportée.

Eh bien maintenant, c’est un petit pas pour Python, un grand pour les développeurs !
Il existe maintenant des guides de packaging sur python.org.

Cliquez ici pour tout savoir sur le packaging officiel dans Python :

Python Packaging User Guide

Python : liste : voir si tous les éléments sont identiques

Encore une petite idée prise ici : Astuces Python en anglais

>>> lst = ['a', 'a', 'a']

>>> len(set(lst)) == 1
True

>>> all(x == lst[0] for x in lst)
True

>>> lst.count(lst[0]) == len(lst)
True

Dans l’ordre, du plus « Pythonique » au moins « Pythonique »… mais paradoxalement, du moins efficace au plus efficace.
Utiliser count() est le plus rapide.

Dang Bader : dbader.org

Si vous souhaitez vous moderniser dans le monde Python et apprendre plein de petites astuces, je vous conseille de vous inscrire ici sur https://dbader.org/.

Voici quelques petites news que je résume et que je traduis en français :

Supprimer temporairement une exception

Il est possible de supprimer temporairement une exception en utilisant contextlib.suppress().

Exemple concret :
import contextlib
with contextlib.suppress(FileNotFoundError):
    os.remove('monfichier.tmp')

Voici l’équivalent du bon try/except des familles :

try:
    os.remove('monfichier.tmp')
except FileNotFoundError:
    pass

Méthodes de classe vs méthodes statiques

Le code parle de lui-même :

class MaClasse:
    def methode_normale(self):
        return 'instance method called', self

    @classmethod
    def methode_de_classe(cls):
        return 'class method called', cls

    @staticmethod
    def methode_statique():
        return 'static method called'

# Appels sur une instance :
# tout fonctionne :
>>> obj = MaClasse()
>>> obj.methode_normale()
('instance method called', )
>>> obj.methode_de_classe()
('class method called', )
>>> obj.staticmethod()
'static method called'

# Appels directs : voyez le résultat :
>>> MaClasse.methode_de_classe()
('class method called', )
>>> MaClasse.methode_statique()
'static method called'
>>> MaClasse.methode_normale()
TypeError:
    "unbound method methode_normale() must be called with MaClasse"
    "instance as first argument (got nothing instead)"

Désassembler un binaire Python

>>> def greet(name):
...     return 'Salut, ' + name + ' !'

>>> greet('Olivier')
'Salut, Olivier !'

>>> import dis
>>> dis.dis(greet)
2   0 LOAD_CONST     1 ('Salut, ')
    2 LOAD_FAST      0 (name)
    4 BINARY_ADD
    6 LOAD_CONST     2 ('!')
    8 BINARY_ADD
   10 RETURN_VALUE