Catégorie : développement

Tout ce qui concerne le développement en général, que ce soit des choses générales, ou des choses bien précises. Cela va de la gestion de projet à la recherche du fonctionnement de pointeurs en C.

git : exemple de fichier

Voici un exemple de fichier de configuration qui ignore la plupart des fichiers qui posent problème :

# --------------------------------------
# git cleanup if too many files / or / problems:
# git reflog expire --expire=now --all && git repack -ad && git prune
# --------------------------------------
# PyCharm
.idea/
# --------------------------------------
# Byte-compiled / optimized / DLL files
__pycache__/
*.py[cod]
# --------------------------------------
# C extensions
*.so
# Distribution / packaging
bin/
build/
develop-eggs/
dist/
eggs/
lib/
lib64/
parts/
sdist/
var/
*.egg-info/
.installed.cfg
*.egg
# --------------------------------------
# Installer logs
pip-log.txt
pip-delete-this-directory.txt
# --------------------------------------
# Unit test / coverage reports
.tox/
.coverage
.cache
nosetests.xml
coverage.xml
# --------------------------------------
# Translations
*.mo
# --------------------------------------
# Mr Developer
.mr.developer.cfg
.project
.pydevproject
# --------------------------------------
# Rope
.ropeproject
# --------------------------------------
# Django stuff:
*.log
*.pot
# --------------------------------------
# Sphinx documentation
docs/_build/
# --------------------------------------
# git
objects/
# --------------------------------------
# swap files
*.swp
*.swo

Bootstrap : classes utilitaires

Vous allez peut-être trouver du code qui ressemble à cela :
<p class="mb-0">Texte dans un paragraphe</p>

Bootstrap >= 4 a mis en place plein de possibilités pour faire du padding et des marges « responsives ».
Elles fonctionnent pour tous les breakpoints :

  • xs (<=576px)
  • sm (>=576px)
  • md (>=768px)
  • lg (>=992px)
  • xl (>=1200px)

<p class="{propriété}{côtés}-{taille}">texte</p>

propriété :

  • m – les marges
  • p – le padding

côtés :

  • tmargin-top or padding-top
  • bmargin-bottom or padding-bottom
  • lmargin-left or padding-left
  • rmargin-right or padding-right
  • x*-left and *-right
  • y*-top and *-bottom
  • blank – les 4 côtés à la fois

taille :

  • 0 – supprimer les marges ou mettre le padding à 0
  • 1 – $spacer *0.25
  • 2 – $spacer *0.5
  • 3 – $spacer
  • 4 – $spacer *1.5
  • 5 – $spacer *3
  • auto – margin à auto

Python : hints & cheats

Son propre fichier basique de log

import datetime
msg = u"Test de log"
dt = datetime.datetime.now()
with open("monlogfile.log", 'a+') as f:
    f.write(u'{:02}:{:02} - {}\n'.format(dt.hour, dt.minute, msg))

Teaching Python

Here

Découpages Python : formation

Checkio

Demystifying Two Factor Auth

https://rcoh.me/posts/two-factor-auth/

Python Open Source Projects of the Year

Here!

