Sam & Max » sql http://sametmax.com Du code, du cul Sat, 07 Nov 2015 10:56:13 +0000 en-US hourly 1 http://wordpress.org/?v=4.1 Automatiser un peu plus SQLAlchemy declarative 3 http://sametmax.com/automatiser-un-peu-plus-sqlalchemy-declarative/ http://sametmax.com/automatiser-un-peu-plus-sqlalchemy-declarative/#comments Wed, 29 Oct 2014 12:34:00 +0000 http://sametmax.com/?p=12584 Avec l’intégration de l’interface déclarative d’SQLAlchemy, le projet Elexir est mort, et bien mort. Mais avec lui, la syntaxe la plus simple de déclaration pour cet ORM. En effet, par défaut, SQLA vous oblige à spécifier le nom de la table et l’attribut ID pour chaque classe :

from sqlalchemy import Column, Integer, String
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy import create_engine
 
Base = declarative_base()
 
class Person(Base):
    # explicit is better than... fuck it !
    __tablename__ = 'person' 
    id = Column(Integer, primary_key=True)
    name = Column(String(250), nullable=False)
 
class Animal(Base):
    __tablename__ = 'animal'
    id = Column(Integer, primary_key=True) # Paie ton DRY
    name = Column(String(250), nullable=False)
 
engine = create_engine('sqlite:///db.db.db...db')
Base.metadata.create_all(engine)

SQLA est très flexible, et il existe des tas de raisons pour vouloir un PK ou un nom de table custo. Mais dans beaucoup de projets, le nom de la table peut être le nom de la classe, et la PK peut être un ID auto incrémenté.

Heureusement, la lib vous permet de customiser absolument tout, même la manière dont on doit la customiser. Si c’est pas méta, tout ça…

Bref, on peut créer sa propre base déclarative qui va faire tout ça pour nous :

from sqlalchemy.ext.declarative import as_declarative, declared_attr
 
# Ceci sera le parent de tout nos objets
@as_declarative()
class Base(object):
 
    # Vu qu'on a pas vraiment envie de se réécrire la
    # métaclasse, SQLA nous file ce gentil décorateur 
    # pour déclarer des attributs qui ne sont pas des 
    # champs
    @declared_attr
    def __tablename__(cls):
        # le nom de la table est le nom de la classe
        return cls.__name__.lower()
    # L'id c'est la vie
    id = Column(Integer, primary_key=True)
 
class Person(Base):
    name = Column(String(250), nullable=False)
 
class Animal(Base):
    name = Column(String(250), nullable=False)

Evidemment il faut que vous soyez certains d’éviter les conflits liés à cette décision, mais c’est quand même vachement pratique.

]]>
http://sametmax.com/automatiser-un-peu-plus-sqlalchemy-declarative/feed/ 3
Le pandas c’est bon, mangez en 7 http://sametmax.com/le-pandas-cest-bon-mangez-en/ http://sametmax.com/le-pandas-cest-bon-mangez-en/#comments Sat, 10 May 2014 06:30:35 +0000 http://sametmax.com/?p=10113 Les bases de Numpy, je m'en vais vous présenter une lib qui roxx du poney dans le calcul numérique : Pandas.]]> Ceci est un post invité de joshuafr posté sous licence creative common 3.0 unported.

Bonjour à tous, jeunes tailleurs de bambou, suite à un article d’introduction à numpy par le grand maître Sam Les bases de Numpy, je m’en vais vous présenter une lib qui roxx du poney dans le calcul numérique : Pandas.

Pour faire simple, Pandas apporte à Python la possibilité de manipuler de grands volumes de données structurées de manière simple et intuitive, chose qui faisait défaut jusqu’ici. Il y a bien eu quelques tentatives comme larry, mais rien n’avait jamais pu égaler les fonctionnalités du langage R. Aujourd’hui Pandas y arrive en fournissant notamment le célèbre type dataframe de R, avec en prime tout un tas d’outils pour agréger, combiner, transformer des données, et tout ça sans se casser le cul. Que du bonheur!

