Sam & Max: Python, Django, Git et du cul » django http://sametmax.com Deux développeurs en vadrouille qui se sortent les doigts du code Wed, 05 Feb 2014 14:20:37 +0000 en hourly 1 http://wordpress.org/?v=3.3.1 Django, une app à la fois – faire une app de base http://sametmax.com/django-une-app-a-la-fois-faire-une-app-de-base/ http://sametmax.com/django-une-app-a-la-fois-faire-une-app-de-base/#comments Sat, 18 Jan 2014 15:22:24 +0000 Sam http://sametmax.com/?p=8789 Ca faisait longtemps que je n’avais pas fait avancer le projet django, an app at a time.

Voici donc un nouveau morceau qui montre comment écrire une petite app de base avec un template et une vue réutilisables qui serviront donc dans les prochaines apps du projet.

Comme d’hab, vous pouvez récupérer le code sur la teub de Guy et il y a une version française et anglaise.

flattr this!

]]>
http://sametmax.com/django-une-app-a-la-fois-faire-une-app-de-base/feed/ 2
Afficher le queryset d’une requête dans les logs SQL sous Django http://sametmax.com/afficher-le-queryset-dune-requete-dans-les-logs-sql-sous-django/ http://sametmax.com/afficher-le-queryset-dune-requete-dans-les-logs-sql-sous-django/#comments Wed, 25 Dec 2013 11:38:12 +0000 Max http://sametmax.com/?p=8447 C’est Noël, 2 articles rien que pour vous dont un très interressant de Sam.

L’ORM de django pour les bases de données est chouette, agréable à utiliser mais construit des requêtes SQL qu’on ne peut reconnaître lors de l’analyse des logs MYSQL du premier coup d’oeil. Et quand on a des centaines de requêtes par secondes c’est carrément impossible de s’y retrouver.

Ce que je vous propose ici c’est d’afficher le queryset (sa ligne et le fichier qui le contient) qui a permit d’exêcuter la requête SQL que vous voyez défiler dans les logs SQL sous forme de commentaires SQL.

L’application se nomme Django Sql StackTrace. C’est facile à installer et ça peut sauver des heures de debug.

Installation Django Sql StackTrace:

Une bonne PIP comme toujours pour bien commencer.

pip install django-sql-stacktrace

Dans votre fichier settings de django.

INSTALLED_APPS = (
    .........................
    'sqlstacktrace',
    .........................
)
 
SQL_STACKTRACE = True

La variable SQL_STACKTRACE sert à activer le debug.
Pensez à le désactiver lorsque vous n’en avez pas besoin.

Où se trouve mes super infos de debug ?

D’après la doc vous pouvez executer un watch

watch -n1 mysqladmin -u login -pmot_de_passe processlist --verbose

Chez moi ça n’a rien donné. Mais du côté des logs MySQL la magie a opérée.
Vérifiez tout d’abord que vos logs sont activés dans mysql.

vi /etc/my.cnf
[mysqld]
......
log = /var/logs/mysql.log
......

Comment on teste ça ?

Redemarrez votre serveur web, surfez sur les pages de votre projet et observez les logs MySql. Vous deviez voir quelques chose de similaire:

tail -F /var/logs/mysql.log
		  644 Query	SELECT `auth_user`.`id`, `auth_user`.`password`, `auth_user`.`last_login`, `auth_user`.`is_superuser`, `auth_user`.`username`, `auth_user`.`first_name`, `auth_user`.`last_name`, `auth_user`.`email`, `auth_user`.`is_staff`, `auth_user`.`is_active`, `auth_user`.`date_joined` FROM `auth_user` WHERE `auth_user`.`id` = 65290
/* File "/Users/max/work/mon_projet/apps/mon_apps/views/others.py", line 146, in user_public_page
	user = User.objects.get(pk=user_id)
*/

Observez cette merveille !
Entre /* */ sont les infos générées par django-sql-stacktrace. J’ai nettoyé quelques fichiers pour plus de lisibilité.
Vous avez droit au chemin du fichier de la requête, à la ligne de la requête et à la requête django elle-même.

