Rémy HUBSCHER avatar Rémy HUBSCHER committed 0c022d1

Notes + Couchdbkit

Comments (0)

Files changed (2)

notes/djangocong-2012.rst

+
+Autolib :
+
+ - sentry
+ - django_compressor
+ - django-ajax-search
+ - lettuce
+
+
+Libération
+ - Supprimer debug-toolbar du install_apps en prod
+ - Faire des benchmakrs à partir des logs existants et les refaire 3 fois
+ - Si possible utiliser jinja2
+ - django-pancake et django-template-preprocessor
+ - django.template.loaders.cached.Loader (Absolument obligatoire)
+
+ - ORM : values() et values_list()
+ - Essayer d'utiliser .only() et defer()
+ - Éviter select_related()
+ - Ne pas oublier les abstract=True
+ - Minimiser les count() et les distinct()
+
+ Attention au Paginator de Django sur une grosse table. (OFFSET/LIMIT)
+
+ # La solution au Paginator
+ - InfinitePaginator()
+ - clamp_pagination()
+ - Seek Based Paginator
+
+ # Cacher les queryset
+ - django-cached-machine
+ - django-cachebot
+ - **johnny-cache** (dès écriture d'un objet dans une table liée au queryset, cache terminé)
+
+ # jinga pour utiliser jinja sur certaine app et pas d'autres
+
+
+Requests
+ - A success story bro
+
+
+Apps as Services 
+ - semver
+ - http://bit.ly/djangocong
+ - http://bit.ly/appstoservices
+ - http://wwW.12factor.net
+
+Admin
+ - django-admin-tools
+ - django-admin-tabs
+ - django-locking 
+ - django-massadmin
+ - django-ajax-select
+ - sesql
+
+
+SQL over 9000
+ - prefetched_queryset
+ - assertNumQueries
+
+
+Ne pas utiliser STATIC_FILES_DIR
+ - {% static %} au lieu de {{ STATIC_URL }}
+
+
+Sentry
+ - Aggrégateur de log
+ - raven.contrib.django + Sentry DSN
+ - raven.handlers.logging import SentryHandler
+ - sentry et raven
+ - pip
+
+
+Mobile
+ - Version minimiser automatiquement
+ - django-pipeline
+ - 1 seul fichier js, 1 seul fichier css
+ - Donner une impression de rapidité
+ - Tester, tester, tester.
+
+ - Varnish pour le cache
+ - Apache + modWSGI
+ - MySQL & Redis
+ - jitter cache
+
+ - Mesurer
+ - RAM / Network / HDD
+ - Logs
+
+ - statsd / collectd / metrology
+ - shinken
+ - loggly
+ - pyflakes
+ - git hooks
+ - self.assertMaxQueries(5)
+
+Repos.io
+
+Packager son projet Django
+ - distribuer / déployer son projet django
+
+ - git 
+   - pull sur le dépôt + tag
+   - PB : pb de rollback / pb de suivi, dépendances manuelles / traçabilité
+
+ - PaaS : Heroku, Ep.io, Gondor 
+ - Solution : Installer comme une bib
+    - simplicité
+    - traçabilité
+    - reproductibilité
+    - OS : deb / RPM
+    - pip ; setup.py
+
+ - Ménage à faire :
+    - NE PAS ALTÉRER sys.path
+
+ - Application réutilisable : package séparé
+ - Application embarqué : non réutilisable
+
+ - requirements : Django==1.4
+ - PIL==1.1.7
+ - pas de lien git dans les requirements
+
+ - setup.py
+     packages=find_packages()
+     include_package_data=True
+
+ - MANIFEST.in
+     include requirements
+     recursive-include project *
+
+ - settings
+    - project/default_settings.py
+ - Deployé
+    - from project;default_settings import * + extension
+ - utiliser add2virtualenv pour rendre wsgi et settings importables sans toucher au PYTHONPATH
+
+ - pip et packets privés
+    --find-links : file:///path/to/packages
+    --index-url : structure de type 
+
+  Bien héberger tous ses paquets
+
+  Déployer :
+    - python setup.py sdist
+    - pip install -U project --find-links
+
+ - http://github.com/brutasse/fab-bundle

src/couchdbkit.rst

+==============================
+Démarrage avec les bases NoSQL
+==============================
+
+:date: 2012-06-09 17:33
+:tags: flask, python, mongodb, couchdb
+:category: Python
+:author: Rémy Hubscher
+
+
+Introduction
+============
+
+Ça faisait un moment que je voulais me lancer dans un projet utilisant
+une DB NoSQL.
+
+Pour les projets web actuel nous n'en ressentions pas encore besoin.
+
+Au cours du dernier TP que j'ai animé à l'UTBM en LO52, j'ai compris
+qu'il était temps de faire le pas. (`Voir la vidéo`_).
+
+.. _`Voir la vidéo`: http://www.dailymotion.com/video/xrcs0p_zigbee-mesh-captors-django-service_tech
+
+Couchdbkit
+==========
+
+La première chose que j'ai essayé de faire c'est de lancer le getting
+started de la `documentation de couchdbkit`_
+
+.. _`documentation de couchdbkit`: http://couchdbkit.org/docs/gettingstarted.html
+
+Vous pouvez `regarder le gist`_
+
+.. _`regarder le gist`: https://gist.github.com/2888668
+
+ça permet de comprendre l'intégralité du fonctionnement, saisie, et
+lecture de la base. On écrit les views en javascript.
+
+L'arborescence est importante, selon ce que vous mettez, vous
+n'accèderez pas forcément à votre view.
+
+On peut voir une interface de navigation et d'administration de
+couchdb ici : http://localhost:5984/_utils/
+
+DjangoApp
++++++++++
+
+Ensuite, j'ai décidé de tester l'intégration à Django et là je n'ai
+pas eu de problème.
+
+Vous pouvez lancer l'example :
+https://github.com/benoitc/couchdbkit/tree/master/examples/djangoapp
+
+.. code-block:: console
+    
+    $ virtualenv apps
+    $ source apps/bin/activate
+    $ pip install -r requirements.txt
+    (apps)$ python manage.py syncdb
+    (apps)$ python manage.py runserver
+
+Ensuite vous testez ici : http://localhost:8000/ et c'est tout.
+
+Démarrer un projet Django CouchDB
++++++++++++++++++++++++++++++++++
+
+Pour démarrer "from scratch", il faut configurer votre projet :
+
+.. code-block:: python
+
+    COUCHDB_DATABASES = (
+        ('djangoapp.mesh’, 'http://127.0.0.1:5984/mesh'),
+    )
+
+    # ...
+
+    INSTALLED_APPS = (
+         ....
+         'couchdbkit.ext.django’,
+         'captor_mesh.mesh’,
+         ....
+     )
+
+Dans le fichier **models.py** on va ensuite créer notre modèle de
+document :
+
+.. code-block:: python
+
+    from couchdbkit.ext.django.schema import *
+    
+    class Captor(Document):
+        arduino_id = IntegerProperty() 
+        pin_id = IntegerProperty()
+        value = IntegerProperty()
+        date_time = DateTimeProperty(auto_now_add=True)
+    
+        def __unicode__(self):
+            return u'[%s] Arduino : %s - PIN : %s - Value : %d' % (
+                self.date_time.strftime('%H:%M:%S'),
+                self.arduino_id,
+                self.pin_id,
+                self.value)
+
+Pour créer automatiquement un formulaire à partir du modèle :
+
+.. code-block:: python
+
+    from couchdbkit.ext.django.schema import *
+
+    class CaptorForm(DocumentForm):    
+         class Meta:
+             document = Captor
+
+
+Nous avons des vues simples qui affiche simplement les templates :
+
+.. code-block:: python
+
+    def gauge(request, arduino_id):
+        return render_to_response('mesh/gauge.html', {'arduino': arduino_id})
+
+    def line(request, arduino_id):
+        return render_to_response('mesh/line.html', 
+                                  {'arduino': arduino_id})
+
+Nous avons d'autres vues plus compliquées où nous souhaitons afficher
+des informations de la base :
+
+.. code-block:: python
+
+    def index(request):
+	    if request.method == "POST":
+    	    return HttpResponseRedirect(
+                  reverse(request.POST['view'], 
+                          args=[request.POST['arduino']]))
+
+        arduinos = list(Captor.view('mesh/arduino'))
+            
+        return render_to_response('mesh/index.html', {'arduinos': arduinos}, 
+                                  RequestContext(request))
+
+.. code-block:: django
+
+    <html>
+      <head>
+    	<title>Mesh network</title>
+      </head>
+    
+      <body>
+    	<h1>Mesh network</h1>
+    
+    	<form action="" method="post">
+    	  <select name="arduino">
+		    {% csrf_token %}
+    		{% for arduino in arduinos %}
+    		<option value="{{ arduino }}">Arduino #{{ arduino }}</option>
+    		{% endfor %}
+    	  </select>
+    	  <select name="view">
+    		<option value="gauge">Gauge</option>
+    		<option value="line">Line</option>
+    	  </select>
+    	  <p><input type="submit" /></p>
+    	</form>
+      </body>
+    </html>
+
+Nous allons donc réaliser un fichier **_design/views/arduino/map.js** :
+
+.. code-block:: javascript
+
+    function(doc) { 
+         if (doc.doc_type == "Captor") 
+              emit(doc.arduino_id); 
+    }
+
+Et je ne suis pas allé plus loin avec CouchDB car la `magic` de
+l'accès à mes views décrites dans un fichier js séparé ne me convenait
+pas.
+
+PyMongo
+=======
+
+Ce qui est plaisant avec MongoDB, c'est de suivre le tutoriel dans le
+`Try it out`_ (un shell javascript dans une page web).
+
+.. _Try it out: http://www.mongodb.org/
+
+Intégration avec Django
++++++++++++++++++++++++
+
+Il y a un module nommé `django-mongodb-engine`_ qui permet d'ajouter le
+backend mongodb à l'ORM de Django.
+
+.. _django-mongodb-engine: http://django-mongodb.org/tutorial.html
+
+Du coup, il n'y a plus de foreignkey mais des ListField() et il y a
+donc quelques modification à l'utilisation.
+
+.. code-block:: python
+
+    from django.db import models
+    from djangotoolbox.fields import ListField
+    
+    class Post(models.Model):
+        title = models.CharField()
+        text = models.TextField()
+        tags = ListField()
+        comments = ListField()
+
+Pour mon réseau de capteur, il n'y a pas véritablement de changement.
+
+Comme c'était un peu trop simple de simplement installer
+`django-mongodb-engine`, j'ai décidé de recoder la chose en utilisant
+Flask et PyMongo.
+
+Un simple `pip install flask pymongo` est on est parti !
+
+N'hésitez pas à aller `voir le code intégral sur le GitHub <https://github.com/Natim/zigbee_mesh_captors/tree/master/mongodb_flask>`_
+
+En conclusion, ce qui est compliqué dans le NoSQL ce sont les
+requêtes.  Bien sur, nous avons les fameux `map_reduce` mais ils ne
+sont pas aussi fast-forward que nos requêtes SQL.
+
+Voici les deux requêtes sur lesquelles je me suis un peu casser les
+dents :
+
+Récupérer la liste des arduinos
++++++++++++++++++++++++++++++++
+
+La première, je me suis lancé dans un map_reduce au lieu de lire
+la doc en profondeur :
+
+On aurait voulu écrire :
+
+.. code-block:: sql
+
+    SELECT DISTINCT arduino
+    FROM mesh_captors
+
+C'est a peu prêt la même chose :
+
+.. code-block:: python
+
+    from pymongo import Connection
+	db = Connection().mesh
+    arduinos = db.mesh_captors.distinct('arduino')
+
+On se connecte à la database nommée `mesh` et on fait la requête sur
+la collection `mesh_captors` pour récupérer les valeurs distinctes de
+`arduino`.
+
+Récupérer la dernière valeur de chaque pin d'un arduino
++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+
+Là on aurait souhaité écrire cela :
+
+.. code-block:: sql
+
+    SELECT DISTINCT pin, value
+    FROM mesh_captors
+    WHERE arduino = 203
+    GROUP_BY pin
+    ORDER BY date DESC
+
+Il y a donc la selection du arduino, la selection des pins avec un
+group_by, puis la selection par date.
+
+.. code-block:: python
+
+    from bson.code import Code
+
+    reducer = Code("""
+                  function (doc, out) {
+                      if(out.date == 0 || out.date < doc.date) {
+                           out.date = doc.date;
+                           out.value = doc.value;
+                      }
+                  }
+                  """)
+
+    # We group lines by pin of the arduino
+    captors_values = db.mesh_captors.group(
+        key=['pin'], 
+        condition={'arduino': int(arduino_id)}, 
+        reduce=reducer, initial={'date': 0})
+
+Une fois écrit ça parait logique, mais ça m'a quand même prit un
+certain temps.
+
+La gestion des index
+====================
+
+Avec mon premier script j'ai réussi à faire 200000 entrées dans la
+base en moins de 10 minutes et mongodb ne pouvait plus faire les
+requêtes. J'ai donc dû rajouter quelques index.
+
+.. code-block:: python
+
+    from pymongo import ASCENDING, DESCENDING
+    db.mesh_captors.ensure_index([('arduino', ASCENDING), ('pin', ASCENDING), ('date', DESCENDING)])
+
+Conclusion
+==========
+
+Le NoSQL est vraiment pratique pour ce genre d'application, pas de
+database lock et une rapidité appréciable.
Tip: Filter by directory path e.g. /media app.js to search for public/media/app.js.
Tip: Use camelCasing e.g. ProjME to search for ProjectModifiedEvent.java.
Tip: Filter by extension type e.g. /repo .js to search for all .js files in the /repo directory.
Tip: Separate your search with spaces e.g. /ssh pom.xml to search for src/ssh/pom.xml.
Tip: Use ↑ and ↓ arrow keys to navigate and return to view the file.
Tip: You can also navigate files with Ctrl+j (next) and Ctrl+k (previous) and view the file with Ctrl+o.
Tip: You can also navigate files with Alt+j (next) and Alt+k (previous) and view the file with Alt+o.