Donc pour commencer, on installe le bousin par un simple : pip install pandas qui va si vous ne l’avez pas déjà fait, aussi télécharger/compiler/installer tout un tas de librairies dont numpy. Je vous conseille aussi d’utiliser ipython afin d’avoir une meilleure interaction avec les libs, notamment avec matplotlib en utilisant le switch ipython --pylab afin d’avoir directement les graphiques en mode interactif, ainsi que toute la bibliothèque numpy directement importée (en interne, ipython fera un import numpy as np).
On appelle la bête d’un simple:

In [1]: import pandas as pd

Oui je sais, la grande classe…

Tout est Series

Le type de base en Pandas est la Series. On peut le voir comme un tableau de données à une dimension:

In [2]: pd.Series(np.arange(1,5))
Out[2]: 
0    1
1    2
2    3
3    4
dtype: int64

La colonne de gauche représente l’index de la Series, normalement unique pour chaque entrée. La colonne de droite correspond à nos valeurs sur lesquelles nous voulons travailler.
L’index n’est pas forcément une suite d’entiers, et la Series peut être nommée:

In [3]: s=pd.Series([1,2,3.14,1e6], index=list('abcd'), name='ma_series')
In [4]: s
Out[4]: 
a          1.00
b          2.00
c          3.14
d    1000000.00
Name: ma_series, dtype: float64

A noter qu’un type-casting est systématiquement appliqué afin d’avoir un tableau de type uniforme (ici le data-type est du float64) qui peut être modifié (dans une certaine mesure) via Series.astype.

Le slicing c’est comme du fisting avec une bonne dose de vaseline, ça glisse tout seul:

In [5]: s['b':'d']
Out[5]: 
b          2.00
c          3.14
d    1000000.00
Name: ma_series, dtype: float64

Et oui, la sélection par indexation se fait sur… l’index de la Series. Ainsi s[‘a’] renverra la ligne dont l’index est ‘a’, mais Pandas est assez intelligent pour reconnaître si on lui demande de nous renvoyer des valeurs suivant l’ordonnancement du tableau de valeurs (comme numpy). Ainsi s[0] renverra la première valeur du tableau, qui ici est égale à s[‘a’].
Là où ça peut poser problème c’est quand notre index est une suite d’entiers, comme par exemple avec x=pd.Series(np.arange(1,5), index=np.arange(1,5)). Si vous demandez x[1], Pandas ne retrouve pas ses petits et vous retournera une zolie KeyError. Pour ces cas ambigus, il existe l’indexation stricte sur le nom de index de la Series via x.loc[nom_d'index], et l’indexation stricte sur le numéro d’ordre dans le tableau via x.iloc[numéro_d'ordre]. Essayez x.loc[0] et x.iloc[0] pour vous rendre compte de la différence.
Comme pour les préliminaires où il est bon de tâter un peu de tout avec de pénétrer dans le vif du sujet, laissons pour le moment l’indexation sur laquelle nous reviendrons plus tard, pour regarder d’un peu plus près comment faire joujou avec nos valeurs.

Un peu à la manière des arrays de numpy, on peut appliquer des fonctions mathématiques directement sur la Serie, ou passer par des fonctions raccourcis:

In [6]: s.sum()
Out[6]: 1000006.14

Ce qui revient au même que de faire np.sum(s) (rappelez vous, ipython avec –pylab a importé numpy dans la variable np).

La fonction describe est bien utile pour avoir un aperçu rapide de ce à quoi on a affaire:

In [7]: s.describe()
Out[7]: 
count          4.000000
mean      250001.535000
std       499998.976667
min            1.000000
25%            1.750000
50%            2.570000
75%       250002.355000
max      1000000.000000
Name: ma_series, dtype: float64