Une alternative ? J’ai pas envie d’installer d’app.

Pour les grosses feignasses ou si vous voulez juste tester occasionnellement quelques queries vous pouvez utiliser la méthode extra pour ajouter vos propres commentaires.

videos = Video.objects.filter(status='online').extra(where=['1=1 /* ceci apparaitra dans les logs mysql ! */'])

Cependant le WHERE 1=1 peut causer quelques baisses de performances, mais lorsqu’on est en debug en local ça peut servir !

PS: Je rappelle également le formidable outil django-debug-toolbar qui devient vite indispensable.

Alors ? Elle est pas belle la vie ?

flattr this!

]]>
http://sametmax.com/afficher-le-queryset-dune-requete-dans-les-logs-sql-sous-django/feed/ 2
Ignorer certains caractères spéciaux dans un template django http://sametmax.com/ignorer-certains-caracteres-speciaux-dans-un-template-django/ http://sametmax.com/ignorer-certains-caracteres-speciaux-dans-un-template-django/#comments Mon, 09 Dec 2013 07:04:51 +0000 Sam http://sametmax.com/?p=8268 Hier Max me demandait comment mettre un template Javascript dans un template Django s’ils utilisent la même syntaxe.

La réponse : utiliser le tag “verbatim” :

{% verbatim %}
  Mettre ici le code que django doit afficher tel quel, sans interpréter.
{% endverbatim %}

flattr this!

]]>
http://sametmax.com/ignorer-certains-caracteres-speciaux-dans-un-template-django/feed/ 6
Ecrire une commande Django http://sametmax.com/ecrire-une-commande-django/ http://sametmax.com/ecrire-une-commande-django/#comments Wed, 04 Dec 2013 07:01:44 +0000 Sam http://sametmax.com/?p=7729 manage.py : c'est portable d'un projet à l'autre et ça permet d'utiliser l'ORM et les templates sans réglage puisqu'on a accès à tous les settings, automatiquement.]]> Quand vous avez à faire un script pour un projet Django, il est pratique de l’avoir sous forme de sous-commande de manage.py : c’est portable d’un projet à l’autre et ça permet d’utiliser l’ORM et les templates sans réglage puisqu’on a accès à tous les settings, automatiquement.

Malheureusement le wrapper de Django destiné à cela date un peu, il est plutôt lourd, pas très souple et utilise des pratiques qui ne sont plus au goût du jour depuis quelques années. Rien de rédhibitoire, mais tout de même.

D’abord, il faut placer sa commande dans un fichier portant le nom de la commande, et dans un package management.commands d’une de vos apps.

Par exemple, si vous voulez faire une commande “nettoyer_godmichets” dans l’application “sex_shop”, il faudra la mettre bien profondément dans le tréfonds de votre projet :

racine
│   ├── sex_shop
│   │   ├── management
│   │   │   ├── commands
│   │   │   │   ├── nettoyer_godmichets.py
│   │   │   │   ├── __init__.py
│   │   │   │   └── __init__.pyc
│   │   │   ├── __init__.py

Notez les fichiers __init__.py, indispendables sinon votre commande ne sera pas trouvée. Oh, et ‘sex_shop’ doit être dans INSTALLED_APPS.

Ensuite, votre commande doit être une classe héritant de BaseCommand. Sa méthode handle() sera appelée automatiquement au lancement de la commande.

from django.core.management.base import BaseCommand
from sex_shop.models import Godmichet
 
class Command(BaseCommand):
 
    def handle(self, *args, **options):
        for god in Godmichet.objects.all():
            god.clean()

Et vous pouvez lancer la commande :

./manage.py nettoyer_godmichets

Néanmoins généralement on voudra avoir un peu de décorum.

 
from optparse import make_option
 
