Test your django-piston API (with auth)

I have to build the API for one of my web service. It’s Django and django-piston powered application and it works well. Okay. I chose the TDD technique. So my problem was: how do I test the API parts which need authentication (basic HTTP, not OAuth for now)? The built-in test client of Django doesn’t seem to have such a feature.

So, here is my small workaround: you have to generate the HTTP_AUTHORIZATION field of your HTTP request. I wrote a small base test class for tests which need authentication:

import base64
import unittest
from django.test.client import Client

class BaseAuthenticatedClient(unittest.TestCase):
    def setUp(self):
        self.client = Client()
        auth = '%s:%s' % ('username', 'password')
        auth = 'Basic %s' % base64.encodestring(auth)
        auth = auth.strip()
        self.extra = {
            'HTTP_AUTHORIZATION': auth,
        }

You just have to replace username and password, and write your own test suite:

class TestAPIAuth(BaseAuthenticatedClient):

    def testrootauth(self):
        response = self.client.get('/api/aresource/id', {}, **self.extra)
        self.assertEqual(response.status_code, 200)

It should be OK. Have fun!

Posted in None at December 17th, 2009. Comments.

FreeBSD : serveur web Lighttpd

Je pars ici sur le fait que vous avez d’une part, les bases d’utilisation d’une FreeBSD (ou de n’importe quelle autre *BSD, voir distribution Linux) et d’autre part un système FreeBSD récent (7 series actuellement) installé, fonctionnel et connecté à internet.

Servir pour le web

Pour cela, vous devez déterminer quel serveur web remplira cette tache pour vous. Chacun ses choix, chacun ses arguments. Je choisi ici d’installer Lighttpd (a.k.a. Lighty), car il convient parfaitement à mes besoins et est simple pour une utilisation standard.

Je couvrirais ici son installation et sa configuration pour l’utilisation suivante :

Rien de bien sorcier, le tout étant d’être méthodique et de prévoir dès le départ où on veut aller.

Lighty

Rien de bien compliquer dans l’installation de Lighty par les ports :

$ cd /usr/ports/www/lighttpd

make install clean

Normalement, les options de compilation pour Lighty doivent vous être demandées. Voici celles que j’ai choisi :

Options de compilation Lighty

Options de compilation Lighty

Normalement, l’installation devrait se dérouler sans trop de problème. Ajoutez maintenant la ligne suivante à /etc/rc.conf :

lighttpd_enable="YES"
Rien d’autre à faire ici. Pour votre information, le fichier de configuration est placé suivant la norme FreeBSD : /usr/local/etc/lighttpd.conf. Pour lancer/arrêter/redémarrer le serveur, utilisez /usr/local/etc/rc.d/lighttpd start|stop|restart. Pour récupérer des infos en cas de soucis, c’est tail -f /var/log/lighttpd.error.log.

PHP5

Là aussi, on va utiliser les ports :

$ cd /usr/ports/lang/php5

make install clean

Rien de très palpitant ici. Il nous faut maintenant installer les extensions PHP voulues, toujours par les ports :

$ cd /usr/ports/lang/php5-extensions

make install clean

Lors de la configuration, la liste des extensions à installer devrait vous êtres demandée. Choisissez celles que vous voulez. Pour info, j’ai choisis celles-ci : ctype, curl, dom, gd, imap, mbstring, mcrypt, mysql, mysqli, pcre, posix, session, simplexml, xml, xmlreader, xmlrwriter, zlib.

A la fin de l’installation, ouvrez le fichier de configuration de Lighty et effectuez les modifications suivantes :

  • Dans la liste server.modules, dé-commentez mod_fastcgi.
  • Descendez et dé-commentez le bloc relatif à fastcgi.server :
    fastcgi.server = ( ".php" =>
                             ( "localhost" =>
                                  (
                                   "socket" => "/tmp/php-fastcgi.socket",
                                   "bin-path" => "/usr/local/bin/php-cgi"
                                  )
                             )
     )

Enregistrez et relancer Lighty. PHP est maintenant opérationnel (vous pouvez tester avec un <?php phpinfo(); ?>).

Django

C’est ici la partie qui m’intéresse le plus.