ce qui donne le nombre de données, la moyenne globale, la déviation standard, le minimum, les quartiles et le maximum de la Serie.

Le truc à retenir est que c’est l’index qui est primordial dans un grand nombre d’opérations. Ainsi si l’on veut additionner 2 Series ensemble, il faut que leurs index soient alignés :

In [8]: s2=pd.Series(np.random.rand(4), index=list('cdef'), name='autre_serie')
 
In [9]: s+s2
Out[9]: 
a               NaN
b               NaN
c          4.021591
d    1000000.401511
e               NaN
f               NaN
dtype: float64

Ici, seuls les index ‘c’ et ‘d’ étaient présents dans les 2 Series, Pandas effectuant avant l’opération d’addition une union basée sur l’index. Les autres entrées sont marquées en NaN, soit Not a Number. Une possibilité pour contrer ce phénomène et de dire à Pandas de remplacer les résultats manquants lors de l’union par 0:

In [10]: s.add(s2, fill_value=0)
Out[10]: 
a          1.000000
b          2.000000
c          4.021591
d    1000000.401511
e          0.563508
f          0.655915
Name: ma_series, dtype: float64

Mais si ce sont uniquement les valeurs qui nous intéressent, et non les indexations, il est possible de les supprimer:

In [11]: s.reset_index(drop=True)+s2.reset_index(drop=True)
Out[11]: 
0          1.881591
1          2.401511
2          3.703508
3    1000000.655915
dtype: float64

Oh joie, oh bonheur, je peux faire ce que je veux avec mes cheveux, enfin mes données…

Et PAN! dans ta frame

La DataFrame est l’extension en 2 dimensions des Series. Elle peut être vue comme un empilement de Series dont les index sont partagés (et donc intrinsèquement alignés), ou comme dans un tableur où les index sont les numéros de lignes et les noms des Series les noms des colonnes. Je ne vais pas décrire toutes les manières de créer une DataFrame, sachez juste qu’on peut les obtenir à partir de dictionnaires, de liste de liste ou de liste de Series, d’arrays ou de records numpy, de fichier excel ou csv et même depuis des bases de données, de fichier JSON ou HTML, et depuis le presse-papiers.

In [14]: genre=[['femme','homme'][x] for x in np.random.random_integers(0,1,100)]
 
In [15]: lateral=[['droite','gauche'][x] for x in np.random.random_integers(0,1,100)]
 
In [16]: age=np.random.random_integers(1,100,100)
 
In [17]: df=pd.DataFrame({'Genre':genre, 'Lateral':lateral, 'Age':age})
 
In [18]: df
Out[18]: 
    Age  Genre Lateral
0    69  femme  droite
1    46  homme  droite
2    89  homme  droite
3    14  homme  droite
4    74  homme  droite
5     5  femme  gauche
6    66  femme  droite
7    73  homme  gauche
8    99  homme  gauche
9    17  homme  gauche
    ...    ...     ...
 
[100 rows x 3 columns]

L’affichage par défaut depuis la version 0.13 est en mode ‘truncate’ où la fin de la DataFrame est tronquée suivant la hauteur de votre terminal, mais ça peut se changer via les divers paramètres à regarder sous pd.options.display.

Là donc nous avons une DataFrame de 3 colonnes (plus un index), chaque colonne étant en réalité une Serie :

In [20]: type(df['Age'])
Out[20]: pandas.core.series.Series

La sélection peut se faire de plusieurs manières, à chacun de choisir sa préférée (moi c’est Dafnée avec ses gros nénés). Ainsi pour avoir les 3 premières lignes des âges

In [21]: df['Age'][0:3]
Out[21]: 
0    69
1    46
2    89
Name: Age, dtype: int64
 
In [22]: df[0:3]['Age']
Out[22]: 
0    69
1    46
2    89
Name: Age, dtype: int64
 
