Mots-clé : database

Les requêtes « à la » Django : howto / principes

Voici une requête « à la Django ».
« objects » est un objet statique destiné à faire les requêtes
p = un objet du modèle « Person » = un modèle base de données que j’ai fait
g = un objet du modèle « Game » = un modèle base de données que j’ai fait

J’ai crée un modèle intermédiaire « PersonGame » qui lie les deux tables en n/n. Pour comprendre l’idée :
Q(person=p) signifie « dont la personne == p »
~Q(person=p) signifie « dont la personne != p »

Si on met deux « __ » cela signifie « faire une jointure entre les deux modèles », par exemple « person__user » signifie « LEFT JOIN PERSON p ON p.id_user = user.id »

Donc pour tout reprendre :
« Aller chercher dans PersonGame toutes les personnes qui ne sont pas le joueur p »
PersonGame.objects.filter(~Q(person=p), game=g)

…et dont le username vaut « u »
.get(person__user__username=u)

Ce qui donne au final :
PersonGame.objects.filter(
    ~Q(person=p),
    game=g).get(person__user__username=u)

Et voici la requête qu’il aurait fallu écrire « à la main » :

SELECT * FROM PersonGame pg
WHERE pg.id_person != p.id
AND pg.id_game = g.id
LEFT JOIN Person pe on pe.id = p.id
LEFT JOIN User us on us.id = pe.id_user
WHERE us.username = u
LIMIT 1;

En fait ça peut paraître rébarbatif, ou surprenant, mais :
– ça tient en une ligne ;
– quand on a fait 3-4 requêtes comme ça :
  – on arrive à faire n’importe quelle requête un peu complexe très très vite ;
  – on arrive à lire très facilement n’importe quelle requête ;
  – le générateur de requêtes est incroyablement mieux optimisé que celui de Symfony (en fait, techniquement, il est parfait, il génère les LEFT JOIN exactement comme il faut et dans l’ordre le plus efficace en fonction des clés qu’on a précisées dans le modèle, voire il fait des requêtes plus efficaces que ce qu’on aurait éventuellement fait à la main (cela m’est arrivé par deux fois)).

Conclusion : ici aussi, en termes de maintenance = ce qui coûte le plus cher, c’est exceptionnellement rentable.

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 !

NodeJS qui sert des fichiers et accède à MongoDB

// Exemple copié collé et adapté sur stackoverflow :
// http://stackoverflow.com/questions/15630770/node-js-check-if-path-is-file-or-directory
// http://stackoverflow.com/questions/7268033/basic-static-file-server-in-nodejs
//
var http = require('http'),
    url = require('url'),
    fs = require('fs'),
    mongoose = require('mongoose'),
    fileSystem = require('fs'),
    path = require('path');
var mimeTypes = {
    "html": "text/html",
    "jpeg": "image/jpeg",
    "jpg": "image/jpeg",
    "png": "image/png",
    "js": "text/javascript",
    "css": "text/css"};

var server;
var Schema = mongoose.Schema;

mongoose.connect('mongodb://localhost/test');
var db = mongoose.connection;

var personneSchema = new Schema({
  nom: String,
  prenom: String
});
var Personne = db.model('Personne', personneSchema);