Premièrement, installez Django (notez que ceci n’est qu’une façon de faire) :

$ cd /usr/ports/devel/py-setuptools

make install clean

/usr/local/bin/easy_install django

OK. Bon, dans mon cas, j’ai une application située dans /storage/www/thomas/, qui sera accessible depuis le web par http://thomas.pelletier.im/. Adaptez suivant vos besoins.

  1. Créez un dossier public dans /storage/www/thomas/.
  2. Adaptez et placez-y le fichier lighty.sh suivant :
    #!/bin/sh
    app_path='/storage/www/thomas/'
    p='/tmp/thomas-django.pid'
    cd "$app_path"
    if [ -f $p ]; then
     kill $(cat -- $p)
     rm -f -- $p
    fi
    
    exec /usr/bin/env \
     PYTHONPATH="$app_path/.." python \
     manage.py runfcgi \
     daemonize=false \
     method=prefork \
     maxspare=2 \
     pidfile="$p"
  3. Rendez le exécutable par www :
    $ chown www:www lighty.sh
    $ chmod +x lighty.sh
  4. Créez un fichier vide django.fcgi dans public/ (pas nécessaire je pense, mais ça marche pour moi).
  5. Ajoutez le bloc suivant à votre ligtttpd.conf :
    $HTTP["host"] == "thomas.pelletier.im" {
            server.document-root = "/storage/www/thomas"
    
            url.rewrite-once = (
                    "^(/.*)$" => "/public/django.fcgi$1",
            )                    
    
            fastcgi.server = (
                    ".fcgi" => (
                            "django" => (
                                    "socket" => "/tmp/thomas-django.socket",
                                    "bin-path" => "/storage/www/thomas/public/lighty.sh",
                                    "check-local" => "disable"
                            )
                    )
            )
    
    }
  6. Enregistrez et relancer Lighty.

Normalement c’est bon, votre projet Django est maintenant servi par lighty !

Mercurial

NB : je ne vais pas entrer dans les détails de la configuration d’un dépôt mercurial.

Il existe plusieurs moyens de servir des dépôts mercurial. J’ai choisis hgwebdir. Le site officiel détail l’installation en CGI. Je trouve ça dommage. Pour ma part, j’utilise ici FastCGI.

Je pars du fait que vous avez vos dépôts déjà configurés et fonctionnels dans /mesdepots/ et que vous avez deux dépôts : un publique : “helloworld” et l’autre privé “myproject”.

Pour la petite histoire, helloworld sera accessible en lecture ET écriture par tout le monde. C’est fait exprès, à but purement pédagogique. myproject lui, n’est accessible en écriture et en lecture uniquement par vous.

Premièrement, créez un dossier hg dans /mesdepots/. Placez-y le fichier hgwebdir.fcgi suivant :

#!/usr/bin/env python
#

An example CGI script to export multiple hgweb repos, edit as necessary

adjust python path if not a system-wide install:

import sys

sys.path.insert(0, "/path/to/python/lib")

enable demandloading to reduce startup time

from mercurial import demandimport; demandimport.enable()

Uncomment to send python tracebacks to the browser if an error occurs:

import cgitb

cgitb.enable()

If you'd like to serve pages with UTF-8 instead of your default

locale charset, you can do so by uncommenting the following lines.

Note that this will cause your .hgrc files to be interpreted in

UTF-8 and all your repo files to be displayed using UTF-8.

#

import os

os.environ["HGENCODING"] = "UTF-8"

from mercurial.hgweb.hgwebdir_mod import hgwebdir from flup.server.fcgi import WSGIServer

The config file looks like this.  You can have paths to individual

repos, collections of repos in a directory tree, or both.

#

[paths]

virtual/path1 = /real/path1

virtual/path2 = /real/path2