In [23]: df.Age[0:3]
Out[23]: 
0    69
1    46
2    89
Name: Age, dtype: int64
 
In [24]: df.loc[0:3, 'Age']
Out[24]: 
0    69
1    46
2    89
3    14
Name: Age, dtype: int64

et oui, les noms de colonnes peuvent aussi être utilisés comme des attributs de la DataFrame. Pratique (qu’on n’attend pas).

L’une des forces de Pandas est de nous proposer tout un tas de solutions pour répondre à des questions existentielles tel que “quel est l’âge moyen par genre et par latéralité?”. Comme en SQL où la réponse sortirait du fondement d’une clause GROUP BY et d’une fonction d’agrégation, il en va de même ici :

In [25]: df.groupby(['Genre','Lateral']).aggregate(np.mean)
Out[25]: 
                     Age
Genre Lateral           
femme droite   45.476190
      gauche   49.208333
homme droite   41.571429
      gauche   55.823529
 
[4 rows x 1 columns]

OMG! c’est quoi c’t’index de malade? Un MultiIndex jeune padawan, qui te permettra d’organiser tes données par catégorie/niveau, et d’y accèder par le paramètre level dans pas mal de fonctions, mais ça je te laisse le découvrir par toi-même. Je ne vais pas non plus m’étendre plus sur toutes les possibilités offertes par les DataFrame, il y a tellement à dire qu’il faudrait plusieurs articles pour en faire le tour. Juste conclusionner sur la facilité d’intégration Pandas/matplotlib en vous disant que les Series et DataFrame ont une fonction plot permettant directement de visualiser les données, et ça, c’est juste jouissif.

Datetime dans les index

Je vous avez dit qu’on reviendrait sur les indexes, et là c’est pour rentrer dans le lourd (mais non pas toi Carlos). Pandas donc supporte l’indexation sur les dates, en reprenant et en élargissant les possibilités offertes par feu le module scikits.timeseries.
Prenons l’exemple de données (complètement bidons) fournies par un capteur à intervalle régulier sur un pas de temps horaire:

In [26]: dtindex=pd.date_range(start='2014-04-28 00:00', periods=96, freq='H')
 
In [27]: data=np.random.random_sample(96)*50
 
In [28]: df=pd.DataFrame(data, index=dtindex, columns=['mesure'])
 
In [29]: df.head()
Out[29]: 
                        mesure
2014-04-28 00:00:00  49.253929
2014-04-28 01:00:00   1.910280
2014-04-28 02:00:00   7.534761
2014-04-28 03:00:00  39.416415
2014-04-28 04:00:00  44.213409
 
[5 rows x 1 columns]
 
In [30]: df.tail()
Out[30]: 
                        mesure
2014-05-01 19:00:00  25.291453
2014-05-01 20:00:00  26.520291
2014-05-01 21:00:00  33.459766
2014-05-01 22:00:00  44.521813
2014-05-01 23:00:00  28.486003
 
[5 rows x 1 columns]

dtindex est un DatetimeIndex initialisé au 28 avril 2014 à 0 heure comportant 96 périodes de fréquence horaire, soit 4 jours. La fonction date_range peut aussi prendre en arguments des objets datetime purs au lieu de chaine de caractère (manquerait plus que ça…), et le nombre de périodes peut être remplacé par une date de fin.
Si l’on veut calculer, disons le maximum (horaire) par jour, rien de plus simple, il suffit de “resampler” en données journalières (‘D’ pour Day) et de dire comment aggréger le tout:

In [31]: df.resample('D', how=np.max)
Out[31]: 
               mesure
2014-04-28  26.298282
2014-04-29  28.385418
2014-04-30  26.723353
2014-05-01  24.106092
 
[4 rows x 1 columns]

Mais on peut aussi convertir en données quart-horaire (upsampling) en remplissant les données manquantes par celles de l’heure fixe:

In [32]:  df[:3].resample('15min', fill_method='ffill')
Out[32]: 
                        mesure
