Leçons du jour, et des jours précédents

Vous lirez ici des petites notes personnelles prises au gré de mes recherches répétées sur mon moteur de recherche préféré, pour justement me répéter moins.

  • Créer des index uniques sur plusieurs colonnes nullables

    Par défaut, PostgreSQL suppose que deux valeurs NULL sont distinctes. Ça pose problème quand on veut des contraintes d’unicité sur plusieurs colonnes dont certaines sont nullables.

    Donc par exemple, si j’ai une contrainte d’unicité sur les colonnes a, b, c, d, ceci fonctionne :

    insert into machin values ("a","b","c", null), ("a","b","c", null);
    

    Mais moi c’est pas ça que je veux et ça fait au moins deux fois que je veux pas ça.

    On peut lui dire que deux valeurs NULL sont identiques avec la clause NULLS NOT DISTINCT à partir de PostgreSQL 15.

    On peut dire à Django de dire à PG que les NULL sont identiques avec le kwarg nulls_distinct=False dans la création de contrainte unique :

        class Meta:
            constraints = (
                models.UniqueConstraint(
                    name="unicity_by_perimeter_and_type",
                    fields=(
                        "perimetre_region", # nullable
                        "perimetre_departement", # nullable
                        "perimetre_arrondissement", # nullable
                        "annee",
                        "type",
                    ),
                    nulls_distinct=False, # !!!! OHHHHH SO CLEVEER
                ),
            )
    

    Source Stackoverflow

    La doc Django sur UniqueConstraint qui mentionne bien le kwarg nulls_distinct

  • Liste des icônes Wagtail

  • Routes vers l’admin Django

    Comment faire des liens vers des listes ou des formulaires dans l’admin Django ? Je suis allée voir dans le code source.

    "admin:%s_%s_add" % (opts.app_label, opts.model_name)
    
    "admin:%s_%s_changelist" % (self.opts.app_label, self.opts.model_name),
    
  • Faire un dump et l´importer proprement en PostgreSQL

    Faire un dump sans les infos de propriétaire et de privilèges :

    pg_dump --format=custom --no-owner --no-privileges lenomdemabase > lenomdemabase.pgdump
    

    Restaurer le dump :

    pg_restore --format=custom --no-owner --no-privileges --dbname lenomdemabase --username lenomdemonuser lenomdemabase.pgdump
    

    Dans le pg_restore la partie --username lenomdemonuser est TRÈS importante.

    Sinon le user propriétaire des tables sera postgres et ça va poser des problèmes.

  • Manipuler des adresses IP en Python

    La lib standard Python contient un module permettant de manipuler des adresses IP.

    La doc

  • Valeur intelligible d’un champ de modèle à choix multiples dans Django

    Pour avoir la valeur “jolie” d’un champ de modèle de type “liste déroulante”, avec un paramètre choices= : il existe la méthode magique instance.get_TRUC_display().

    Dans un template :

    
    {{ mon_instance.get_status_display }}
    
    

    Source

  • Capture d’écran vidéo sous OSX

    Cmd + maj + 5

    (Capture d’écran classique : Cmd + maj + 4)

  • Numéros de téléphone réservés au cinéma

    Des numéros non attribuables sont réservés par l’ARCEP afin de les utiliser dans des films ou des séries.

    Les portables et chaque région ont une plage allouée :

    • 01 99 00 .. ..
    • 02 61 91 .. ..
    • 03 53 01 .. ..
    • 04 65 71 .. ..
    • 05 36 49 .. ..
    • 06 39 98 .. ..

    Source Wikipédia

  • str.title() marche aussi si les mots sont séparés par des underscore

    "oh_my_snake_case".title()
    = "Oh_My_Snake_Case"
    

    C’est utile si on veut convertir du snake_case en CamelCase. Après il ne reste plus qu’à supprimer les _.

  • Justify-self n'a pas d'effet sur un élément flexbox

    Je vais bien finir par m’en souvenir.

    Si je veux un effet de ce type :

    A B C        D
    

    Je dois donner margin-left: auto à D.

  • Git reset mais garder les modifs non commitées

    git reset --keep
    

    May your uncommited changes never go missing.

  • Ce domaine reçoit-il des e-mails ?

  • pip-compile échoue et ne me dit pas pourquoi

    Parfois pip-compile se prend les pieds dans le tapis avec une erreur toute nulle :

    failed to parse pyproject.toml
    

    Pour avoir une erreur claire :

    pip install -e .
    

    Aujourd’hui, le problème, c’était le répertoire du projet Django à côté de deux répertoires qui cassaient l’autodiscovery.

    Pour résoudre, deux possibilités de modif du package.toml :

    [tools.setuptools]
    py-modules=[]
    

    ou bien :

    packages = ["mon package"]
    
  • Réparer un arbre wagtail

    Souvent j’ai cette erreur quand je manipule “programmatiquement” des pages Wagtail :

    NoneType object has no attribute _inc_path

    Pour ça :

    mana fixtree --full
    

    (NB. mana est un alias pour python manage.py 🧙)

  • Django ORM - filter prefetch_related

    from django.db.models import Prefetch
    
    Person.objects.filter(...)
      .prefetch_related(
        Prefetch(
          "order_set",
          queryset=Order.objects.filter(...),
          to_attr="orders_filtered"
        )
      )
    
  • Décimaux en Python

    Decimal(48.33)   # non
    Decimal("48.33") # oui
    
  • Attraper une ou plusieurs exceptions en Python

    try:
      # ...
    except ZeroDivisionError as e:
      print(e)
      raise # re raise that exception
    except ValueError:
      print("Write a number")
    finally:
      cleanup()
    
  • Ligne parfaite

    Théoriquement, 66 caractères, soit environ 33 em.

    Paraît-il.

  • Numéros de spam

    Ne pas répondre :

    • 01 62
    • 01 63

    • 02 70
    • 02 71

    • 03 77
    • 03 78

    • 04 24
    • 04 25

    • 05 68
    • 05 69

    • 09 48
    • 09 49
  • Empêcher le push sur main

    git config branch.main.pushRemote nope
    
  • Trouver un élément x qui remplit une condition dans l'itérable `trucs`

    next(x for x in trucs if condition)
    
  • Sécurité des iframes

    Option historique : X-frame-options.

    • Accepte DENY : impossible d’inclure le site dans une iframe ;
    • ou SAMEORIGIN : seulement possible d’iframer le site depuis lui-même.

    Option actuelle : CSP frame-ancestors

    Utiliser cette option de CSP en combinaison avec X-frame-options: DENY.

    Compatible chrome, firefox, safari 10+, Edge 15+, mais incompatible IE.
    Interroger les usagers/logs pour connaître les navigateurs.

  • Pip freeze

    pip freeze affiche toutes les dépendances pip installées dans le venv.

  • Hit me up

    hit me up = contactez-moi

  • Docker-compose build . - qu'est-ce que ça fait donc ?

    Dans un fichier docker-compose :

    build .
    image: truc
    

    Build l’image à partir du Dockerfile, et la tague “truc” pour économiser le temps de build.

  • Docker for mac — parler à l’hôte depuis le conteneur docker

    host.docker.internal
    
  • CORS - cross origin resource sharing

    Par défaut, les requêtes CORS n’envoient pas les cookies.

    Pour envoyer les cookies : xhr.withCredentials = true.

    Mais ça ne marchera pas si Access-Control-Allow-Origin vaut *.

    Aussi : dans l’en-tête Access-Control-Allow-Origin, il faut inclure le scheme.

  • Raccourcis clavier de navigation

    Passer d’un onglet à l’autre : ctrl + tab

    Passer d’une fenêtre à l’autre dans la même app : cmd + `

  • Git push force with lease

    git push --force-with-lease
    

    Évite d’écraser le travail des autres quand on fait un git push --force.

    Plus exactement, --force-with-lease n’écrase pas un historique qui n’aurait pas été récupéré au préalable depuis le distant.

    On combine avec --force-if-includes pour en outre vérifier que l’état du dépôt distant a été appliqué à notre historique local.

    Je viens d’ajouter un alias :

    git config --global alias.pfl "push --force-with-lease --force-if-includes"
    

    (MAJ 14 novembre 24 :

    • ajout –force-if-includes
    • ajout explications )

    Source

  • PostgreSQL en ligne de commande

    • \dt liste les tables (SHOW TABLES)
    • \d <table> <=> DESC <table>
    • \x eXpanded display ~ \G de MySQL
    • \list <=> show databases
  • Centrer horizontalement et verticalement

    .parent {
      display: flex;
      justify-content: center;
      align-items: center;
    }
    
  • Faire un point médian sur Mac

    Alt + Maj + F

  • Annuler le dernier commit git

    git reset HEAD^
    
    • On rembobine le curseur d’un cran.
    • Si en plus on veut supprimer les modifications : ajouter --hard.
  • Afficher la liste des branches et leur dernière modification

    for k in `git branch | perl -pe s/^..//`; do echo -e `git show --pretty=format:"%Cgreen%ci %Cblue%cr%Creset" $k -- | head -n 1`\\t$k; done | sort -r
    

    Source

  • Pour déplacer des fichiers plus vite

    cp app/services/metadata/elasticsearch{,_images}_synchronizer.rb
    
  • Changer le mot de passe root MySQL

    Ou le mot de passe de n’importe qui en fait.

    mysqladmin -u root password NEWPASSWORD
    
  • Magento - fusion des CSS cassée

    Magento introduit des erreurs de syntaxe si les CSS à concaténer contiennent des url() avec des quotes.

    Pas bien :

    background: #eee url( '../images/default/grid/loading.gif' ) no-repeat 5px 5px;
    

    Ça donne ceci :

    background: #eee url('https://my-domain.com/js/extjs/resources/images/default/grid/loading.gif'') no-repeat 5px 5px;
    

    Certains navigateurs s’emmêlent les pinceaux avec les deux guillemets finaux.

    Il faut veiller à déclarer les URL relatives sans apostrophes :

    background: #eee url(../images/default/grid/loading.gif) no-repeat 5px 5px;
    
  • Active Record - faire des jointures imbriquées avec conditions

    Source SO

    Try to change your where clause :

    Publication  .joins( :publication_contributors => :contributor )
      .where( :publication_contributors => {:contributor_type => "Author"}, 
              :contributors             => {:name => params[:authors]} ) 
    

    ActiveRecord api is not extremely consistent here: the arguments for where do not work exactly as those for joins. This is because the arguments for joins do not reflect the underlying SQL, whereas the arguments for where do.

    where accepts an hash whose keys are table names, and values are hashes (that themselves have column names as keys). It just prevents ambiguity when targetting a column that has the same name in two tables.

  • Dnsmasq sous Homebrew - mais où sont les logs ?

    Les logs de dnsmasq sont dans /var/log/system.log. Donc pour voir ce que dnsmasq a logué :

    grep dnsmasq /var/log/system.log
    

    Souvent, chez moi, les erreurs sont assez claires :

    dnsmasq[52895]: error at line 1 of /etc/dnsmasq.d/tea-solr dnsmasq[52895]: FAILED to start up

  • Développer en parallèle sur une gem et sur un projet qui utilise cette gem

    Pour travailler sur la gem locale :

    bundle config local.GEM_NAME /path/to/local/git/repository
    

    Pour lister les configs de gemmes locales :

    bundle config
    

    Pour supprimer une config de gemme locale :

    bundle config --delete local.GEM_NAME
    
  • Zsh - erreur "no matches found"

    Mentionné dans ce billet : quand je veux invoquer une tâche rake avec des arguments, comme ceci :

    rake my:task:name[my_arguments]
    

    J’obtiens une erreur “zsh: no matches found”, et c’est chiant. Je suis obligée d’écrire ça :

    rake "my:task:name[my_arguments]"
    

    La solution est de modifier le fichier .zshrc pour dire à zsh de ne pas partir en erreur s’il ne trouve pas de match…

    Dans le .zshrc :

    unsetopt nomatch
    
  • Restaurer un fichier supprimé

    Trouver le commit qui a servi à supprimer le fichier :

    git rev-list -n 1 HEAD -- 
    

    Restaurer le fichier :

    git checkout ^ -- 
    
  • Sauvegarder les résultats de request.sql dans result.txt

    mysql -ublabla -h le-host -p --batch lenomdeladb < request.sql > result.txt
    

    Juste pour me souvenir que ça marche, et que ce n’est pas la peine de re-googler à chaque fois.

  • Rechercher et remplacer avec sed dans plusieurs dossiers

    find ./prefixe-du-dossier-* -type f -exec sed -ibak -e 's#search#replace#g' {} \;
    
  • Vim - astuces de l’estimé collègue deudtens

    Au-delà du i pour insérer

    i : insérer là où est le curseur (insert)

    A : insérer en fin de ligne (APPEND PUTAIN !)

    a : pas intéressant (comme i, mais un caractère plus loin)

    o : insertion sur une nouvelle ligne

    Je copie-colle et c’est ma joie

    Copier la ligne : yy

    Couper la ligne : dd

    Coller : p

  • Git rebase onto

    git rebase --onto master oldbase mabranchenow
    

    @tut_tuuut faut la lire à l’envers git rebase —onto A B C je veux appliquer les commits de C jusqu’au branchement avec B au dessus de A

    — Yves Brissaud (@crev) 5 novembre 2014

  • Retourner au dossier précédent

    Retourner au dossier où on était juste avant :

    cd -
    

    Retourner sur la branche précédemment active :

    git co -
    

    Rebase sur la branche précédemment active :

    git rebase -
    
  • Commit de fixup

    git ci --fixup hashducommit
    

    Pour faire un “amend” à distance lors du prochain git rebase -i.

  • Git add amélioré

    Pour ajouter au prochain commit tous les fichier supprimés du système de fichiers :

    git add -u
    

    (-u pour update)

  • Utiliser zgrep

    zgrep -c 9791027900015 *.gz
    

    Compte le nombre d’occurrences de zgrep 9791027900015 dans tous les fichiers .gz du répertoire.

  • Savoir quel .gitignore a .gitignoré un fichier

    git check-ignore mon-fichier.sql --verbose
    

    Ne marche que pour git ≥ 1.8.3 sorti en mars 2013.

    Source Stackoverflow

  • Faire le ménage dans un dépôt git

    Supprimer toutes les branches déjà mergées dans la branche courante :

    git branch --merged | grep -v "\*" | xargs -n 1 git branch -d
    

    Supprimer toutes les branches déjà mergées dans le master du remote :

    git branch -r --merged | grep -v master | sed 's/origin\//:/' | xargs -n 1 git push origin
    

    Supprimer les branches qui existent en local mais pointent sur une branche supprimée du remote :

    git fetch -p
    

    (-p comme « prune ».)

  • Grep sur phpinfo() en ligne de commande

    php -r 'phpinfo();' | grep php.ini
    
  • Éviter d'entrer son mot de passe quand Vagrant démarre avec un montage NFS

    Méthode sale si vagrant est le seul programme qui modifie les exports NFS :

    sudo chown `whoami`:staff /etc/exports
    

    Il y a une méthode plus « propre » qui consiste à autoriser sans mot de passe certaines commandes normalement réservées au sudo. Le seul problème c’est que je n’ai pas trop compris ce qu’il faut faire…

  • Ouvrir le dossier courant dans le finder

    open .
    
  • Chercher le mot « bétonnière » dans le dossier courant et les sous-dossiers

    grep -r betonniere .
    
  • Trier une liste de fichiers par taille

    du . --max-depth=1 -h | sort -h
    
    ls -Slr
    
  • Supprimer une branche distante

    git push origin :newfeature
    
  • Renommer une branche

    git branch -m oldname newname
    

    Syntaxe simplifiée pour renommer la branche courante :

    git branch -m newname
    

    Source Stackoverflow

  • Installer une vieille formule avec Homebrew

  • Déployer une branche avec Capistrano

    OK :

    ./bin/cap deploy -s branch=dev/thisismybranch
    ./bin/cap deploy -S revision=358e81487e467a982d8e85bada99260211b336f8
    

    Risky :

    ./bin/cap deploy -S revision=devel/thisismybranch
    

    If your local branch is not at the same stage as on origin, you will deploy origin/devel/thisismybranch, but you will tag your local commit devel/thisismybranch.

  • Trouver les fichiers qui contiennent une chaîne donnée

    grep -lr 9782848998299 .
    

    Explications :

    • -l pour afficher le nom du fichier et pas toute la ligne ;
    • -r pour chercher dans le dossier courant et dans les sous-dossiers.

    Attention à ne pas oublier le . à la fin de la commande.

  • Requêtes en MongoDB

    « where distribution is not null » :

    db.ebooks.findOne({distribution: {$ne: null}})
    

    « where distribution is null » :

    db.ebooks.count({distribution:null})
    

    Avec des embedded documents ça marche aussi :

    db.ebooks.count({"distribution.global":{$ne : null}})
    
  • Le patch suhoshin et les fichiers .phar

    Exécuter le script de vérification :

    curl http://getcomposer.org/installer | php
    

    Il y a des chances pour qu’il demande d’ajouter ceci dans un php.ini quelque part :

    suhosin.executor.include.whitelist = phar
    
  • Mettre à jour tous mes submodules git

    Oui parce que git submodule update ben ça ne marche pas du tout.

    Ce que je veux faire, en vrai, c’est git submodule foreach git pull origin master.

  • Oups j'ai oublié de créer ma branche de feature

    If you have

    ...-*-*-*-X-*-*-*-*-A 
    

    On branch A and X is the commit where you meant to create a new branch B starting at X you can simply

    git checkout A
    git branch B
    git reset --hard X
    

    or simply

    git branch -m A B
    git branch -f A X
    

    If, on the other hand, you wanted to start B from a different commit Y, you will need to rebase:

    git branch B A
    git branch -f A X
    git rebase --onto Y A B
    

    After backing up, of course ;)

  • Trouver un fichier dans un dossier ou sous-dossier d'après un bout de son nom

    find -iname *ton-morceau-de-nom*
    
  • Checkout sur une branche distante

    git checkout -t origin/haml
    

    -t pour « track ». Permet de faire un checkout sur une nouvelle branche nommée « haml », configurée pour suivre la branche « origin/haml ».

  • Git rebase ça fait quoi déjà ?

    git rebase master
    

    Rebase la branche courante (ici « feature ») sur « master ».

    git rebase master feature
    

    Rebase la branche « feature » sur la branche « master ».

    (Oui pasque je ne me souviens jamais de l’ordre des arguments…)

  • Supprimer des branches

    Pour supprimer une branche locale :

    git branch -d the_local_branch
    

    Pour supprimer une branche distante :

    git push origin :the_remote_branch
    

    Si quelqu’un a déjà supprimé la branche distante :

    git fetch -p
    

    Source

  • Lancer une commande CasperJS

    Commande de base :

    casperjs --includes=config/config-.coffee,config/client/.coffee test create-new-pn-client.coffee
    

    La variable test est un hack qui permet d’inclure plusieurs fichiers de configuration.

    Pour ignorer les erreurs de certificats HTTPS, ajouter l’option --ignore-ssl-errors=yes.