virtual/root = /real/root/*

/ = /real/root2/*

#

[collections]

/prefix/to/strip/off = /root/of/tree/full/of/repos

#

paths example:

#

* First two lines mount one repository into one virtual path, like

'/real/path1' into 'virtual/path1'.

#

* The third entry tells every mercurial repository found in

'/real/root', recursively, should be mounted in 'virtual/root'. This

format is preferred over the [collections] one, using absolute paths

as configuration keys is not supported on every platform (including

Windows).

#

* The last entry is a special case mounting all repositories in

'/real/root2' in the root of the virtual directory.

#

collections example: say directory tree /foo contains repos /foo/bar,

/foo/quux/baz.  Give this config section:

   [collections]

   /foo = /foo

Then repos will list as bar and quux/baz.

#

Alternatively you can pass a list of ('virtual/path', '/real/path') tuples

or use a dictionary with entries like 'virtual/path': '/real/path'

WSGIServer(hgwebdir('hgweb.config')).run()

Toujours dans le même dossier, créez et complétez le fichier hgweb.config suivant :
[paths]
helloworld = /mesdepots/helloworld/
myproject = /mesdepots/myproject/

[extensions] hgext.highlight=

[web] style = monoblue allow_archive = bz2 gz zip baseurl = /hg/ pygments_style =

Dans le fichier .hg/hgrc de myproject, ajoutez les paramètres suivant dans le bloc [web] :
allow_push = monutilisateur
allow_read = monutilisateur
Vous allez maintenant devoir créer le fichier contenant les comptes virtuels (ie : les utilisateurs mercurial ne sont pas des utilisateurs système). Créez le fichier /mesdepots/hg/passwords et ajoutez une ligne par utilisateur (vous pouvez utiliser des outils en ligne pour générer les lignes à rajouter). Pensez que dans l’exemple, le nom d’utilisateur est monutilisateur.

Editez le fichier /usr/local/etc/lighttpd.conf en l’adaptant à vos besoin : ici, les dépôts sont accessibles depuis http://monsite.com/hg/.

$HTTP["host"] == "monsite.com" {
    server.document-root = "/mesdepots/"
    server.follow-symlink = "enable"
    fastcgi.debug = 1

url.rewrite-once = (
    "^/hg(.*)" =&gt; "/hg/hgwebdir.fcgi$1",
)

$HTTP["url"] =~ "^/hg/" {
    dir-listing.activate = "enable" 

    # My Private repositories
    auth.debug = 2
    auth.backend = "htpasswd"
    auth.backend.htpasswd.userfile = "/mesdepots/hg/passwords"
    $HTTP["url"] =~ "myproject/" {
        auth.require = ( "" =&gt; (
                "method" =&gt; "basic",
                "realm" =&gt; "This is a private repository. If not allowed, go fuck yourself.",
                "require" =&gt; "valid-user"
            )
        )
    }

    # Global config

    fastcgi.server = (
        ".fcgi" =&gt; (
            "hgwebdir" =&gt; (
                "bin-path" =&gt; "/mesdepots/hg/hgwebdir.fcgi",
                "socket" =&gt; "/tmp/hg.socket",
                "check-local" =&gt; "disable",
            )
        )
    )
}

}

Définissez l’utilisateur et le groupe www comme propriétaire du dossier /mesdepots/hg (chown -R www:www /mesdepots/hg). Donnez enfin les droits d’exécution à hgwebdir.fcgi (chmod +x /mesdepots/hg/hgwebdir.fcgi) et redémarrez votre lighty.

Posted in None at October 17th, 2009. Comments.

Premier contact avec le spam sur kiznet.fr

Jusqu’à maintenant, je n’avais jamais eu de soucis avec le spam sur kiznet.fr, et je ne m’en plaignait pas. Et là, il y a trois jours, tout à commencé : environ 1 000 spams en commentaires par jour. J’ai d’abord mit en place Akismet pour Django (avec quelques bidouilles). Le spam est passé de 1 100 à 900. Moins pire, mais c’est pas la joie. J’ai alors fait ce que je m’étais juré de ne jamais faire (et j’ai encore mal, je vous l’assure) : j’ai mit en place un captcha (en l’occurrence, reCAPTCHA). J’ai honte, mais au moins, ça fait moins mal. Outre le défi psychologique personnel qu’à représenté la mise en place du captcha, je me suis heurté aussi à un soucis technique : tous les snippets que j’ai trouvé concernant reCAPTCHA n’ont servi à rien ! Ils sont tous soit incomplet (tout le code n’est pas fournit) soit dépassé (Django évolue). J’ai donc mit la main à la pâte et j’ai codé ma propre application (d’une manière pas très élégante, mais pour se point précis, je trouve que Django n’offre pas une assez grande souplesse. A l’occasion, j’essayerais de voir si je ne peux pas écrire un patch), que je rendrais disponible sur mon bitbucket.

Au passage, le site n’a pas été disponible aujourd’hui de 18h40 à 18h45 : petit soucis de la part d’Alwaysdata, mais qui, heureusement et comme toujours, ont maîtrisé la situation.

Posted in None at July 14th, 2009. Comments.

Lightsearch

Bonjour. Il y a deux jours, j’ai trouvé que ce blog manque cruellement d’une fonction de recherche. J’ai donc fait un rapide tour des applications de recherche déjà existantes pour Django. J’en ai retenu trois :

  • djangosearch, qui n’est malheureusement plus en développement.
  • Haystack, qui est une vraie machine à gaz à mon sens (et beaucoup trop lourd pour un simple blog).
  • Divers snippets, soit trop simplets, soit qui ne marche pas (compatibilité avec Django 1.x par exemple), bref : rien qui ne me va.

Une conclusion s’imposait : développer ma propre application de recherche. Je vous présente donc Lightsearch.

Le but de cette application est de fournir un moyen simple d’effectuer des recherches simples. Si vous cherchez de hautes performances et une liste de fonctionnalités imposante, vous pouvez passer votre chemin. Ce n’est absolument pas le but de cette application.

Si vous souhaitez jeter un oeil, approfondir le sujet ou filer un coup de main, rendez-vous sur la page BitBucket de Lightsearch.

EDIT: voilà, la fonction de recherche est disponible sur Kiznet grâce à Lightsearch.

Posted in None at May 10th, 2009. Comments.

Gérer le fichier robots.txt avec Django

Lorsque j’ai mit en place LifeDo, je me suis rendu compte que Django m’envoyait souvent des mails a cause d’une erreur générée par un fichier robots.txt introuvable. J’ai alors cherché un peu sur le net et trouvé Django robots. Cette application a l’air relativement complète et efficace (gestion de règles spéciales par URL etc…). Cependant j’ai trouvé la solution un peu trop lourd pour mes besoins (fournir un fichier robots de deux trois lignes).

Voici donc une petite méthode que j’utilise afin de pouvoir fournir le fichier robots.txt. Elle n’est pas parfaite, loin de là, mais a au moins le mérite de faire ce pour quoi elle est prévue.

Je pars du principe que j’ai une application nommée misc (mais vous pouvez l’appeler comme bon vous semble) qui contient un fichier views.py. De plus, j’ai un urls.py a la racine de mon projet.

Il faut dans un premier temps définir la fonction qui va servir le fichier :

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
from django.views.decorators.cache import cache_page

from django.http import HttpResponse
from django.conf import settings

import os

@cache_page(60*60*24*365) # Met cette fonction en cache pour 1 an

def robots(request,
            template_name='robots.txt', # Nom du fichier (par defaut)
            mimetype="text/plain"): # MIME : a ne pas changer
    robots_path = os.path.join(settings.MEDIA_ROOT, template_name)
    robots_file= open(robots_path, 'r')
    robots_content = robots_file.read()
    robots_file.close()
    return HttpResponse(robots_content, mimetype=mimetype)

Ce code est donc à placer dans le fichier views.py.

Comme vous pouvez le voir, le fichier doit être placé (par défaut du moins) à la racine du répertoire où pointe le MEDIA_ROOT du settings.py. A ajuster selon vos besoin donc.

Ensuite, il faut faire le lien entre l’URL et cette nouvelle fonction. Voici ce à quoi votre urls.py doit ressembler :

1
2
3
4
5
6
7
8
from django.conf.urls.defaults import *

# Vos autres imports ici
from misc.views import robots


urlpatterns = patterns('',
    # Vos autres URLs ici
    (r'^robots\.txt$', robots),

)

Voici un petit fichiers robots.txt (que j’utilise), à titre d’exemple :

1
2
User-agent: *
Disallow: /admin/

Voilà. Comme ça, les robots ne vous embêterons plus :) .

Posted in None at May 5th, 2009. Comments.

Progression de LifeDo

Voilà, c’est juste pour annoncer que sous peu je vais pouvoir lancer une mise à jour majeur de LifeDo (elle a beau être majeur, elle n’est pas pour autant une révolution pour l’utilisateur, la plupart des changements sont des optimisations interne et la création de l’API).

En tout cas, c’est de plus en plus un bonheur de coder avec Django. J’ai honte de regarder mon code d’il y a quelques mois, je me demande comment j’ai pu faire pour êcrire des trucs pareil !

Ah sinon, comme l’a signalé benoitc sur IRC, Google Code se met enfin à Mercurial ! Je déteste SVN (système de dépôt de source que Google Code utilisait jusqu’à maintenant) donc je ne voulais plus héberger de projets là-bas. Je ne pense pas migrer vers Google-Code pour mes sources. J’irais peut-être y faire un tour quand mes dépôts bitbucket seront pleins !

Posted in None at April 25th, 2009. Comments.

Dépôt mercurial pour Djangoproject

Je viens de tomber par hasard sur un dépôt mercurial semblant être effectivement un dépôt officiel, servant de miroir au dépôt principal. Information à vérifier.

Voir le dépôt.

Posted in None at January 20th, 2009. Comments.

Django, on s’y fait vite

Je dois programmer un module pour Joomla! devant créer un carrousel 3D à partir d’albums photos (il faut aussi que je développe ces albums).

Bienvenue dans la pataugeoire ! Des dizaines de fichiers à éditer. Des noms dans le code pas clair. Du PHP (oui, bon ça c’est personnel). des dizaines de lignes de codes pour juste créer un modèle. Encore beaucoup d’autres pour utiliser la BDD. Écrire soit même le panneau d’administration (on s’y fait tellement vite à ce que tout saut automatique ou presque). Aucun moyen de recharger les changements affectés au code. Du temps (beaucoup !) perdu, pour un code qui ne marche pas, ou mal. Sans réelles erreurs de débogage.

Django, c’est (très) bien, et on s’y fait (très) vite.

(c’était un petit coup de gueule de jour (de la nuit plutôt). Pardon aux fanatiques de Joomla!)

Posted in None at December 29th, 2008. Comments.

Framework de commentaires de Django

Je viens de découvrir (grâce à un message sur la liste de diffusion pour développeurs) que Django possède un framework pour la gestion des commentaires (sur n’importe quel objet).

Je vais donc creuser un peu le sujet. Attendez vous à un nouveau billet plus complet là dessus, et une mise à jour du code du site par la même occasion.

Posted in None at December 8th, 2008. Comments.

Django et moi

Je ne sais pas si vous vous rappelez (ou si vous l’avez lu tout simplement), mais dans le dernier post, je parlais d’utiliser Django pour développer mon site perso qui sera hébergé sur mon futur hébergeur, OVH. C’est ce “projet” qui me permet d’apprendre à utiliser Django, je ne l’ai utilisé, même pas en test, nul par ailleurs. C’est donc vachement expérimental ! Alors du coup, je découvre au fur et à mesure plein de fonctions et de méthodes du framework Python, et qui rendent une grande partie de mon code inutile et redondant. Le projet est donc mal pensé à la base. J’hésite alors à mettre en ligne la version actuelle de mon site (je nommerais Kiznet maintenant), ou à attendre plus longtemps pour recommencer ce développement de manière plus “propre”. D’un coté, je voulais terminer vite le Kiznet pour pouvoir commencer à poster tous les autres projets que j’ai en tête, de manière à me faire un support. Le problème est que la version actuelle est, comme dit plus haut, mal pensée, lourde (tant au niveau exécution que schéma de base de données) et il lui manque pas mal de fonctionnalités qui me ferait réécrire pas mal de code pour les implémenter (à cause de ma mauvaise utilisation de Django). Voila, vous êtes informés ! Je remercierais grandement toute personne qui pourrait m’aiguiller sur quelque point que ce soit.

Bon surf !

Posted in None at November 22nd, 2008. Comments.