2014-04-28 00:00:00  49.253929
2014-04-28 00:15:00  49.253929
2014-04-28 00:30:00  49.253929
2014-04-28 00:45:00  49.253929
2014-04-28 01:00:00   1.910280
2014-04-28 01:15:00   1.910280
2014-04-28 01:30:00   1.910280
2014-04-28 01:45:00   1.910280
2014-04-28 02:00:00   7.534761
 
[9 rows x 1 columns]

Cependant, Pandas propose aussi d’autres possibilités non dépendantes des DatetimeIndex mais qu’il est bon de connaître, notamment celle pour remplacer les données manquantes avec fillna ou celle pour interpoler entre les données valides avec interpolate

In [52]:  df[:3].resample('15min')
Out[52]: 
                        mesure
2014-04-28 00:00:00  49.253929
2014-04-28 00:15:00        NaN
2014-04-28 00:30:00        NaN
2014-04-28 00:45:00        NaN
2014-04-28 01:00:00   1.910280
2014-04-28 01:15:00        NaN
2014-04-28 01:30:00        NaN
2014-04-28 01:45:00        NaN
2014-04-28 02:00:00   7.534761
 
[9 rows x 1 columns]
 
In [53]:  df[:3].resample('15min').fillna(df.mean())
Out[53]: 
                        mesure
2014-04-28 00:00:00  49.253929
2014-04-28 00:15:00  26.378286
2014-04-28 00:30:00  26.378286
2014-04-28 00:45:00  26.378286
2014-04-28 01:00:00   1.910280
2014-04-28 01:15:00  26.378286
2014-04-28 01:30:00  26.378286
2014-04-28 01:45:00  26.378286
2014-04-28 02:00:00   7.534761
 
[9 rows x 1 columns]
 
In [54]:  df[:3].resample('15min').interpolate()
Out[54]: 
                        mesure
2014-04-28 00:00:00  49.253929
2014-04-28 00:15:00  37.418016
2014-04-28 00:30:00  25.582104
2014-04-28 00:45:00  13.746192
2014-04-28 01:00:00   1.910280
2014-04-28 01:15:00   3.316400
2014-04-28 01:30:00   4.722520
2014-04-28 01:45:00   6.128641
2014-04-28 02:00:00   7.534761
 
[9 rows x 1 columns]

Voilà, j’espère que vous aurez plaisir à travailler avec cette librairie, il manquait vraiment un outil de cette trempe en Python pour l’analyse de données et je pense qu’on n’a plus trop grand chose à envier maintenant par rapport à des langages spécilisés. Je n’ai pas parlé de Panel qui est le passage à la troisième dimension, ni des possibilités d’export, notamment la df.to_html que je vous laisse le soin de découvrir.

A plus, et amusez vous bien avec votre bambou.

\o/

]]>
http://sametmax.com/le-pandas-cest-bon-mangez-en/feed/ 7
NoSQL : arrêtons de dire n’importe quoi 39 http://sametmax.com/nosql-arretons-de-dire-nimporte-quoi/ http://sametmax.com/nosql-arretons-de-dire-nimporte-quoi/#comments Sat, 22 Mar 2014 10:26:06 +0000 http://sametmax.com/?p=9844 J’ai regardé le mouvement NoSQL évoluer au fil des années. On y retrouve à peu près tout ce qui fait l’informatique depuis que le monde IT est monde : brillance et troll, hype et génie, utile et gadget, buzz et fact, sam et max, etc.

De plus on peut mettre n’importe quoi sous le label NoSQL, et du coup ça a été fait. En fait un fichier est déjà une base de données NoSQL :)

Mais rant mise à part, des projets comme redis, riak, elastic search ou mongodb changent vraiment la donne.

Malheureusement, tout comme d’autres technos du moment (prog asychrone, tout-http, pre-processeurs, generateurs…), les gens ont tendance à l’utiliser comme la barre de fer, la silver bullet, le passe-partout, le tournevis sonique, bref, le truc à tout faire.