from django.core.management.base import BaseCommand
from sex_shop.models import Godmichet, Marque
 
 
class Command(BaseCommand):
 
    # ici on peut mettre un message d'aide
    help = 'Fait briller dard dard les dards'
 
    # optionellement une aide pour les arguments
    args = 'marque1, [marque2], marque3'
 
    # On peut ajouter des options passables à la commande
    option_list = BaseCommand.option_list + (
        make_option('--dry-run',
            action='store_true',
            dest='dry_run',
            default=False,
            help='Affichage uniquement, aucune action réelle'),
        )
 
    def handle(self, *args, **options):
 
        # on retrouve dans args les arguments positionnels de la commande
 
        if not args:
            for god in Godmichet.objects.all():
                # Plutôt que print(), on peut utiliser ce wrapper
                # pour ecrire sur le terminal. Cela permet de bénéficier
                # automatiquement de l'option --verbosity
                self.stdout.write('Processing %s' % god.name)
 
                # La valeur des options est passée via kwargs.
                if not options['dry_run']:
                    god.clean()
        else:
            # l'utilisateur a passé des marques ? On nettoie que les gods
            # de ces marques...
            for marque in args:
                try:
                    gods = Marque.objects.get(name=marque).gods.all()
                    for god in gods:
                        self.stdout.write('Processing %s' % god.name)
 
                        if not options['dry_run']:
                            god.clean()
 
                except Marque.DoesNotExist:
                    # si la marque n'existe pas, on fait une erreur
                    # ceci arrête le script, retourne un code d'erreur 1
                    # et met le texte en rouge
                    raise CommandError('La marque %s n'existe pas' %s marque)

Et voilà, votre commande accepte maintenant optionnellement qu’on lui passe une liste de marques et/ou une option --dry-run:

./manage.py nettoyer_godmichet devilmaycry choucroutebestfriend --dry-run

En plus de ça, la commande accepte automatiquement :

  • --help
  • --verbosity
  • --settings
  • --pythonpath

Qui ont le même effet que sur toutes les commandes django officielles.

flattr this!

]]>
http://sametmax.com/ecrire-une-commande-django/feed/ 4
Changer le mot de passe du super utilisateur django en ligne de commande http://sametmax.com/changer-le-mot-de-passe-du-super-utilisateur-django-en-ligne-de-commande/ http://sametmax.com/changer-le-mot-de-passe-du-super-utilisateur-django-en-ligne-de-commande/#comments Sun, 01 Dec 2013 00:20:18 +0000 Sam http://sametmax.com/?p=8020 Vous avez oublié ce maudit mot de passe ? Impossible de se connecter à l’admin ?

./manage.py changepassword username

Pas besoin d’être root.

Souvenez-vous aussi que vous pouvez quasiment tout faire depuis un shell en faisant :

./manage.py shell

Ce qui vous permet d’importer les modèles de vos apps et faire toutes les querys que vous voulez.

flattr this!

]]>
http://sametmax.com/changer-le-mot-de-passe-du-super-utilisateur-django-en-ligne-de-commande/feed/ 0
Compter les doublons avec l’ORM Django http://sametmax.com/compter-les-doublons-avec-lorm-django/ http://sametmax.com/compter-les-doublons-avec-lorm-django/#comments Fri, 22 Nov 2013 08:28:48 +0000 Sam http://sametmax.com/?p=7761 GROUP BY et HAVING sont assez peu intuitifs en SQL, et encore moins avec l'ORM Django.]]> GROUP BY et HAVING sont assez peu intuitifs en SQL, et encore moins avec l’ORM Django.

Voici donc la recette pour compter le nombre d’occurrences des valeurs d’un champ, pour par exemple trouver les doublons.

En supposant une table Massage avec une valeur finish :

ID         FINISH
1          manuel
2          oral
3          oral
4          oral
>>> Massage.objects.values('finish').annotate(count=Count('id'))
{'manuel': 2, 'oral': '9': 'rectal': 1, 'paradoxal': -1}

Pour obtenir uniquement les doublons, il suffit de filtrer pour avoir les valeurs qui ont un count plus grand que 1 :

Massage.objects.values('finish').annotate(count=Count('id')).filter(count__gt=1)