Thanks to Dan Bader

  • Python Parallel Computing (in 60 Seconds or less): here
  • Python Decorators: A Step-By-Step Introduction: here
  • Interfacing Python and C: Advanced “ctypes” Features: here
  • Working with Random Numbers in Python » ici
  • Face detection
  • Différentes manières de testers plusieurs flags en même temps en Python :
    x, y, z = 0, 1, 0
    if x == 1 or y == 1 or z == 1:
        print('ok')
    if 1 in (x, y, z):
        print('ok')
    # si l'un d'eux n'est pas vide:
    if x or y or z:
        print('ok')
    if any((x, y, z)):
        print('ok')
  • Comment trier un dictionnaire Python par ses valeurs :
    »»» xs = {'a': 4, 'b': 3, 'c': 2, 'd': 1}
    »»» sorted(xs.items(), key=lambda x: x[1])

    ou bien :
    »»» import operator
    »»» sorted(xs.items(), key=operator.itemgetter(1))
  • Mesurer le temps d’exécution de petits morceaux de code Python :
    »»» import timeit
    »»» timeit.timeit('"-".join(str(n) for n in range(100))',
                      number=10000)
    0.3412662749997253
    »»» timeit.timeit('"-".join([str(n)
                                 for n in range(100)])',
                      number=10000)
    0.2996307989997149
    »»» timeit.timeit('"-".join(map(str, range(100)))',
                      number=10000)
    0.24581470699922647
  • Comment utiliser la classe namedtuples :
    # namedtup1e est une classe :
    »»» from collections import namedtuple
    »»» Car = namedtup1e('Car' , 'color mileage')
    # Our new "Car" class works as expected:
    »»» my_car = Car('red', 3812.4)
    »»» my_car.color
    'red'
    »»» my_car.mileage
    3812.4
    # Une belle représentation repr avec :
    »»» my_car
    Car(color='red' , mileage=3812.4)
    # Comme les tuples, les namedtuples sont immuables :
    »»» my_car.color = 'blue'
    AttributeError: "can't set attribute"
  • « is » vs « == » :
    »»» a = [1, 2, 3]
    »»» b = a
    »»» a is b
    True
    »»» a == b
    True
    »»» c = list(a)
    »»» a == c
    True
    »»» a is c
    False

    is est True si deux variables pointent vers le même objet ;
    == est True si les variables contenues dans les objets sont identiques.

  • Titre Titre  :
    »»» Code code code

Learning Python in minutes
https://learnxinyminutes.com/docs/python3/

How to Send an Email With Python
https://dbader.org/blog/python-send-email

Python Decorators From the Ground Up
https://pabloariasal.github.io/python-decorators-from-the-ground-up/

How — and why — you should use Python Generators
https://medium.freecodecamp.org/how-and-why-you-should-use-python-generators-f6fb56650888

Download information on all your gmail emails and the body text to either csv or json. I developed this to download my 100K + emails stored over several years on gmail.
https://teklern.blogspot.fr/2017/11/download-all-your-email-information.html

Memoization in Python: How to Cache Function Results
https://dbader.org/blog/python-memoization

Implementing a Neural Network from Scratch in Python – An Introduction
https://www.datasciencecentral.com/profiles/blogs/implementing-a-neural-network-from-scratch-in-python-an

—–

Introduction to NumPy and Pandas – A Simple Tutorial
https://cloudxlab.com/blog/numpy-pandas-introduction

Fastest way to uniquify a list in Python >=3.6
https://www.peterbe.com/plog/fastest-way-to-uniquify-a-list-in-python-3.6

8 Python Modules For Files Handling
http://devarea.com/8-python-modules-for-files-handling/

How do async for loops work in Python? Using asynchronous for loops in Python
https://quentin.pradet.me/blog/using-asynchronous-for-loops-in-python.html

How to use Python and Flask to build a web app — an in-depth tutorial
https://medium.freecodecamp.org/how-to-use-python-and-flask-to-build-a-web-app-an-in-depth-tutorial-437dbfe9f1c6

Framework ultra simple pour faire des micro-services en Json
Falcon is a bare-metal Python web API framework for building very fast app backends and microservices.
http://falconframework.org

How to break a CAPTCHA system in 15 minutes with Machine Learning
https://medium.com/@ageitgey/how-to-break-a-captcha-system-in-15-minutes-with-machine-learning-dbebb035a710

Python Exceptions: An Introduction
https://realpython.com/python-exceptions/

Python Metaclasses
https://realpython.com/python-metaclasses/

Building a Simple Web App with Bottle, SQLAlchemy, and the Twitter API
https://realpython.com/blog/python/building-a-simple-web-app-with-bottle-sqlalchemy-twitter-api/

Python – Regular Expressions Practical Guide
http://devarea.com/python-regular-expressions-practical-guide/#.Wki2nN_iZhE

A fast high-level screen scraping and web crawling framework.
https://scrapy.org

A fast high-level screen scraping and web crawling framework.
https://pyfiddle.io/

IUT : projet JavaScript – jQuery à rendre

A rendre pour le 2 avril au plus tard


Comment le rendre

Faites un fichier README.txt et déposez-le ici


Sujet

Ce que vous voulez tant que c’est dans le cadre de ce que l’on a vu. Vous avez tout le Web comme inspiration !
N’oubliez pas de me donner le nom et le mot de passe pour se connecter !
Si vous gérez des profils différents (admin / user ou autre), donnez moi les noms et mots de passe de différents profils !


