PostgreSQL : mémo

Comment créer une base de données :
CREATE DATABASE interro WITH OWNER 'monutilisateur' ENCODING 'UTF8' TEMPLATE=template0;

Changer le mot de passe de l’utilisateur monutilisateur :
Se connecter en root puis :
root@inyab:~# su postgres
postgres@inyab:/root$ psql
could not change directory to "/root"
psql (9.1.18)
Type "help" for help.
postgres=# ALTER USER monutilisateur WITH PASSWORD 'nommotdepasse';

Comment pouvoir se connecter en python :

try:
    conn = psycopg2.connect('dbname=interro '\
                            'host=localhost '\
                            'user=monutilisateur '\
                            'password=monmotdepasse')
except Exception as e:
    print(e)
sys.exit(u'Script terminé')

Attention j’ai l’impression qu’on ne peut se connecter que sous le nom d’utilisateur sous lequel c’est exécuté, mais à valider.

Django : formulaire qui édite aussi des champs d’un modèle clé étrangère

Déclarer les modèles

Voici mes modèles en résumé, parce que vous y arriverez nécessairement un jour ou l’autre :

class Personne(models.Model):
    user = models.OneToOneField(User)
    date_naissance = models.DateField(default=None,
                                      null=True, blank=True,
                                      verbose_name=_(u'Birth date'))
    class Meta(BaseModel.Meta):
        ordering = ['date_v_debut']

Si on veut faire une forme qui édite nom, prenom et date_naissance : facile… pour date_naissance. Faire la forme avec les champs du modèle :

class ProfileForm(forms.ModelForm):
    class Meta:
        model = Personne
        fields = ('date_naissance',)

Ajoutons la date de naissance :

    a = _(u'Birthdate:')
    date_naissance = forms.DateField(
        label=a,
        widget=forms.DateInput(attrs={'title': a}))

Déclarer les champs de la clé étrangère

Etape précédente facile ! Mais pour les champs d’un modèle clé étrangère, ici User ?
D’abord l’exclure de la forme (ne vous inquiétez pas, je ne vous mets qu’une partie du code, mais vous aurez le code en entier à la fin !) : exclude = ('user',)
Ensuite mettre les champs dans __init__, ici exemple avec last_name :

    def __init__(self, *args, **kwargs):
        super(ProfileForm, self).__init__(*args, **kwargs)
        a = _(u'Last name:')
        self.fields['user_last_name'] = forms.CharField(
            label=a, max_length=100,
            widget=forms.TextInput(attrs={
                'title': a, 'size': 100, 'type': 'text',
                'placeholder': _(u'my last name'),
                'class': 'form-control'}),
            error_messages=self.e)

Donc là vous aurez tous vos champs affichés, mais : (1) ils ne seront pas remplis avec la valeur de votre modèle si vous êtes en mode édition et (2) ils ne sont pas ordonnés, car ajoutés à la fin = ils apparaîtront forcément à la fin.

Pré-remplir le champ

Voici comment pré-remplir le champ : ce n’est pas dans la déclaration de la forme, mais dans la déclaration de la vue via get_initial() :

class EditView(LoginRequiredMixin, generic.UpdateView):
    model = Personne
    template_name = 'my_home/profile/edit.html'
    form_class = ProfileForm
    success_url = reverse_lazy('my_home_index')

    def get_initial(self):
        initial = super(EditView, self).get_initial()
        a = self.object.user
        initial['user_last_name'] = a.last_name if a.last_name else u''
        return initial

Les étapes en résumé

Ce titre = principal = h1.
There’s no need to say more… 🙂

Modèles

User

Déjà fait, merci Django ! Ahahaha. Bon.

Personne

class Personne(models.Model):
    user = models.OneToOneField(User)
    date_naissance = models.DateField(default=None,
                                      null=True, blank=True,
                                      verbose_name=_(u'Birth date'))
    class Meta(BaseModel.Meta):
        ordering = ['date_v_debut']

Forme