flattr this!

]]>
http://sametmax.com/compter-les-doublons-avec-lorm-django/feed/ 11
Créer un super utilisateur Django sans prompt http://sametmax.com/creer-un-super-utilisateur-django-sans-prompt/ http://sametmax.com/creer-un-super-utilisateur-django-sans-prompt/#comments Wed, 20 Nov 2013 09:23:12 +0000 Sam http://sametmax.com/?p=7981 syncdb sans prompt, et donc pas de création de superutilisateur...]]> Automatiser le déploiement d’un projet Django passe par un syncdb sans prompt, et donc pas de création de superutilisateur :

./manage.py syncdb --noinput

Vous pouvez bien entendu toujours en créer un plus tard, avec :

./manage.py createsuperuser --username=vous --email=votre_mail

Mais il va vous prompter pour saisir le mot de passe ou alors mettre un mot de passe inutilisable automatiquement, ça ne résout pas votre problème. Une astuce consiste à le créer avec un code Python et à piper ce code dans un shell :

echo "from django.contrib.auth.models import User; User.objects.filter(username='vous').count() or User.objects.create_superuser('vous', votre_email', 'mot_de_passe')" | ./manage.py shell

Personnellement je colle ça dans une tache fabric. Parfois on peut même générer le mot de passe aléatoirement avec un uuid qui est printé localement.

flattr this!

]]>
http://sametmax.com/creer-un-super-utilisateur-django-sans-prompt/feed/ 5
La stack techno qu’on utilise pour faire un site Web, et pourquoi http://sametmax.com/la-stack-techno-quon-utilise-pour-faire-un-site-web-et-pourquoi/ http://sametmax.com/la-stack-techno-quon-utilise-pour-faire-un-site-web-et-pourquoi/#comments Mon, 11 Nov 2013 06:40:38 +0000 Sam http://sametmax.com/?p=7648 Une stack techno n’est pas une référence. Il n’y a pas de combo absolu qui rox absolument tout, c’est une question de contexte technique, financier, humain…

Mais c’est vrai que ça aide bien d’avoir sous les yeux les pratiques des autres.

Je ne vais pas expliquer pourquoi Python, je l’ai déjà fait.

Commençons plutôt par la partie purement Web, pour laquelle on utilise Django, le framework Web Python.

Max et moi avons tout deux fait du PHP avant, j’ai tâté des frameworks internes, du Symfony et plus tard du Zope. J’ai regardé du côté de Pyramid et de ses prédécesseurs, et Django est celui qui me plaît le plus. J’ai juste un peu forcé la main à Max :-)

Car oui, le framework a été avant tout un choix de goût.

Ce n’est pas un choix de performances : le framework n’a aucun impact dessus. Aucun. Les architectures ont un impact. Le framework, non. Votre bottleneck sera sur les IO, pas sur le CPU. Le choix de technos asynchrones peut avoir un impact, mais ce n’est pas une question de framework. Tornado, Twisted ou NodeJS, on s’en fout.

Donc Django, essentiellement parce qu’il me plait. Et il me plaît pour ces raisons :

  • Il y a un bon équilibre entre découplage et intégration. En général c’est soit très découplé et mal intégré, soit très bien intégré et très couplé.
  • C’est bien foutu et bien documenté. Et c’est stable. Vraiment très stable. Les core devs sont hyper sérieux.
  • C’est très versatile et ça peut faire plein de trucs out of the box, petits comme gros.
  • C’est assez facile à apprendre. Ça reste un framework, donc ce n’est pas la plus simple des démarches, mais dans le royaume des frameworks de cette taille, ça reste vraiment le plus simple.
  • La communauté est fantastique : il y a des centaines d’apps qui couvrent pratiquement tous les besoins.
  • Et bien entendu, c’est en Python.

En terme de base de données, on a fait du MySQL pendant longtemps. Ça a plutôt bien marché. Maintenant je commence mes nouveaux projets avec PostGres, qui est plus solide. Parfois je fais juste du Sqlite, parce que ça suffit.