Fonctionnalités obligatoires

  • Connexion + déconnexion (vu en cours)
  • Effets jQuery sur les éléments
  • Appels JSON : au moins deux appels en plus de ceux vus en cours

Sujets possibles

  1. Site de partage de photos
  2. Site de cocktails (cf ci-dessus)
  3. e-rated : site d’appréciations (selon des sujets, à définir)
  4. Ask-a-question : site où l’on pose des questions sur des sujets divers, et des gens répondent
  5. Write-a-book-together : site où l’on se connecte et où on peut écrire un livre à plusieurs
  6. Wedding-couple-site : site où l’on uploade + partage des photos de mariage + livre de commandes
  7. Playing-cards-collection : site où on scanne + échange des cartes (Magic the gathering)
  8. Polls-and-surveys : site de création de sondages (= QCM, exemple très beau ici : quipoquiz)
  9. Poems-generator : faire un cadavre exquis qui génère des poèmes + possibilité pour les utilisateurs de les noter / d’ajouter des mots
  10. The-future-of-post-it : faire un carnet de choses à faire pour les utilisateurs, qui envoie des mails de rappels de ces choses à des dates données
  11. Gift-ideas : un site où l’on va faire des idées de cadeaux / suggérer des idées de cadeaux + les noter (les meilleurs ressortent en premier)
  12. Le-bon-recoin : refaire le bon coin en plus simple
  13. Suggest-crawlers : site de suggestions : on clique sur un mot, il en suggère plein d’autres avec + définitions / liens de sites pour chacuns
  14. Tv-fans : site de présentations + notes d’émissions télé
  15. Faire le jeu SokoBan vu en cours, avec la possibilité de login, enregistrement. Pour les appels JSON supplémentaires, lorsque l’utilisateur choisit un tableau, s’en souvenir (= AJAX) et lorsqu’il se reconnecte, le remettre directement. Puis enregistrer son score lorsqu’il a terminé un niveau + montrer les meilleurs scores.

Pour les sujets qui suivent, ils sont possibles mais plutôt complexes et demandent plus d’investissement. Si vous êtes motivés, demandez-moi plus d’informations, je vous expliquerai les difficultés que vous allez rencontrer.

  1. Turn-by-turn : faire un jeu multijoueurs en tour par tour (jeu de cartes, de poker, ou de plateau etc)
  2. Chat-with-someone : site de chat/discussion
  3. A-maze-ing : site où l’on peut se ballader dans un labyrinthe et essayer d’en trouver la sortie

Sujet imposé si vous n’avez pas d’idée

Cocktails : on se connecte, on a une liste d’éléments (récupérés en JSON) disponibles, on coche ceux qui nous intéressent, on valide, c’est envoyé, et le retour en JSON affiche les cocktails qu’il est possible de faire avec ce que l’on a coché.


Ce que vous devez rendre

Idéalement

Une Url vers un site Web (utilisez Alwaysdata par exemple)

Si vous n’avez pas le choix

Les fichiers source de votre projet


Pour favoriser votre organisation

Utilisez ce que l’on a vu en cours (Google boilerplate)


Librairies autorisées

Interdiction d’utiliser une librairie JavaScript qui ne vienne pas des sites autorisés précédemment


Retard

Après le mardi 2 avril minuit

Passé ce délai ce sera 1 pt par 2 heures de retard (je prendrai en compte la date de réception du mail).
Pour ceux qui essaient vraiment d’aller jusqu’à la dernière minute, toute heure entamée est comptée comme une heure complète. Exemple : un point en moins si je le reçois le 3 avril à 00:01.
N’oubliez pas de me donner le nom et le mot de passe pour se connecter !


Copier-coller

  • Copie sur une autre personne (« je se savais pas comment implémenter telle ou telle fonctionnalité dont j’avais besoin pour aller plus loin, je l’ai copiée sur un autre ») :
    • si la personne est clairement nommée : note pour la fonctionnalité divisée par 2 (uniquement la moitié du travail a été faite) ;
    • 0 aux deux personnes sinon ;
  • Si je m’aperçois que vous avez bêtement copié collé des sources Internet, je vous convoquerai pour vous demander de m’expliquer la fonctionnalité, et :
    • si vous ne savez pas m’expliquer le code alors 0 ;
    • si vous savez m’expliquer tout le code alors votre note totale sera divisée par vous + le nombre de contributeurs à ce projet, ce qui se rapprochera certainement de 0 aussi.