Champs normaux + champs de la clé étrangère = du modèle « étranger » :
class ProfileForm(forms.ModelForm):

    class Meta:
        model = Personne
        fields = ('sexe', 'statut', 'est_fumeur',
                  'est_physique', 'date_naissance')
        exclude = ('user', 'est_physique')

    a = _(u'Birthdate:')
    date_naissance = forms.DateField(
        label=a,
        widget=forms.DateInput(attrs={
            'title': a,
            'class': 'form-control datetimepicker'}))

    def __init__(self, *args, **kwargs):
        super(ProfileForm, self).__init__(*args, **kwargs)
        a = _(u'Last name:')
        self.fields['user_last_name'] = forms.CharField(
            label=a, max_length=100,
            widget=forms.TextInput(attrs={
                'title': a, 'size': 100, 'type': 'text',
                'placeholder': _(u'my last name'),
                'class': 'form-control'}),
            error_messages=self.e)

Vue

Pré-remplir les champs :
class EditView(LoginRequiredMixin, generic.UpdateView):
    model = Personne
    template_name = 'my_home/profile/edit.html'
    form_class = ProfileForm
    success_url = reverse_lazy('my_home_index')

    def get_initial(self):
        initial = super(EditView, self).get_initial()
        a = self.object.user
        initial['user_last_name'] = a.last_name if a.last_name else u''
        return initial

Voilà résumé : Vue + Forme + Modèle. Inévitable sur des bons frameworks, et classique non ?

J’ai oublié une dernière chose : ces champs sont dans un OrderedDict() c’est à dire que c’est l’ordre dans lequel ont été mis les éléments qui importe, donc dans notre cas, ils seront affichés en dernier. Si jamais vous voulez les afficher en premier, vous êtes obligés de reconstruire un OrderedDict() dans lequel vous ajoutez vos champs dans l’ordre que vous désirez. Ici, je ne vais que remettre mon champ en premier, et remettre les autres.

Voici le code de __init__ final :

    def __init__(self, *args, **kwargs):
        super(ProfileForm, self).__init__(*args, **kwargs)
        field_user_first_name = forms.CharField(
            label=a, max_length=100,
            widget=forms.TextInput(attrs={
                'title': a, 'size': 100, 'type': 'text',
                'placeholder': _(u'my first name'),
                'class': 'form-control'}),
            error_messages=self.e)
        new_fields = OrderedDict([
            ('user_first_name', field_user_first_name),
        ])
        for k, v in self.fields.items():
            new_fields[k] = v
        self.fields = new_fields

Gmail : recherche avancée : les filtres

Oui je dis « les » filtre, mais je n’en ai essayé qu’un seul !

Vous pourrez lire un peu partout (y compris dans l’aide même de google !) que pour faire un filtre de recherche du genre :

mail envoyé par e@gmail.com ou mail envoyé par f@gmail.com ou mail envoyé par g@gmail.com

…il vous suffit de taper simplemement

from:e@gmail.com or from:f@gmail.com or from:g@yahoo.fr

Cela ne fonctionne pas… en tous les cas pour moi.

Mais j’ai trouvé la solution : faites des pipes !
Je vous parle pas du moyen classique de monter en grade dans le cinéma, mais du vrai « pipe », ALTGR et « 6 » de votre clavier.

Pour mettre plusieurs mails, c’est entre parenthèses, pas comme précisé dans la documentation de google :

Filtre « envoyé par e ou f ou g ou reçu de e ou f ou g »

from:(e@gmail.com|f@gmail.com|g@yahoo.fr) | to:(e@gmail.com|f@gmail.com|g@yahoo.fr)

Cas pratique

Avoir tous les échanges de tonton@live.fr : mails envoyés à cette personne mais aussi mails reçus : solution :

from:tonton@live.fr | to:tonton@live.fr

Django : django.db.utils.IntegrityError: table__new.colonne may not be NULL

C’est le genre de problème qui arrive très souvent lorsqu’on touche à une base de données, en ajoutant un champ.
Pourquoi ? Parce que par défaut, les champs crées sans paramètres ne doivent pas être vides.

Exemple concret : je veux rajouter pour un modèle, un champ « exemple » :