L’adage populaire dit “quand on a un bon marteau, tous les problèmes ressemblent à des clous”. Or, je constate qu’au dessus de ça, les dev appliquent aussi souvent le dicton préféré d’un de mes colocs : “arrête de taper si fort, prend un plus gros marteau”.

Ca donne du NoSQL utilisé partout, pour tout, brandi comme LA solution, vendu à des débutants comme une panacée de traitement d’informations. Zob, vous vous doutez bien que ça pose problème, non ?

Anti-fact 1 : NoSql, c’est plus facile pour démarrer

Il n’existe pas à l’heure actuelle de base NoSQL embarquée qui arrive à la cheville de SQLite : ça marche partout, dans tous les langages, sans rien à avoir installer ou configurer pour la plupart des langages.

Dès que vous demandez à un débutant d’installer un truc, vous rajoutez une barrière d’entrée énorme.

De plus, il y a beaucoup, beaucoup, beaucoup plus d’hébergeurs qui fournissent du SQL que du NoSQL en solution par défaut. Et comme toute les technos legacy, il y a 100 fois plus de doc.

Enfin, il y a la fameuse question du “quoi” ? Quel système allez-vous installer ? Couch ? Casssandra ? Mongo ? On parle de NoSQL, ou de schemaless ? C’est pas la même chose ? Memcache et Redis, c’est que pour le cache ? Elastic Search, c’est que pour la recherche de texte ? Les données géographiques, je les mets dans quoi, mongo ou un GIS spécialisé ? Attends, j’ai entendu parler d’une super bdd de graph…

L’abondance de solutions, le manque de recul et les informations contradictoires disponibles rendent non seulement le choix difficile, mais en plus hasardeux. Car contrairement au monde du SQL, se gourrer en NoSQL peut vous pourir toute votre archi.

Souvenez-vous qu’il est beaucoup plus difficile de migrer son système de base de données NoSQL d’une solution à une autre car il n’y a pas ce petit détail en commun entre les produits : le SQL justement.

Anti-fact 2 : Avec NoSql, pas besoin de réfléchir à son modèle de données

Je crois que c’est ce qui me fait le plus grincer des dents. Les gens qui disent qu’on peut tout mettre dedans, hop, et on verra plus tard. J’ai vu les pires modèles de données possibles stockés en MongoDb ou Redis, parceque les gars qui avaient travaillé dessus avait juste dumpé leurs données sans réfléchir.

Une base NoSQL ne vous oblige pas à formaliser votre schéma, mais ça ne veut CERTAINEMENT PAS dire qu’il ne faut pas le faire. L’auteur de Redis a très bien expliqué le problème (je graisse pour donner une effet dramatique et puissant au message) :

Redis is not the kind of system where you can insert data and then argue about how to fetch those data in creative ways. Not at all, the whole idea of its data model, and part of the fact that it will be so fast to retrieve your data, is that you need to think in terms of organising your data for fetching. You need to design with the query patterns in mind.

C’est vrai pour tout système dans lequel on met ses données, SQL, NoSQL, fichier, mémoire, le tiroir de votre bureau…

Il faut penser au type des données, leurs formats, les relations entre les éléments, comment et à quelle fréquence vous allez les écrire, les lire, garantir la consistence de leurs relations et leur fraicheur (ou pas d’ailleurs, mais il faut en faire le choix). Les bases SQL sont contraignantes parce qu’elles vous obligent à penser, dès le début, en ces termes.

C’est vrai, vous êtes de grandes personnes, a priori vous savez ce que vous faites, vous n’avez pas besoin qu’on vous FORCE à le faire. C’est pour ça que j’aime le typage dynamique. Je ne veux pas que tu me demandes mes papiers pour déclarer une variable, je sais ce que je fais.