Voici un exemple de ce que vous pouvez faire, si vous choisissez le projet cocktails.


PDFs

JavaScript
jQuery

Nginx

Nginx hints / aide

Mon objectif était :

  • Si on tape l’URL sans le / à la fin, il redirige en ajoutant le / à la fin
  • Si on tape l’URL avec le / à la fin, tout doit fonctionner
  • Tout ne doit être que statique et les fichiers doivent obligatoirement exister, sauf index.html et index.htm
  • J’en suis donc arrivé à ces règles, plus « proches » de la configuration possible dans Nginx :

    1. Filtre « custom » avec / à la fin. Si oui, n’accepter que index.html ou index.htm
    2. Filtre « custom » en ignorant le / à la fin. Si oui, le nom, qui doit être forcément un fichier, sinon, rediriger en ajoutant un / pour qu’il reboucle au début
      location ~* ^/unity/(?<p>.+)/$ {
        root /web/htdocs/unity;
        try_files /$p/index.html /$p/index.htm /$p =403;
        access_log off;
        expires 1h;
      }
      location ~* ^/unity/(?<p>.+) {
        root /web/htdocs/unity;
        try_files /$p $p @redirect_with_slash_at_the_end;
        access_log off;
        expires 1h;
      }
      location @redirect_with_slash_at_the_end {
        return 301 $scheme://www.mywebsite.com$request_uri/;
      }
    

Python : faire un « beep » sur Debian

Le code est simple, il fautt avoir paplay d’installé (je ne sais pas si c’est installé par défaut dans Debian).

A partir de là voici un exemple de code qui joue un fichier ogg :

# python3
>>> import subprocess
>>> subprocess.Popen([
... "paplay",
... "/home/olivier/.local/blah/Mission_Failed.ogg"
... ]).poll()
>>>

Sublime Text : licence. Oui. Licence.

Oui.

TAX INVOICE

TAX INVOICE? One image:

Run away!

Je l’ai fait. OUI.
Sublime Text licence bought

J’ai même choisi toutes les catégories dans lesquelles il peut entrer :

  • développement
  • développement divers
  • développement Internet
  • développement Django
  • Php
  • programmation C
  • programmation JavaScript
  • Python
  • geek
  • gestion de projet
  • euh j’arrête, il peut entrer… partout ! (aucune digression, merci !)

Je lutte activement contre la mentalité Française latine : j’essaie d’expliquer à tout le monde qu’il faut arrêter de scier la branche sur laquelle on (= les développeurs) est assise : si vous pouvez avoir quelque chose de gratuit, mais que vous vous en servez souvent, faites le calcul : s’il vous fait économiser du temps, donc de l’argent, bah la conclusion est simple : il faut que cela continue. Pour que cela continue, payez (moins que ce qu’il vous a fait économiser, sinon c’est pas intéressant) mais payez, bon sang !

J’ai évolué de ce côté et je fais tout pour que les personnes que je fréquente – et étudiants – aillent dans ce sens.

Donc oui, Sublime Text me fait gagner du temps tous les jours depuis plusieurs années, et aujourd’hui, la conclusion est évidente : il m’a fait gagner bien plus que 80 €.

Donc… je résume, une image vaut mille mots. Et même si pour certains c’est vieux, le principe reste éternel :

Anastacia - I paid my dues

PS : pour ceux qui ne savent pas ce que « OMFG » signifie, ne le dites à personne, et allez vite chercher sur Internet. Promis on ne le dira à personne. C’est comme « RTFM ». Promis.

python, flask, https et wsgi howto

Je ne vais pas dire le temps que j’ai passé à réussir à mettre ça en oeuvre si quelqu’un fait ça en quelques minutes j’aurais honte… mais disons que j’ai passé beaucoup de temps à le faire tourner, mais je suis vraiment impressionné par sa rapidité. Voici – via ma petite expérience – comment faire tourner un serveur Web très optimisé, en mode production  :
  • flask
  • wsgi
  • https

https / certbot