class Groupe(models.Model):
    description = models.CharField(max_length=150)
    exemple = models.CharField()

Je fais « makemigration / migrate » et là, horreur : « django.db.utils.IntegrityError: main_groupe__new.exemple may not be NULL »

La solution

  • Supprimer le dossier « migrations » de l’application concernée
  • Lancer un migrate --fake-initial qui va « simuler » un migrate, sans essayer de créer les tables
  • Lancer makemigration nomappli (!! c’est ici que si on oublie le nom de l’appli rien ne se passe !!)
  • Seconde astuce : supprimer le champ fautif
  • Lancer migrate : là il va supprimer de la base le champ fautif
  • Remettre le champ avec blank=True, default=None
  • Refaire makemigration puis migrate sans préciser le nom de l’appli, comme d’habitude.

Et votre nouveau code sera pris en compte :
class Groupe(models.Model):
    description = models.CharField(max_length=150)
    exemple = models.CharField(max_length=150,
                               blank=True, default=None)

Oui je sais ça a l’air long, mais en pratique ça prend deux-trois minutes seulement !

Varnish 4.x : comment faire un redirect

Après avoir passé plusieurs heures sans aucune réponse satisfaisante, j’ai enfin trouvé la solution du redirect.
Varnish n’a pas la possibilité de faire un redirect « simplement », il faut normalement laisser cela au « backend », c’est à dire au serveur derrière (Apache ou autre).
Mais moi je ne voulais pas. C’est mon droit non ?
Voici comment faire un redirect qui fonctionne :

sub vcl_recv {
    # Rediriger tous les ".fr" vers ".com"
    if (
        (req.http.host ~ "(.*)monsite\.fr$")
    ) {
        # ! error = envoyer vers la sous-routine "vcl_error"
        set req.http.x-redir = "http://www.bb.com" + req.url;
        return(synth(850, "Moved permanently"));
    }
}

L’astuce principale était : utiliser vcl_synth

D’après le code, je vous résume ce que j’ai compris : si jamais on a un status 850 qui semble être un ordre particulier, alors automatiquement changer les headers en y appliquant la redirection, et les renvoyer directement le résultat.
Donc, après la routine vcl_recv il vous suffit d’ajouter vcl_synth comme ceci :

sub vcl_synth {
    if (resp.status == 850) {
        set resp.http.Location = req.http.x-redir;
        set resp.status = 302;
        return (deliver);
    }
}

github : mémo pour les pressés

Voici un mémo de mon expérience très rapide de github, qui n’est destiné à l’origine qu’à moi, mais que je partage pour ceux qui voudraient aller vite :

  • Forker un repo : c’est à dire, faire « sa » propre branche d’un source pour pouvoir travailler dessus.
    Sur github : en haut à droite du « repo » principal, vous avec le menu « fork ». Cliquez dessus et c’est fini !
    Details »» ici ««
  • Copier en local
    De l’étape précédente, vous aurez une adresse genre
    https://github.com/VOTRE-USERNAME/repo-qui-vous-plait
    Faites :
    git clone https://github.com/VOTRE-USERNAME/repo-qui-vous-plait
    et git fera un copier coller en local des sources github
  • Ajouter le repo d’origine pour les futures synchronisations :
    Copier coller l’URL du repo d’origine qui est sur github :
    git remote add upstream https://github.com/repo-qui-vous-plait
  • Assurez-vous que tout est ok :
    Avec ce code :
    git remote -v
    Vous devriez avoir :
    origin https://github.com/VOTRE-USERNAME/repo-qui-vous-plait.git (fetch)
    origin https://github.com/VOTRE-USERNAME/repo-qui-vous-plait.git (push)
    upstream https://github.com/POSSESSEUR-DU-REPO/repo-qui-vous-plait.git (fetch)
    upstream https://github.com/POSSESSEUR-DU-REPO/repo-qui-vous-plait.git (push)
  • Ensuite ces étapes en boucle :
    • Repo origine distant vers local :
      Ce qui a été fait par les autres sur le repo général (s’il y en a) vers local.
      git fetch upstream
    • Repo perso distant vers local :
      Ce qui a été fait par les autres sur votre repo (s’il y en a) vers local.
      git checkout master
      puis
      git merge upstream/master
      Details »» ici ««
    • Noter vos modifications en local :
      Préciser à git de chercher (1) toutes vos modifications, option « a » (2) le commentaire, option « m ».
      git commit -am "Mon commentaire"
    • Appliquer vos modifications à distance :
      git push
      Details pas de github, mais en français et très bien faits :
      »» ici ««