Seulement il faut le faire, et ce n’est souvent pas fait. Pire, le modèle n’est généralement jamais formalisé NULLE PART. Un schéma, c’est une doc. Sans doc, le coût d’entrée dans votre projet est élevé, sa maintenance est galère, le potentiel de bug lors de l’évolution est plus grand. Mais une doc c’est chiant à écrire et à tenir à jour.

C’est un des intéressants effets secondaires des ORMs : les classes de définition sont le modèle documenté dans sa structure, ses relations, ses limites, ses contraintes, ses tests et vérifications, etc. La doc par le code, j’adore.

Anti-fact 3 : NoSql, c’est plus performant

A chaque fois qu’on lit “x est plus performant que z”, il faut faire une pause et réfléchir deux minutes. Généralement il y a un piège.

Les performances, ça dépend toujours du contexte. Par exemple, Redis est plus performant à la lecture et l’écriture, mais les données doivent tenir en RAM, sinon ça bouffe sur la mémoire virtuelle. Autre chose, Redis est très lent à démarrer sur des gros jeux de données (ça peut aller à plusieurs minutes si vous avez des Go). MongoDB doit normalement pouvoir tenir une augmentation de charge de manière prédictive en rajoutant des noeuds. Mais sur un seul noeud, c’est toujours moins performant qu’un PostGres. Et 0.01 % des sites ont besoin de plus d’un serveur.

Par ailleurs, les performances sont très dépendantes de l’anti-fact 2. Il faut créer les bons index, avoir un cache correctement ajusté, faire des requêtes intelligentes. Pour tous les systèmes.

Bref, encore une fois, NoSQL n’est pas une techno magique. Il est contre-productif, et j’ai envie de dire même irrespectueux envers ses collègues, de la vendre comme telle.

Anti-fact 4 : NoSQL remplace le SQL

Tweeter tourne sur MySQL ET Memcache.

Stackoverflow utiliser SQL Server 2008 ET Redis.

Il y a carrément des sites qui utilisent PostGres et MongoDb en parallèle. En fait, il y a des outils pour les faire collaborer.

Nous sur notre plus gros site on utilise Redis pour les sessions, les compteurs, les crawlers, les queues et passer des données entre process. Pas juste pour le cache. On utilise PostGres pour les données complexes avec des queries lourdes. Et on utilise Solr pour le moteur de recherche.

Les bases NoSQL sont des nouveaux outils, qui sont mieux adaptés à CERTAINS usages ou à CERTAINS contextes. Pas tout, tout le temps, partout. C’est un outil en plus, pas un obligatoire remplaçant.

Par ailleurs, on peut utiliser PostGres comme une base de données clé-valeur ou JSON, on peut mettre SQLite complètement en mémoire vive, on peut utiliser MySQL comme un moteur de recherche de texte… Multiplier les points of failures dans une archi n’est pas toujours une bonne idée. Ces outils qu’on considère comme de l’histoire ancienne ont beaucoup plus de ressource que vous ne l’imaginez. Ils sont ultra performants. Des années et des années d’optimisation.

Le monde de la tech n’est jamais lisse. Jamais.

]]>
http://sametmax.com/nosql-arretons-de-dire-nimporte-quoi/feed/ 39
MySQL: créer un utilisateur avec une base de données à son nom sur laquelle il a tous les droits 11 http://sametmax.com/mysql-creer-un-utilisateur-avec-une-table-a-son-nom-sur-laquelle-il-a-tous-les-droits/ http://sametmax.com/mysql-creer-un-utilisateur-avec-une-table-a-son-nom-sur-laquelle-il-a-tous-les-droits/#comments Mon, 15 Oct 2012 15:07:09 +0000 http://sametmax.com/?p=2575 Le truc classique, qu’on fait tout le temps, et qu’on oublie toujours comment on a fait la dernière fois.

Vu qu’on a pas toujours PhpMyAdmin installé…