Je vous donne des pistes, à vous de finir.
L’idée, c’est que je veux paramétrer pour la n-ième fois un nouveau nom de domaine via certbot, et je ne veux pas qu’il touche à la conf de nginx. L’idée c’est que :
(1) Je le fais simplement via certbot-auto puis après,
(2) toutes les autres fois j’utilise « -d » pour qu’il ne touche pas à la conf, et je le fais à la main, en m’inspirant de la conf que cert-bot a fait dans le (1)
Donc, pour faire le (1), je demande à certbot de me créer uniquement le certificat : ./certbot-auto certonly -d monsite.fr
Pour le https, regardez ici.

Python

Code exemple, qui lit un fichier JSON, lance le serveur et sert le fichier lu en tant que JSON :

import json

from flask import Flask, jsonify

app = Flask(__name__)

with open("mon fichier JSON", 'r') as f:
    json_levels_full_description = json.load(f)


@app.route('/')
def index():
    return jsonify(json_levels_full_description)

if __name__ == '__main__':
    app.run(host='0.0.0.0', port=8004)
else:
    application = app

Attention : surtout ne faites pas le classique if __name__ == '__main__' car uwsgi inclut ce fichier, donc ce fichier n’est jamais un __main__, par contre la configuration au lancement a besoin d’une variable globale nommée application.

Nginx

Je copie colle la configuration d’un site qui tourne déjà en https, en voici un extrait diminué à mort pour ne pas polluer avec des règles personnelles, regardez bien le texte en gras, c’est le plus important pour faire tourner Nginx en collaboration avec wsgi :

server {
  server_name "~(www\.)?monsite\.(com|fr|org|biz|be)$";

  index index.html index.htm;

  access_log /blah/proxy-access.monsite.log proxylog;
  error_log /blah/proxy-error.monsite.log error;

  listen 443 ssl;
  ssl_certificate /blah/fullchain.pem;
  ssl_certificate_key /blah/privkey.pem;
  include /blah/options-ssl-nginx.conf;
  ssl_dhparam /blah/ssl-dhparams.pem;

  location / {
    include denied_clients;

    proxy_set_header Host $host;
    proxy_set_header X-Real-IP $remote_addr;
    proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    proxy_set_header X-Server $host;

    # (!) here it's uwsgi:
    include uwsgi_params;
    uwsgi_pass 127.0.0.1:8002;

  }
}

# redirect http -> https:
server {
  listen *:80;
  server_name "~^monsite\..*$";
  return 302 https://$host$request_uri;
}

Flask

Pour installer flask, et uwsgi, faites un environnement virtuel puis installez les deux :

$ python3 -m venv venvpython.3.6.6
$ source venvpython.3.6.6/bin/activate
$ pip install --upgrade pip
$ pip install flask

uwsgi

Il faut être dans le venv activé (voir paragraphe précédent), puis l’installer via pip :

$ pip install uwsgi

Enfin, le script de lancement !

uwsgi --chdir=/web/htdocs/blah \
    --module=flask_server:application \
    --master --pidfile=/tmp/uwsgi.blah.pid \
    --socket=127.0.0.1:8004 \
    --processes=5 \
    --uid=1000 --gid=2000 \
    --harakiri=20 \
    --max-requests=5000 \
    --vacuum \
    --home=/web/htdocs/blah/venvpython.3.6.6

flask_server correspond au fichier python flask_server.pydécrit dans la section python au début et application est le nom de la variable globale (lisez le code de la section python)

Lorsque vous le lancerez, tout se passera bien, mais l’exécutable ne sera pas en tâche de fond. Cela a un avantage : un CTRL-C et vous l’arrêtez ! L’inconvénient, c’est que vous n’avez pas la main.

Si vous voulez le lancer en tâche de fond et qu’il écrive les logs dans un fichier, ajoutez l’option     --daemonize=/var/log/uwsgi/blah.uwsgi.log


Notes d’amélioration :
– Le port 8004 peut être passé en tant que variable d’environnement à l’application flask via le paramètre `env` de la commande uwsgi, ce qui rend le code plus portable ;
– Les paramètres uwsgi peuvent être consignés dans un fichier my_app.ini et uwsgi serait alors lancé en faisant uwsgi my_app.ini

Python » PyPI

Contribuer à une librairie Python