La tendance python : vivement que la France rattrape l’international !

J’ai eu une discussion avec des fans de Symfony il y a quelques temps. Ils me soutenaient (avec une prétention qui m’a surpris) que Php était de très loin le langage le plus recherché actuellement. Ils se trompaient lourdement. Dire que Php est le langage le plus utilisé, oui. Dire qu’il est le plus demandé, et qu’il le restera, non.

Python est l’avenir

Aucune discussion possible.

Python c’est tout. End of story

Voici le mail que j’ai envoyé récemment à plusieurs de mes clients, et, qui explique la tendance python :

En tapant « most popular development languages » sur google, on voit que :

Enfin je l’ai appris ce matin de la part d’un professeur : deux universités de France retirent Php pour le remplacer par Python cette année (si vous êtes intéressé je vous dirais lesquelles, je n’ai plus en tête les villes de ces universités).

J’imagine les gens de mauvaise foi qui vont aller chercher sur le Web et sortir les deux ou trois sites qui mettent Php devant Python… oui vous allez en trouver, mais en proportion, la plupart des sites expliquent que la tendance d’aujourd’hui c’est Python et JavaScript. Quant à ce dernier, moi qui ai fait quelques sites en NodeJS, je confirme que c’est l’âge de pierre aussi bien côté serveur Web que côté langage JavaScript lui-même… peut être qu’enfin à sa version 6, il fera du scoping normal (lisez ici) déjà rien que ça c’est moisi, sans parler des principes des closures qui empêchent carrément de faire de grosses applications qu’on peut facilement maintenir… là aussi, je ne pourrai jamais convaincre des gens qui ont principalement fait du JavaScript sans avoir essayé aussi intensivement d’autres langages (on ne peut pas comparer dans ce cadre, et toute discussion devient alors impossible)…

PyCharm et Django : comment faire une requête directe

Si, comme moi, vous voulez faire des requêtes directement et voir toutes les tables, pour les modifier manuellement, rien de plus simple, il faut juste chercher sur le Web pendant pas mal de temps. Voici la solution pour économiser de précieuses minutes :

  • Lancer la console python (Tools » Python console)
  • Taper :

    from django.db import connection
    cursor = connection.cursor()
    cursor.execute("PRAGMA table_info(langue)")
    for c in cursor.fetchall():
        print(c)
  • Et vous obtiendrez un résultat qui pourrait ressembler à :
    (0, 'id', 'integer', 1, None, 1)
    (1, 'date_creation', 'datetime', 1, None, 0)
    (2, 'date_last_modif', 'datetime', 1, None, 0)
    (3, 'date_v_debut', 'datetime', 1, None, 0)
    (4, 'date_v_fin', 'datetime', 0, None, 0)
    (5, 'nom', 'varchar(50)', 1, None, 0)
    (6, 'locale', 'varchar(2)', 1, None, 0)
    (7, 'bidirectionnel', 'bool', 1, None, 0)
    (8, 'nom_local', 'varchar(50)', 1, None, 0)
    (9, 'active', 'bool', 1, None, 0)
  • Si vous voulez plus de détail, et que la console a des problèmes avec les accents, voici un code qui remplace les accents par un point d’interrogation :
  • Taper :

    from django.db import connection
    cursor = connection.cursor()
    cursor.execute("SELECT * FROM langue")
    for c in cursor.fetchall():
        for d in c:
            if type(d) is str:
                print(d.encode(
                    sys.stdout.encoding,
                    errors='replace'))
            else:
                print(d)
  • Et vous obtiendrez un résultat qui pourrait ressembler à :
    None
    b'Russian'
    b'ru'
    False
    b'???????'
    False