CREATE DATABASE nom_db;
GRANT ALL PRIVILEGES ON nom_db.* TO "nom_utilisateur"@"localhost" IDENTIFIED BY 'mot_de_passe';
FLUSH PRIVILEGES;
]]>
http://sametmax.com/mysql-creer-un-utilisateur-avec-une-table-a-son-nom-sur-laquelle-il-a-tous-les-droits/feed/ 11
Log des requêtes SQL faites par l’ORM Django en temps réel http://sametmax.com/log-des-requetes-sql-faites-par-lorm-django-en-temps-reel/ http://sametmax.com/log-des-requetes-sql-faites-par-lorm-django-en-temps-reel/#comments Fri, 16 Mar 2012 17:30:01 +0000 http://sametmax.com/?p=262 django-debug-toolbar ou connection.queries pour suivre les requêtes SQL de l'ORM Django.]]> Même en recherchant sur les sites spécialisés en anglais, je tombe toujours sur des solutions basées sur la django-debug-toolbar, l’affichage de connection.queries ou l’usage de devserver. Ça a son utilité (surtout la fabuleuse django-debug-toolbar), mais c’est compliqué, long à mettre en oeuvre, et pas très pratique.

Il y a beaucoup plus simple, et le plus beau, c’est que Django le propose en natif.

Par default, Django loggue toutes les requêtes SQL dans django.db.backends si DEBUG = True. Il suffit donc de configurer le logger, dans votre settings.py:

import os
import tempfile
 
LOGGING = {
    'version': 1,
    'disable_existing_loggers': False,
    'formatters': {
        'verbose': {
            'format': '%(levelname)s %(asctime)s %(module)s %(process)d %(thread)d %(message)s'
        },
        'simple': {
            'format': '%(levelname)s %(message)s'
        },
    },
    'handlers': {
        'tempfile': {
            'level': 'DEBUG',
            'class': 'logging.handlers.RotatingFileHandler',
            'formatter': 'verbose',
            'filename': os.path.join(tempfile.gettempdir(), 'django-debug.log'),
        },
        'console':{
            'level':'DEBUG',
            'class':'logging.StreamHandler',
            'formatter': 'simple'
        },
    },
    'loggers': {
        'django.db.backends': {
            'handlers': ['console', 'tempfile'],
            'level': 'DEBUG',
            'propagate': True,
        },
    },
}

Lancez ./manage.py shell:

In [1]: m = Table.objects.filter(title__isnull=False)
 
In [2]: m
DEBUG (0.018) SELECT "table"."id", "table"."title", "table"."original_title" FROM "table" WHERE "table"."title" IS NOT NULL LIMIT 21; args=()
Out[2]: [< Table object >, < Table object >, < Table object >, < Table object >, < Table object >, < Table object >, < Table object >, < Table object >, < Table object >, < Table object >, < Table object >, < Table object >, < Table object >, < Table object >, < Table object >, < Table object >, < Table object >, < Table object >, < Table object >, < Table object >, '...(remaining elements truncated)...']

Une version plus verbeuse sera aussi logguée dans un fichier temporaire. Par exemple, sous Linux il sera dans /tmp/django-debug.log.

Toutes les requêtes faites par le site Web seront également affichées dans le terminal sur lequel tourne le serveur, et dans le fichier temporaire.

Une fois que vous maitrisez le concept, je vous recommande plutot de mettre le logger django.db.backends dans votre local_settings.py, histoire d’éviter de l’avoir en prod :

LOGGING['loggers'].update({
        'django.db.backends': {
            'handlers': ['console', 'tempfile'],
            'level': 'DEBUG',
            'propagate': True,
        },
    }
)

Autre bénéfice, il suffit de commenter cette partie quand on ne souhaite plus afficher autant d’informations.

]]>
http://sametmax.com/log-des-requetes-sql-faites-par-lorm-django-en-temps-reel/feed/ 0