Pas de NoSQL. Après plusieurs expériences avec MongoDB et CouchDB, je n’ai pas été convaincu que les bénéfices dépassaient le coût. Il faudrait un article complet là dessus (qu’on m’a d’ailleurs demandé).

Question OS. c’est du CentOS avec Max (il a plus l’habitude) ou du Ubuntu Server pour mes autres projets. Je reste sur les LTS. Ce n’est pas un choix très réfléchi, c’est surtout par habitude.

Pas de machine virtuelle. On a essayé, sans y trouver un grand intérêt :

  • Il faut quand même faire des scripts de migration, donc autant s’en servir pour le déploiement.
  • On perd en perfs.
  • Les erreurs liées au mal-fonctionnement d’une VM sont absolument indébuggable.
  • Si on ne fait pas la VM soit-même, il faut mettre ses couilles dans les mains d’un pestataire de service. J’ai horreur de ça.
  • Trouver des gens avec la compétence pour gérer une VM, c’est difficile. Un script de déploiement, c’est du code que tout dev saura déjà lire. Par extension ça veut dire que je m’y replonge facilement des semaines plus tard.

Et donc pour le déploiement, j’utilise fabric, avec fabtools.

Ce n’est pas la solution la plus efficace, d’autant que ça limite à Python 2.7, mais c’est la plus simple. C’est juste du code Python. N’importe qui peut comprendre le déploiement en 15 minutes. Ça se modifie vite, s’adapte facilement.

Il faut comprendre qu’on a jamais plus d’une dizaine de serveurs pour un projet, ces choix sont donc fait en fonction de cela. Il va sans dire que si vous gérez un parc de centaines de machines, ça ne sera pas du tout le même choix technique. Peut être que Chef ou des VM seront alors carrément plus interressant. Peut être que le NoSQL et sa capacité au scalling sera bien plus rentable.

Il ne s’agit pas de décrier les technos que nous n’utilisons pas. Il s’agit juste de dire, voilà les choix que nous avons fait, dans tel contexte, pour telles (bonnes ou mauvaises) raisons.

Durant les dernières années, on a ajouté Redis à notre stack. C’est un outil fantastique qui sert à tout : de la base de données pour les trucs simples (il y a des fois ou un schéma est overkill) à la solution de caching. C’est ce qu’on a de plus proche du NoSQL.

L’outil est tellement simple à installer (vraiment le degré zero de la maintenance, c’est beau) et à utiliser que ça ne vaut juste pas le coup de s’en priver.

Du coup, plus de memcache. Toutes les grosses requêtes sont sauvegardées dans Redis, dès qu’on fait un script qui a besoin de persistance temporaire, Redis, pour communiquer entre plusieurs process, Redis, pour toutes les opérations qui ont besoin de grosses perfs comme les stats, Redis. Vive Redis.

D’ailleurs on utilise Redis aussi comme broker pour notre gestionnaire de queues et de taches : celery. Si vous pythonez, je vous recommande chaudement celery pour toutes les tâches en background, les crawlers, les chaînes de process, etc.

On a aussi du moteur de recherche. Là on tappe dans du Solr (avec haystack). C’est très puissant, en tout cas syntaxiquement car ça ne fait pas de sémantique. Ne vous attendez-donc pas à rattraper Google. Mais c’est aussi méga chiant à configurer et très lourd. Je pense qu’un jour on va migrer sur ElasticSearch, mais c’est pas la priorité. Don’t fix what ain’t broken.

Devant tout ça on a Nginx. Comme beaucoup on a fait Apache => Cherokee => lighttp => nginx. Et franchement, je ne reviendrais jamais en arrière : plus léger, plus rapide, plus facile à installer et à configurer, plus versatile. Nginx fait tout, et mieux.

En proxy on a du gunicorn. Parce qu’on avait la flemme de configurer uwsgi et qu’on a pris l’habitude.

Après on utilise plein de libs, de petits outils, etc. Mais ça c’est le gros de notre archi.