db.on('error', console.error.bind(console, 'connection error:'));
db.once('open', function () {
    console.log('DB connexion reussie');
    server = http.createServer(function (request, response) {
        if (request.url=='/personnes') {
            console.log("> JSON");
            Personne.find({}).select('nom prenom').exec(function (err, p) {
                if (err) {
                    return console.log(err);
                }
                var retour='[';
                for (var i = 0; i < p.length; i++) {
                    retour+='{'+ p[i].nom+', '+p[i].prenom+'},';
                };
                retour=retour.substr(0, retour.length-2)+']';
                response.writeHead(200, {
                    'Cache-Control': 'no-cache, must-revalidate',
                    'Expires': 'Mon, 26 Jul 1997 05:00:00 GMT',
                    'Content-type': 'application/json'
                });
                response.end(retour);
            });
            // Stopper tout traitement :
            return;
        }
        var uri = url.parse(request.url).pathname;
        var filename = path.join(process.cwd(), uri);
        console.log("> " + filename);
        fs.exists(filename, function(exists) {
            if ((!exists) || (fs.lstatSync(filename).isDirectory())) {
                console.log(">> fichier inexistant : " + filename);
                response.writeHead(200, {'Content-Type': 'text/plain'});
                response.write('404 Not Found\n');
                response.end();
                // Stopper tout traitement :
                return;
            }
            var mimeType = mimeTypes[path.extname(filename).split(".")[1]];
            response.writeHead(200, {'Content-Type':mimeType});

            var fileStream = fs.createReadStream(filename);
            fileStream.pipe(response);
        });

        //var p = new Personne({
        // nom: 'Pons',
        // prenom: 'Olivier'
        //});
        //p.save(function (err) {
        // if (err) {
        // return console.log(err);
        // }
        // var query = Personne.find();
        // query.select('nom prenom');
        // query.exec(function (err, p) {
        // if (err) return console.log(err);
        // for (var i = 0; i < p.length; i++) {
        // console.log(p[i].nom+' '+p[i].prenom);
        // };
        // console.log('Nombre total d\'enregistrements : '+p.length);
        // });
        //})
    });
    // Listen on port 8000, IP defaults to 127.0.0.1
    server.listen(8000);
    // Put a friendly message on the terminal
    console.log("Server running at http://127.0.0.1:8000/");
});

MySQL : astuces pour quelques ordres de base

Voici quelques astuces dont je me sers souvent, pour ne pas avoir à systématiquement les rechercher, je m’en suis fait une copie dans un petit champ texte :

  1. Création d’une base de données
    CREATE DATABASE z DEFAULT CHARACTER SET utf8;
  2. Initialisation d’une variable via une requête :
    SELECT @MON_ID:= ID FROM matable WHERE CHAMP='champ_recherche';
    puis on se ressert de cette variable pour faire une insertion en base de données :
    INSERT INTO autre_table (ID_CLE_EXTERNE, DESCRIPTION, DOCUMENT) VALUES
    (@MON_ID, 'valeur1', './valeur2');
  3. Relancer le service MySQL
    1. service mysqld stop
    2. attendre une trentaine de secondes afin que le cache soit vidé et que toutes les allocations mémoires soient correctement libérées
    3. service mysqld start
  4. Convertir une table en utf8 :
    Très important : si vous tapez ce qui suit avec pour objectif de convertir la table et les données cela ne fonctionnera pas :
    ALTER TABLE tbl_name CONVERT TO CHARACTER SET charset_name;
    Ce n’est qu’en utilisant les ordres suivants que la conversion sera faite par MySQL !
    ALTER TABLE t1 CHANGE c1 c1 BLOB;
    ALTER TABLE t1 CHANGE c1 c1 TEXT CHARACTER SET utf8;
  5. Renommer un champ :
    Ici, je renomme la clé primaire « ID » et je la passe en minuscules (« id« ) :
    ALTER TABLE ville CHANGE ID id int(11) NOT NULL auto_increment;
  6. Modification des AUTO_INC
    Pour changer la valeur d’un auto_inc (‘réinitialiser la valeur’, comme on le dit dans MySQL ici) :
    ALTER TABLE tbl_name AUTO_INCREMENT = N
  7. Créer un utilisateur pour une base de données (la manière sale)
    Important : d’abord sans le ‘.*’ sinon ça ne fonctionne pas, et puis refaire avec le '.*'.
    Très important : ici on autorise tout à l’utilisateur qui va être crée. C’est donc une méthode rapide mais de bourrin et ce n’est pas la bonne méthode si vous voulez faire un utilisateur proprement, destiné à exister pour longtemps. Dans ce cas il faut faire quelque chose de plus précis afin de ne pas tout lui autoriser.
    GRANT ALL PRIVILEGES ON basededonnees
    TO ‘nomutilisateur’@’192.168.2.16’
    IDENTIFIED BY ‘motdepasse’;
    GRANT ALL PRIVILEGES ON basededonnees.*
    TO ‘nomutilisateur’@’192.168.2.16’
    IDENTIFIED BY ‘motdepasse’;