Principes

  • Faire un dossier (ex : source)
  • Dans source, cloner la librairie à laquelle on veut contribuer via
    git clone [repo concerné]
  • Faire un dossier pour l’environnement pip (ex : pip_env_lib)
  • Dans pip_env_lib, installer l’environnement pip via pipenv install
  • Dans pip_env_lib, lancer le « shell pip » via pip shell
  • Rarement documenté et important :
    – aller dans le dossier source
    – faire pip install -e .

pip install -e . (n’oubliez pas le . qui change tout, cela indique le répertoire à utiliser) signifie « si jamais le shell Python en cours essaie d’accéder à la librairie à laquelle on veut contribuer, aller la chercher dans source ». En fait, concrètement parlant, pip fait simplement un lien symbolique entre le dossier source d’installation de pip (ici c’est pip_env_lib), et le dossier source. N’oubliez pas que cela ne concerne le shell Python en cours (ici pip_env_lib).

L’avantage pratique de ce « lien symbolique », c’est qu’il est directement relié au code source. Donc, lorsqu’on modifie le code source de la librairie elle-même (dossier source), elle est immédiatement disponible. Si on fait donc des fichiers de tests unitaires, il suffit de les relancer, les modifications du code source sont toujours instantanément prises en compte. Toujours dans le shell Python en cours (ici pip_env_lib).

Résumé : exemple de code

mkdir [source]
cd [source]
git clone [repo concerné]
cd ..
mkdir [pip_env]
cd [pip_env]
pipenv --python 3 install
pip shell
pip install -e .

.. et les modifications dans [source] peuvent commencer !


PyPI : nouveau package / contribution

La documentation détaillée en anglais se trouve ici.
Bien meilleure que ce qui suit, mais longue, longue…

Voici les notes prises à la volées, d’un contributeur plutôt habitué à faire des contributions dans le monde Python.
Toute nouvelle note / correction éventuelle est la bienvenue.

Il y a trois choses différentes qu’on a tendance à confondre, mais qui n’ont fondamentalement aucun rapport. On a tendance à les confondre parce qu’on essaie d’utiliser, pour des raisons pratiques, le même nom, mais dans trois cadres différents.
Ces trois choses à bien dissocier sont :
le nom du package PyPI : dans le fichier de déploiement setup.py, il est défini dans le paramètre « name » de la commande setup(). C’est ce qui est vu dans PyPI.
le nom du projet qu’on met dans github : c’est le répertoire qui contient tous les fichiers du projet (= il peut être totalement différent, en général on s’arrange pour les appeler d’un nom différent) = c’est ce qu’on clone dans l’ordre git clone [repo concerné]
– le développeur final qui fait un « import » dans son code : ce module là peut être aussi différent, car c’est du Python. En général, c’est le répertoire de premier niveau du package (il doit contenir, bien sûr, « __init__.py » pour être vu comme un package et importable par Python). En général : « from [ma lib] import [quelque chose]« .

En général, on essaie de s’arranger pour que le nom du package sur PyPI soit le même que le nom du package dans le code Python même.
Mais s’il est déjà pris (dans un des trois contextes), il faut forcément utiliser un autre nom, et c’est là qu’il faut faire attention à ne pas se mélanger les pinceaux.

Pour faire un nouveau package sur PyPI le plus homogène possible :

  1. essayer de faire un projet « vide »
  2. faites une version squelette « alpha1 »
  3. faites une mise à jour sur PyPI : si ça fonctionne, alors vous pouvez continuer. Sinon, il faut utiliser un autre nom

Versionning

Dans PyPI, les versions sont obligatoirement du type «a.b.c».
En général :
[a].[b].[c]
[a] = fonctionnalités totalement nouvelles / incompatibilité
[b] = améliorations / nouvelles "petites" fonctionnalités
[c] = correctifs bogues

setup.py

Lorsque vous faites une nouvelle contribution, vous devez obligatoirement fournir un fichier setup.py.
Dans ce fichier, setup.py, il y a une fonction, ici aussi, obligatoire : setup().
Cette fonction, parmi tous les paramètres, a un paramètre particulier : classifiers qui permet au niveau de PyPI de dire « ce package est rangé dans telle(s) catégorie(s) ». La liste des expressions disponibles se trouve ici.

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 !