flattr this!

]]>
http://sametmax.com/la-stack-techno-quon-utilise-pour-faire-un-site-web-et-pourquoi/feed/ 25
Mise à jour de Django-quicky pour supporter django 1.6 http://sametmax.com/mise-a-jour-de-django-quicky-pour-supporter-django-1-6/ http://sametmax.com/mise-a-jour-de-django-quicky-pour-supporter-django-1-6/#comments Sat, 09 Nov 2013 13:32:23 +0000 Sam http://sametmax.com/?p=7716 django-quicky ne marchait plus sur cette nouvelle version.]]> Avec Django 1.6 qui vient de sortir hier, j’ai pu m’apercevoir que django-quicky ne marchait plus sur cette nouvelle version. Un sombre histoire de setup_environ qui est deprecated. Comme je l’utilise (ce qui est mieux avec les outils qu’on code ^^), j’ai fait un petit correctif vite-fait.

J’en ai profité aussi pour ajouter deux fichiers de requirements avec des apps Python que j’installe très très souvent pour mes projets.

flattr this!

]]>
http://sametmax.com/mise-a-jour-de-django-quicky-pour-supporter-django-1-6/feed/ 2
Afficher l’IP d’un visiteur – Django vs Nginx http://sametmax.com/afficher-la-vrai-ip-dun-visiteur-django-vs-nginx/ http://sametmax.com/afficher-la-vrai-ip-dun-visiteur-django-vs-nginx/#comments Fri, 25 Oct 2013 03:53:30 +0000 Max http://sametmax.com/?p=7525 Lorsque l’on veut connaitre son ip on fait souvent appel à des sites du genre: whatismyip.com, mon-ip.com ou on utilise un ifconfig en ssh.
Des fois on a aussi besoin de connaître l’ip d’un visiteur sur son site, 2 petites méthodes pour le faire sous Django et Nginx.

Sous Django:

Dans l’Urlconf

urlpatterns = patterns('',
    # return client IP
    url(r'^my_ip$', get_ip),
)

Créez une vue du nom de get_ip qui sera utilisée par l’urlconf

from django import http
 
def get_ip(request):
    """
        Vue qui retourne l'IP du client
    """
    try:
 
    	# récupère l'ip du client
        return http.HttpResponse(request.META["REMOTE_ADDR"] )
 
    except Exception, e:
        return http.HttpResponse('error %s' % e)

Le code ci-dessus peut retourner l’adresse locale (127.0.0.1) dans ce cas il faut tester l’existence de la variable HTTP_X_REAL_IP, certains serveurs web ont besoin d’être configuré.

Sous Nginx:

Dans le fichier de configuration de nginx on écrit une nouvelle location

# return client ip
location /my_ip {
    default_type 'text/plain';
    content_by_lua 'ngx.print(ngx.var.remote_addr)';
}

il suffit de se rendre à l’url http://monsite.com/my_ip pour voir s’afficher son ip. Cependant il faut avoir nginx compilé avec le module Lua ce qui peut être délicat si l’on a jamais compilé d’application.


Conclusion:

Si l’on s’en réfère aux deux exemples ci dessus on serait tenté d’utiliser Nginx car en une seule ligne tout le bazar est réglé.
Le hic c’est qu’il faut que Nginx soit compilé avec le module Lua pour afficher l’ip (sauf si quelqu’un connaît une autre façon d’afficher un message de sortie).
La version Django n’est pas fiable à 100% car suivant comment est configuré votre serveur web il se peut que vous vous retrouviez avec une ip du genre 127.0.0.1.
Il y a aussi la possibilité de parser le résultat des sites web cités en tout début d’article, certains proposent des Api je crois mais vous dépendez d’un autre service (on faisait ça au début) qui peuvent vous lâcher à tout moment (ce qui nous est arrivé).
Personnellement je compile toujours nginx avec Lua et quelques autres modules, ça permet de s’affranchir de plus en plus du backend.

PS: j’ai mis à jour le titre car il laissait sous-entendre autre chose (merci à Sébastien)

flattr this!

]]>
http://sametmax.com/afficher-la-vrai-ip-dun-visiteur-django-vs-nginx/feed/ 11