Raspberry pi
Escrit per Aaloy a 25 de April , 2013 a les 8:20 p.m.
Abans de festes en Xisco (a.k.a Zigazaga) em va dur la Raspberry, la idea era provar-la per mirar de substituir els ordenadors Pentium IV dels nins, però sobretot veure de primera mà aquest petit dispositiu del qual se n'estava parlant tant a la xarxa. Tanmateix el cost d'un dispositiu així si fa no fa és el d'un sopar sense postres, així que l'experiment si sortia malament tampoc no era excessivament complicat.
Xisco va dur la Rasberry i una capseta, tot queda molt compacte, la veritat. Una visita al magatzem xinès del costat de l'oficina va proporcionar el cable per connectar el monitor i vaig reciclar una tarja SD de 32 Gb que tenia sense utilitzar i que ara torna a ser al caixò dels cables.
La instal·lació va ser cosa de baixar-se la Rasbian, crear el disc d'instal·lació i seguir les instruccions, en poc menys d'una hora el sistema complet i funcionant, amb l'entorn gràfic i tot.
Es pot fer feina, però va molt justet de velocitat. Els nins estan acostumats a jugar online, a l'OpenOffice i ja vaig veure que no aniria gairebé la cosa. Tot i això per fer feina ofimàtica i programar va prou bé.
Així que pla B, a veure com es comportava com a servidor. Vaig desinstal·lar la part gràfica i fer un parell d'optimitzacions que vaig trobar per la web. Després a matxacar el sistema:
- Postgresql 9.1
- Django
- Supervidor
- Servidor ssh
- ngnix
- vim configurat
- htop
- tmux amb byobu
- gunicorn
- virtualenv i virtualenvwrapper
Tot això amb el Raspberry ja sense monitor i que està a la part inferior de la taula a l'altra punta del despatx de casa.
Vaig instal·lar la primera aplicació Django completa amb Gunicorn i connexió a Postgresql. Sense cachés ni res i desde l'ordinador principal un apache benchmark va amollar 3 req/s fent 100 peticions i 5 concurrents. La CPU del dispositiu al 100% però allà el veus tirant de base de dades i servint contingut. Potser 3 peticions per segon no pareixen moltes, però comencem a pensar en hores i dies i la cosa ja canvia.
Així doncs ja tinc un servidor prou bo per trastejar, que pot fer distints usos canviant la tarja SD i que torba uns 20-30 segons en posar-se en producció. Prou divertit.
Ara sols em quedava veure quins més usos es podrien donar al un servidor de despatx. A ca'n APSL vam montar un proxy de PyPi per evitar tenir que descarregar-nos els paquets cada vegada i agilitar la instal·lació dels entorns de les aplicacions. Vaig pensar que seria una bona idea, i vaig instal·lar el PyPiCache, ho vaig configurar a 3 workers (per mi amb un bastaria) i el vaig posar dins l'atenta gestió del supervisor i vaig configurar l'ordinador de feina per a que els paquets les demanàs a la Raspberry.
El proxy el que fa es mirar si té el paquet amb la versió que li demanam, si la té la serveix i si no la demana a PyPi. 42M d'arxius ja hi tenc guardats: Django, PIL, Pillow, Sort, reportlab, ...
El sistema encara no ha tirat de swap, els 512 MB de RAM aguanten la poca càrrega que li estic donant però amb prou utilitat per poder dir que en un tres i no res hauré amortitzat la compra (bé quan Xisco passi la factura clar! :) )
Raspberry te torna el gust a trastejar amb màquines, veure que una cosa tant petita i barata pot fer tanta cosa fa encara més ganes de fer servir més la imaginació i veure fins on es pot arribar.
Traducciones/Translations by apertium
1 comentari, 0 trackbacks (URL) , Tags: Python Django Codi lliure Linux
Mails amb Django - IV
Escrit per Aaloy a 29 de March , 2013 a les 11:21 a.m.
Amb tot el que hem vist fins ara podem organitzar els nostres enviaments de correus i mantenir al seu manteniment organitzat encara que el nombre d'opcions sigui gran.
Hi ha encara un grapat d'escenaris que és interessant tractar, ja que ens els trobarem sovint. Pensem en aquests possibles escenaris:
-
Ens hem de connectar a un servidor SMTP extern i el temps de connexió és gran, la qual cosa fa que la nostra aplicació web no respongui.
-
Tenim un entorn de preproducció on els correus no s'enviïn però on volem mantenir registre del que s'hauria enviat.
-
Volem mantenir un registre dels correus que s'envien dins la nostra aplicació.
El primer escenari es pot resoldre amb un servidor de correu local y fent realy cap al servidor final, però això a vegades no és possible ja que no tenim la gestió del servidor de correu.
També ho podem resoldre amb un sistema de coes, és a dir, l'enviament de correu es posa com a tasca i és un altre programa (un worker) el que se n'encarrega de fer l'enviament. La combinació de Celery+Redis ens pot anar bé, però també estam afegint un factor gran de complexitat: hem de tenir Redis instal·lat, configurar adequadament Celery i mantenir els workers actius amb supervisor.
Encara que el sistema de coes possiblement sigui l'opció millor per sistemes amb molta càrrega, s'ha d'avaluar bé si per la nostra aplicació convé complicar-nos tant la vida. Quan més peces posem més complexitat, més punts a gestionar, i la simplicitat importa. Si la nostra aplicació creix Django té prou flexibilitat per permetre'ns anar separant capes, afegir les coes, ...
Una solució a tots aquests problemes i escenaris, que a més és prou simple és la de guardar els correus que s'han d'enviar dins una taula de la base de dades i executar un procés cron per fer els enviaments. Això allibera ràpidament a la nostra aplicació web i no té la complexitat de manteniment d'un sistema de coes dedicat.
Com que el cron ho gestionam nosaltres, en preproducció podem dir que no s'executi, i els correus queden dins la base de dades i els podem veure sense cap problema sense fer un enviament real. Si hem de mantenir registre del que s'han enviat doncs també ho tenim.
Això no vol dir que aquest sistema no tengui mancances: si generam molts correus per segon el nostre coll d'ampolla serà la base de dades, però també és veritat que si enviam tal quantitat de correus, llavors sí que potser convé complicar-nos un poc més amb l'arquitectura de l'aplicació.
Dit això uns present django-mailer2. Al link he posat el fork que fem servir a APSL ja que ens hem trobat amb alguns problemes relacionats amb l'unicode que hem resolt i també amb la necessitat de poder visualitzar els e-mails amb adjunts. Així que es va fer un fork del fork i el fem servir a l'espera que l'autor del fork original ens admeti el codi nou, i ja posats vam generar la documentació per posar-la online a readthedocs.
django-mailer2 actua com a backend de Django, per la qualcosa una vegada instal·lada l'aplicació i posada als nostres settings.py hem de configurar el backend per a que quedi com
EMAIL_BACKEND = ‘django_mailer.smtp_queue.EmailBackend’
i farem un syncdb per crear les taules que necessitam a la base de dades.
A partir d'aquest moment els correus que enviem quedaran a la base de dades de django-mailer i no sortiran fins que executem la comanda
python manage.py send_mail
que és el que normalment posarem al cron.
Podem interactuar amb la prioritat d'enviament amb la capçalera
{‘X-Mail-Queue-Priority’: ‘<value>’}
on <value> pot prender els valor:
- now per no posar-ho a la coa i enviar-lo immediatament.
- normal prioritat per defecte
- low prioritat baixa. Sortiran els darrers.
Si fem servir django-mailviews fixau-vos que seria prou senzill fer una classe base que abans d'enviar el correu li posàs la prioritat desitjada, afagint aquest paràmetre als headers.
Per mi aquesta aplicació ha estat un dels factors de productivitat més grans dels darrers mesos. Potser perquè les nostres aplicacions han d'enviar avisos amb adjunts i correus formatejats en HTML, és veritat, però poder mantenir el registre del que s'ha enviat i a l'entorn de preproducció despreoucupar-nos de maldecaps de si s'enviarà el correu sense voler o no és una meravella.
Epíleg
Amb això acab aquesta sèrie d'apunts sobre l'enviament de correus electrònics i Django. Esper que us hagi agradat llegir-los tant com a mi m'ha agradat escriure'ls.
La idea, com sempre, és compartir un poc les troballes que un va fent al llarg del temps. Pensau però que no hi ha veritats absolutes, potser per la vostra aplicació el que he contat per aquí no s'aplicarà, o demà sortirà una manera millor de fer les coses. Si això passa no deixeu d'avisar!
Traducciones/Translations by apertium
0 comentaris, 0 trackbacks (URL) , Tags: Django
Mails amb Django - III
Escrit per Aaloy a 28 de March , 2013 a les 1:18 p.m.
Fins ara hem vist com podem enviar correus electrònics amb format text, amb format HTML en el dos formats. També hem vist que podem generar el nostres correus a partir de plantilles Django.
A poc que l'aplicació es vaig fent més gran veurem que és molt interessant poder tenir tots els correus que enviam centralitzats, de manera que no hagem d'anar a cercar per codi i per les diferents aplicacions els correus que enviam.
Sovint a més els correus que enviam són semblants: van dirigits a la mateixa gent, o tenen informació que canvia molt poc d'uns als altres, com el peu del missatges o la salutació.
Una situació ideal per utilitzar dues coses: les plantilles de Django per a la generació de correus i aprofitar com Django va a cercar les plantilles per tenir tots els correus centralitzats a un mateix lloc, i per una altra banda l'herència pròpia de Python que ens permetrà guanyar temps a l'hora de generar els correus i farà que siguem menys propensos a error a l'hora de generar-los.
Ara és quan a un se li encén la bombeta i es posa com a un boig a generar classes de Python per a la generació de correus amb Django. No faceu tanta via! La primera regla del bricolatge: "si està fet compra-ho". Així que el primer que hem de fer és cercar si ja hi ha alguna cosa feta. I com no, hi és. De fet hi ha vàries llibreries per a la generació de correus fent servir plantilles Django. Una de les més interessants és la que ha fet Ted Kaemming anomenada django-mailviews que a més d'encapsular tot això en classes Python, ho ha posat dins un paquet Django que ens facilita la vida de visualitzar els correus.
La documentació no és tot el completa/acurada que hauria de ser i és focalitza més en la visualització que en el que jo trob més interessant que és aquesta possibilitat d'encapsulació. Així que fork al canto per a millorar la documentació!
En aquest article faré servir aquesta llibreria per mostrar com fer les tasques més habituals i que ja hem vist abans. Ja que hi som aprofitaré també per mostrar com podem enviar un fitxer adjunt.
Enviar un correu sols de text
Volem enviar un correu a un usuari.
Definirem les dues plantilles que utilitzarem, la primera per l'assumpte (subject) y l'altra per al contingut.
Cream un directori anomenat mail (sóc així d'original) dins templates de la nostra aplicació principal. A partir d'aquí podríem anar creant diferents subdirectoris segons els correus per a enviar, però supòs que ja es veu la idea...
La plantilla per l'assumpte queda com:
{# plantilla 'mail/subject.html'
Greetings {{user.get_full_name}}
i seguidament definim el cos del missatge
{# plantilla 'mail/body_text.html' #}
Dear {{user.get_full_name}},
You are the best.
Please give us your money!
El codi per enviar aquest missatge podria ser alguna cosa semblant a:
from mailviews.messages import TemplatedEmailMessageView
from django.contrib.auth.models import User
class SimpleSpamView(TemplatedEmailMessageView):
"""
Classe per spamejar l'usuari demo i dir-li que ens doni els doblers
"""
subject_template_name = 'mail/subject.html'
body_template_name = 'mail/body_text.html'
def get_context_data(self, **kwargs):
context = super(SimpleSpamView, self).get_context_data(**kwargs)
context['user'] = self.user
return context
def render_to_message(self, *args, **kwargs):
self.user = User.objects.get(username='demo')
kwargs['to'] = (self.user.email, )
return super(SimpleSpamView, self).render_to_message(*args, **kwargs)
i per enviar-lo bastaria cridar:
SimpleSpamView().send()
Pareix molta feina comparat amb el que teníem, veritat? Doncs sí, però com passa amb els class based views de Django aquesta feina extra inicial es veu compensada de sobres per la claretat del codi, que d'aquesta manera queda molt documentat, separant el text de generació, i sobretot possibilitant la reutilització.
Per cert la sortida d'aquest correu és:
Content-Type: text/plain; charset="utf-8"
MIME-Version: 1.0
Content-Transfer-Encoding: 7bit
Subject: Greetings Demy Ostracion
From: webmaster@localhost
To: demo@example.com
Date: Thu, 28 Mar 2013 11:40:53 -0000
Message-ID: <20130328114053.6625.94194@T1600>
Dear Demy Ostracion,
You are the best.
Please give us your money!
Com podem veure es tracta d'un missatge codificat com a text, que és precisament el que cercàvem.
Correu amb text i HTML
Sabem que Demy, el nostre usuari de proves, fa servir un lector de correu que permet el format HTML, així que per reforçar més el missatge el que farem serà enfatitzar el missatge generant una plantilla HTML
{# mail/body_html.html #}
Dear {{user.get_fullname}},
<h1>You are the best!</h1>
<strong>Please give us your money!</strong>
Ara el que farem és que enlloc de fer herència de TemplatedEmailMessageView la farem de la classe TemplatedHTMLEmailMessageView que té un atribut nou html_body_template_name
from mailviews.messages import TemplatedHTMLEmailMessageView
from django.contrib.auth.models import User
class SimpleSpamView(TemplatedHTMLEmailMessageView):
"""
Classe per spamejar l'usuari demo i dir-li que ens doni els doblers
"""
subject_template_name = 'mail/subject.html'
body_template_name = 'mail/body_text.html'
html_body_template_name = 'mail/body_html.html'
def get_context_data(self, **kwargs):
context = super(SimpleSpamView, self).get_context_data(**kwargs)
context['user'] = self.user
return context
def render_to_message(self, *args, **kwargs):
self.user = User.objects.get(username='demo')
kwargs['to'] = (self.user.email, )
return super(SimpleSpamView, self).render_to_message(*args, **kwargs)
i l'enviam com abans
SimpleSpamView().send()
que té per sortida:
Content-Type: multipart/alternative;
boundary="===============6944209950729072063=="
MIME-Version: 1.0
Subject: Greetings Demy Ostracion
From: webmaster@localhost
To: demo@example.com
Date: Thu, 28 Mar 2013 11:56:34 -0000
Message-ID: <20130328115634.6625.91235@T1600>
--===============6944209950729072063==
Content-Type: text/plain; charset="utf-8"
MIME-Version: 1.0
Content-Transfer-Encoding: 7bit
Dear Demy Ostracion,
You are the best.
Please give us your money!
--===============6944209950729072063==
Content-Type: text/html; charset="utf-8"
MIME-Version: 1.0
Content-Transfer-Encoding: 7bit
Dear Demy Ostracion,
<h1>You are the best!</h1>
<strong>Please give us your money!</strong>
Adjuntant arxius a un correu
Des de marketing ens diuen que ara el que convé és adjuntar fitxers en pdf per reforçar encara més el missatge i tenir el nostre usuari entretingut, així que ens passen un pdf amb informació corporativa que hem d'adjuntar a cada e-mail que li enviam a Demy.
Nosaltres el desarem al nostre directori pdf, però tenim un problema, la classe que estam fent servir no diu res d'adjuntar pdf. Però nins, això és codi obert i li podem fer una ullada al codi. Com podem veure el que fa render_to_message és tornar-nos una instància de la classe EmailMessage de Django i aqueta té dos mètodes per adjuntar imatges attach i attach_file. La primera la farem servir principalment quan tenim un fluxe de dades (per exemple hem generat nosaltres el pdf i està en memòria) i la segona quan tenim el fitxer dins el sistema d'arxius.
from mailviews.messages import TemplatedHTMLEmailMessageView
from django.contrib.auth.models import User
class SimpleSpamView(TemplatedHTMLEmailMessageView):
"""
Classe per spamejar l'usuari demo i dir-li que ens doni els doblers
"""
subject_template_name = 'mail/subject.html'
body_template_name = 'mail/body_text.html'
html_body_template_name = 'mail/body_html.html'
def get_context_data(self, **kwargs):
context = super(SimpleSpamView, self).get_context_data(**kwargs)
context['user'] = self.user
return context
def render_to_message(self, *args, **kwargs):
self.user = User.objects.get(username='demo')
kwargs['to'] = (self.user.email, )
msg = super(SimpleSpamView, self).render_to_message(*args, **kwargs)
msg.attach_file('./pdfs/test.pdf')
return msg
SimpleSpamView().send()
Que torna:
Content-Type: multipart/mixed; boundary="===============5276732835281812163=="
MIME-Version: 1.0
Subject: Greetings Demy Ostracion
From: webmaster@localhost
To: demo@example.com
Date: Thu, 28 Mar 2013 12:11:51 -0000
Message-ID: <20130328121151.6625.85753@T1600>
--===============5276732835281812163==
Content-Type: multipart/alternative;
boundary="===============7874338344503940985=="
MIME-Version: 1.0
--===============7874338344503940985==
Content-Type: text/plain; charset="utf-8"
MIME-Version: 1.0
Content-Transfer-Encoding: 7bit
Dear Demy Ostracion,
You are the best.
Please give us your money!
--===============7874338344503940985==
Content-Type: text/html; charset="utf-8"
MIME-Version: 1.0
Content-Transfer-Encoding: 7bit
Dear Demy Ostracion,
<h1>You are the best!</h1>
<strong>Please give us your money!</strong>
--===============7874338344503940985==--
--===============5276732835281812163==
Content-Type: application/pdf
MIME-Version: 1.0
Content-Transfer-Encoding: base64
Content-Disposition: attachment; filename="test.pdf"
JVBERi0xLjQKJcOkw7zDtsOfCjIgMCBvYmoKPDwvTGVuZ3RoIDMgMCBSL0ZpbHRlci9GbGF0ZURl
Y29kZT4+CnN0cmVhbQp4nDPQM1Qo5ypUMFAwALJMLU31jBQsTAz1LBSKUrnCtRTyIHJAWJTO5RTC
ZWoGlDI3NwEqDklR0HczVDA0UghJi7YxMLQzs7AxMLIztDEwttM1sjEwsYsN8eJyDeEK5ApUAAB7
....
....
--===============5276732835281812163==--
Com podeu veure s'ha enviat tant el text pla, l'html i el nostre adjunt.
En una aplicació real el path cap a l'arxiu seria una variable i/o estaria codificat de manera absoluta, però per l'exemple ja em perdonareu.
A partir d'aquí ja podeu veure que les possibilitats de personalització són infinites. Pensau que en una aplicació real amb molts correus a enviar, poder organitzar el codi en classes ens permetrà reaprofitar molta feina, i sobretot ens llevarà feina de depuració a l'hora d'anar canviant el texte del correu o les condicions en que s'ha d'enviar un correu.
I al proper article parlarem d'enviaments, coes, registre i depuració. Tot en un mateix paquet django-mailer2.
Traducciones/Translations by apertium
1 comentari, 0 trackbacks (URL) , Tags: Django
Mails amb Django - II
Escrit per Aaloy a 25 de March , 2013 a les 11:56 p.m.
Enviar un e-mail fent servir plantilles Django
Com ja tots sabeu Django té un sistema de plantilles molt eficaç. Normalment les fem servir per mostrar l'html de les nostres aplicacions web, però no estan limitades a això, i com veurem les podem fer servir per generar els nostre correu.
La manera de fer-ho és fent servir render_to_string que podem trobar a django.template.loader. Aquesta funció agafa com a paràmetres el nom de la plantilla que volem fer servir i un diccionari amb els paràmetres que farem servir a la plantilla. Agafa un tercer paràmetre, que ha de ser de tipus Context, però que per als nostres objectius no la farem servir gaire.
Quan volem enviar un correu ho podem fer dins la vista, però sovint ho farem des de classes d'utilitat, el model o un formulari, on el request no està disponible. Per això hem de tenir em compte que la majoria de vegades no tindrem accés a les variables que normalment estan accessibles a les plantilles mitjançant el context processors. Així doncs que si el correu te links pensau que tot el que siguin referències a arxius i links forçosament necessiten de Sites, MEDIA_URL i STATIC_URL (o fer servir el tag {% static %} introduït a les darreres versions de Django.
Suposem doncs que tenim el seguent, dins una plantilla que hem anomenta newsletter.html i que tenim accessible dins els TEMPLATE_DIRS
Benvolgut {{user.get_fullname}},
No deixis de visitar les nostres ofertes a http://{{current_site}}/ofertes/
Salutacions,
--
info@{{current_site}}
http://{{current_site}}
I a la funció que genera el correu tendríem per exemple:
from django.contrib.sites.models import Site
from django.conf import settings
from django.template.loader import render_to_string
from django.core.mail import EmailMessage
def send_wellcome():
"""Envia un missatge als nostres usuaris"""
current_site = Site.objects.get_current().domain
context = {'current_site': current_site}
for u in User.objects.all():
context['user'] = u
body = render_to_string('dos/newsletter.html', context)
msg = EmailMessage(subject="hello",
body = body,
from_email="jo@apsl.net",
to=['someuser@example.com'])
msg.content_subtype = "html"
msg.send()
Fixem-nos com context és un diccionari on li hem de passar totes les variables que utilitza la nostra plantilla. En aquests tipus de corrreus el més habitual és anar en pilot automàtica i fer servir les variables {{STATIC_URL}} o {{MEDIA_URL}} habituats a tenir-les accessibles gràcies als context_processors i trobar-nos que als nostres e-mails no es veuen les imatges. Així que a més de passar-les al context ens hem d'assegurar que es passen aquestes variables i a més que les urls es generen com a url absolutes.
Per això a la plantilla de Django podem fer servir la llibreria static i utilitzar els tags get_static_prefix i get_media_prefixper a generar les urls. Seria una cosa semblant a
{% load static %}
{% get_static_prefix as STATIC_PREFIX %}
{% get_media_prefix as MEDI_PREFIX %}
<img src="http://{{current_site}}/{{STATIC_PREFIX}}/img/log.jpg" alt="logo" />
<img scr="http://{{current_site}}/{{MEDIA_PREFIX}}/profile/{{foto}}" alt="foot pujada" />
No fa falta recordar que a static hi posarem totes aquelles images, css, js, etc que formen part de l'estructura de l'aplicació i a media hi haurà totes aquelles imatges i continguts que puja l'usuari de la nostra aplicació.
Donat que hem utilitzat en render_to_string per a generar el cos del missatge, segur que a més d'un se li haurà acudit que podem fer el mateix amb el subject, doncs sí, si és necessari es pot fer i de fet es fa. Però això ja serà al proper article...
Traducciones/Translations by apertium
2 comentaris, 0 trackbacks (URL) , Tags: Django
Mails amb Django - I
Escrit per Aaloy a 24 de March , 2013 a les 8:35 p.m.
En aquest article, o millor dit, sèrie d'articles, intentaré explicar el millor que pugui la problemàtica que ens trobam en el dia a dia la gent que hem d'enviar correus electrònics des de les nostres aplicacions Django, no tant des del punt de vista de com es fa, sinó del programador que té que desenvolupar d'aplicació.
Veurem distints escenaris i com podem tenir una solució que s'adapta a cada un d'ells, de manera que acabem amb un petit receptari, que esper anar ampliant amb l'adjuda de tots. La referència fonamental per Django és la documentació "sending email", miraré de no repetir-la molt, però per completitud convé fer una petita introducció.
Configurar el servidor
Volem enviar un e-mail ràpid en resposta a un esdeveniment.
Per a poder enviar un e-mail des de la nostra aplicació Django prèviament haurem d'haver configurat en nostre servidor SMTP. Això es fa amb les variables del settings EMAIL_HOST, EMAIL_PORT, EMAIL_HOST_USER i EMAIL_HOST_PASSWORD. Ja que hi som convé definir el mail que farem servir en cas que no el posem de manera explícita al DEFAULT_FROM_EMAIL
Normalment no hem de definir res si el nostre servidor d'e-mail és el mateix servidor on està l'aplicació i tenim un SMPT local, però pot ser necessari si volem fer servir un altre servidor amb un compte específic, com per exemple el gmail de Google podríem fer:
DEFAULT_FROM_EMAIL='jo@exemple.com'
EMAIL_USE_TLS = True
EMAIL_HOST = 'smtp.gmail.com'
EMAIL_HOST_USER = 'usuari-gmail@gmail.com'
EMAIL_HOST_PASSWORD = 'clau-super-secreta'
EMAIL_PORT = 587
Enviar un e-mail
Volem enviar un mail a un conjunt de destinataris, per això podem fer servir la funció send_mail que ens proporciona Django
from django.core.mail import send_mail
send_mail(subject='test email 2', message='hello world 2',
from_email='antoni.aloy@example.com',
recipient_list=['un@apsl.net', 'dos@trespams.com'], fail_silently=False)
Aquí convé notar que hem de posar tant qui envia el mail from_email com la llista de destinataris. És un error molt comú posar al recipient_list el mail de la persona que ha de rebre l'e-mail sense aturar-se a pensar que la funció necesita una llista com a paràmetre. Si sols teniu una adreça que ha de rebre l'e-mail pensau que l'hem de convertir primer en una llista
from django.core.mail import send_mail
send_mail(subject='test email 2', message='hello world 2',
from_email='antoni.aloy@example.com',
recipient_list=['un@apsl.net',], fail_silently=False)
Si volem fer enviaments massius Django a les darreres versions incorpora la funció send_mass_mail que permet enviar correus sense tenir que obrir cada vegada la connexió amb el servidor.
Django a més ens proporciona dues funcions adicionals per simplificar el codi necessari per enviar els correus quan els destinataris són la llista d'administradors del lloc i el managers dels mateix, definits respectivament a les configuracions ADMINS i MANAGERS, que són mail_admins i mail_managers.
D'aquesta manera, com ja sabem quins són els destinataris sols hem d'omplir l'assumbte (subject) i el missatge (message) i ja ho tenim. Aquestes funcions agafen un paràmetre adicional html_message que ens permet enviar el missatge com a text/html enlloc del text/plain definit per defecte.
Així per exemple:
from django.core.mail import mail_managers
mail_managers(u'Avís nou usuari', u"Tens un nou usuari al sistema")
Ens serviria per enviar un correu quan es crea un nou compte d'usuari amb ben poca feina.
Enviar un correu en format HTML
Encara que la manera més ràpida i simple d'enviar un correu sia el text pla, sovint ens trobam que els nostres clients volen que el coreu s'envïi en format html, amb tota mena de decoració: negreta, cursives, links, imatges, arxius adjunts, ...
Això representa una nova complexitat, no per la dificultat d'enviar-ho, que com veurem és prou senzilla, sinó perquè no hi ha cap garantia que es vegi com un vol a la majoria de clients de correu. Per això el que més convé és fer els HTML dels correus el més senzill possibles, sense floritures i tornant a la programació a l'antiga. La gent de mailchimp té un bon grapat de consells damunt el tema i fins i tot va alliberar en el seu dia un bon conjunt de plantilles que ens poden servir de guia i que trobareu a github baix el nom de email-blueprints.
Començarem pel més senzill: suposarem que sols volem enviar un correu en format HTML sense part en text pla.
from django.core.mail import EmailMessage
msg = EmailMessage(subject="hello",
body="""<strong>Hello</strong> World""",
from_email="jo@exemple.com",
to=["someotheruser@example.com", ])
msg.content_subtype = "html"
msg.send()
La classe EmailMessage ens permet a més definir una llista de destinataris ocults (el típic bbc), destinataris amb còpia, afegir arxius, modificar les capçaleres o definir com establirem la connexió amb el sistema d'enviament de missatges. Hi tornarem un poc més tard amb això.
En missatges realment importants, on volem que es faci la lectura independentment del client de correu, convé enviar tant la part HTML com la part tn text plà. Django també ens permet fer-ho amb facilitat fent servir la classe EmailMultiAlternatives. Com a sublcasse d'EmailMessage ens permet fer el mateix que aquesta, però a més ens permet definir tant la part de text plà com l'html. D'aquesta manera el nostre *hello world" quedaria
from django.core.mail.import EmailMultiAlternatives
text_content = "hello wolrd!"
html_content = "hello <strong>world!</strong>"
msg = EmailMultiAlternatives(subject,
text_content=text_content,
from_email="jo@example.com",
to=["someone@example.com"])
msg.attach_alternative(html_content, "text/html")
msg.send()
Testejar els mails
Anam avançant en el tema, ara ja sabem com enviar mails en text pla i en format HTML, enviar-los als administradors, a una persona, a vàries, ... Tot això està molt bé, però com que fem les proves amb la nostra pròpia compta de correu aquesta comença a estar plena de mails de proves i hem d'anar amunt i avall amb el client de correu.
Com bé podeu suposar hi ha d'haver una manera de poder fer feina amb els correus sense aquests inconvenients. La resposta està en els backends que Django ens permet definir a la configuració.
Per defecte Django configura la variable EMAIL_BACKEND com
EMAIL_BACKEND = 'django.core.mail.backends.smtp.EmailBackend'
això vol dir que per defecte Django intentarà enviar l'e-mail mitjançant el servidor smpt que hem definit dins EMAIL_HOST (que és localhost per defecte), si no tenim el servidor smtp configurat ens donarà error a l'hora d'enviar el correu.
Podem substituir aquest comportament per defecte per un backend alternatiu, a l'hora de testejar i veure que està passant, sobretot si estam en el servidor de desenvolupament de Django, podem fer que el missatge s'enviï per la consola canviant el backend a
EMAIL_BACKEND = 'django.core.mail.backends.console.EmailBackend'
així, encara que la configuració que tinguem sigui bonan, podem fer que el missatge no s'enviï i que sols surti per la consola.
Content-Type: text/html; charset="utf-8"
MIME-Version: 1.0
Content-Transfer-Encoding: 7bit
Subject: hello
From: jo@example.com
To: unaltre@exemple.net
Date: Sun, 24 Mar 2013 19:23:41 -0000
Message-ID: <20130324192341.9841.81239@T1600>
<strong>Hello</strong> World
-------------------------------------------------------------------------------
pràctic, veritat?
També hi ha un altre backend que pot ser útil, el File backend que enlloc de mostrar el missatge a la consola ens permet definir un directori on deixar-hi els correus mitjançant la variable EMAIL_FILE_PATH, però la veritat és que no l'he fet servir gens, com veurem hi ha altres alternatives més potents i que també ens faciliten la vida a l'hora de desenvolupar.
En els propers apunts ...
Fins ara no hem vist res que no sigui a la documentació de Django, però esper que us hagi servit d'introducció ràpida per al que vindrà després:
- Generar correus amb les plantilles Django
- Alternatives per generar els correus
- Backends alternatius: django-mailer2
Traducciones/Translations by apertium
3 comentaris, 0 trackbacks (URL) , Tags: Django
Testejar app Django
Escrit per Aaloy a 03 de February , 2013 a les 1:31 p.m.
Una de les coses que me fan més peresa quan he de crear una nova aplicació Django és tenir que configurar una aplicació per poder-ne fer els tests quan sols estàs fent un mòdul que serà reutilitzable per a altres aplicacions.
Una vegada s'ha fet l'aplicació el que voldria és poder-ho testejar sense tenir que configurar tot un projecte i no acabava de trobar-ho del tot fins que vaig veure la manera en que ho feia Brutasse a django-password-reset.
L'aproximació de Brutasse és tenir una projecte Django dins la mateixa applicació que s'està creant, de manera que aquesta es pot testejar sense tenir que crear el projecte sencer i controlant a la mateixa vegada els settings que estam fent servir. És a dir, just el que estava cercant.
Aquests darrers dies he estat treballant un poc en una branca de django-mailer2 i l'he refactoritzat per seguir la seva idea a l'hora de fer els unit tests.
Una vegada creat el [paquet python] (http://guide.python-distribute.org/index.html) el que es fa es crear un arxiu que serà l'executable que iniciarà els tests. runtests.py per no ser massa originals
Creant l'executable
Aquí el que fem és configurar l'aplicació com se fos una execució desatesa de Django, és a dir, configuram els settings, el path i després ja sols ens queda fer l'execució dels unittests
#!/usr/bin/env python
# encoding: utf-8
# ----------------------------------------------------------------------------
import os
import sys
parent = os.path.dirname(os.path.abspath(__file__))
sys.path.insert(0, parent)
os.environ['DJANGO_SETTINGS_MODULE'] = 'django_mailer.testapp.settings'
from django.test.simple import DjangoTestSuiteRunner
def runtests(*test_args):
test_args = test_args or ['testapp']
parent = os.path.dirname(os.path.abspath(__file__))
sys.path.insert(0, parent)
runner = DjangoTestSuiteRunner(verbosity=1, interactive=True,
failfast=False)
failures = runner.run_tests(test_args)
sys.exit(failures)
if __name__ == '__main__':
runtests()
Fixem-nos ja de pas que faríem el mateix si hem creat una aplicació Django que no volem que s'executi com a servei web. Per exemple una importació de dades, o una exportació. Hi ha diverses maneres d'aconseguir-ho i aquesta és una de les més simples.
Una vegada hem establit la variable d'entorn de la nostra aplicació (la que contindrà els tests) ja tenim tota la potència del framework Django al nostre abast.
L'apliació testejadora
Per no fer-nos massa enfora de la convenció de Django, posam l'aplicació que realment executarà els tests dins el directori del mòdul que hem desenvolupat, en el nostre exemple django_mailer.
Podríem posar-li test, però la veritat és que no m'agrada això de tenir un projecte test i dintre un mòdul test. Sovint fa difícil saber on tens els errors, així que un nou explícit ja va bé.
Dins testapp trobam el que seria un projecte Django bàsic, sols que a més hi hem postat un settins que són molt reduïts,
EMAIL_PORT = 1025
ROOT_URLCONF = 'django_mailer.apptest.urls'
SECRET_KEY = 'yo secret yo'
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.sqlite3',
'NAME': 'django_mailer.sqlite',
},
}
INSTALLED_APPS = (
'django.contrib.auth',
'django.contrib.contenttypes',
'django_mailer',
'django_mailer.testapp'
)
Bàsicament hi ha els paràmetres de configuració que necesistarem per a executar l'aplicació que estam creant, configuració de base de dades i la configuració dels INSTALLED_APPS on hi ha la pròpia aplicació de test i l'aplicació que estam testejant, de manera que ho tinguem tot dins el Python path.
A partir d'aquí tot va com a la documentació de Django, és a dir, cream un mòdul anomenat test i utilitzam el mòdul TestCase de Django i tota la resta d'artilleria.
El que hem aconseguit, però, és fer més senzill el manteniment de l'aplicació ja que queda tot autocontingut. Al tenir uns settings mínims també podem estar raonablement segurs que l'aplicació que cream funcioni bé per ella mateixa sense altres interferències, i com no, ens queda documentat el que necessitam per a fer-la anar.
No estic convençut de poder-ho considerar encara una "millor pràctica", però ho trob útil i potser a algú dels qui em llegiu també us pot solucionar un problema, així que ja em contareu.
Traducciones/Translations by apertium
0 comentaris, 0 trackbacks (URL) , Tags: Python Django
Two Scoops of Django
Escrit per Aaloy a 20 de January , 2013 a les 11:52 a.m.
Ahir vai acabar de llegir "Two Scoops of Django", el nou llibre, encara en versió Alfa que han tret DAniel Greenfeld i Audrey Roy.
A diferència d'altres llibres que t'ensenyen a programar o et mostren el funcionanment d'un framework de programació, aquest llibre no és un tutorial per als no iniciats, sinó més bé una mena de consultoria de millors pràctiques en forma de llibre.
Els autors "es banyen", recomanant utilitats i maneres de fer les coses per tal que els nostres projectes siguin més bons de desplegar i mantenir. Es recomanen llibreries externes per a facilitar-nos la vida: South, Virtualenv, Virtualenvwrapper (vells coneguts també per aquest blog) i fins i tot entra en la petital polèmica que hi ha damunt si és millor fer-ho tot amb CBV, fer una mescla o no fer-les servir.
Puc dir que estic gairebé al 100% d'acord amb totes les recomanacions. Val a a dir que hem arribat a les mateixes conclusions que els autors en garebé tots els aspectes. Fins ara no feiem servir les variables d'entorn i és una cosa que de ben segur ens mirarem amb cura.
Punts que potser estaria bé tractar o de discrepància:
-
Nom del projecte. Personalment no m'agrada gens tenir el nom del projecte igual al nom de l'aplicació django igual a l'aplicació principal. A poc que et descuidis pot donar lloc a errors d'importació que et fan perdre una bona estona. Per això m'estim més anomenar al projecte prj_[nom_projecte], i en Django 1.5 me pareix que optaré per anomenar a la part principal del projecte com a main i buidar-la de contingut llevat de ser el nexe d'unió de les urls i potser de la vista que ens mostra l'index.
-
Mail. Per mi és una de les millores més interessants que hem incorporat darrerament en el desenvolupament d'aplicacions Django. Django et permet enviar els mails a la consola per depurar, o bé, junt amb Python podem habilitar un servidor de correu local. Però en la majoria d'ocasions ens interessa poder veure el correu que s'havia enviat, o seguir el link, i no volem que realment s'estiguin enviant els correus. Per això la troballa de django-mailer-2 va ser força interessant, ja que ens permet guardar els mails que s'envien dins la base de dades i poder consultar-los amb l'administrador de Django. Els correus no s'envien fins que habilitam una tasca al cron. Trob que és ideal en els entorns de preproducció (amb la tasca del cron desactivada) i en les primeres etapes de la posada en producció, on vols tenir un control molt fi del que està passant a l'aplicació.
Tornant al llibre. Trob que és una inversió molt bona, però heu de saber què us trobareu: si no heu fet cap projete amb Django no és un bon llibre per aprendre. Si ja n'heu fet un grapat, segur que el llibre us servirà per millorar la vostra feina diària.
Traducciones/Translations by apertium
3 comentaris, 0 trackbacks (URL) , Tags: Python Django
Pensar en el manteniment compensa
Escrit per Aaloy a 30 de August , 2012 a les 11:35 p.m.
Avui, fa poques hores hem posat en producció una nova web per Fiesta Hotel Group anomenada Palladium Weddings és una web pensada per pantalles grans, on s'intenta mostrar el que és el producte de noces que té el client. Si us pensau casar aviat o renovar vots, no deixeu de fer-li una ullada, els escenaris i ambients són una autèntica meravella.
Com és habitual la web està desenvolupada amb Django i pensada per a suportar una càrrega important de visites, i a la vegada fer que el manteniment dels continguts sia el més ràpid possible. Pensau que muntar un paquet de noces, amb tot els extres, fotografies, escenaris, ... no és una tasca trivial. Ens hem de posar amb la pell no tan sols del client potencial que visita la web, sinó de la gent que l'ha de mantenir, i per tant l'aplicació s'ha de cuidar tant en l'aspecte visible com en el que es veu menys, però que també té una part fonamental en l'èxit de la web. Si aconseguim fer una web fàcil de mantenir és més senzill que els continguts es mantenguin actualitzats i que la web estigui més viva.
Però a part d'aquestes consideracions, per mi aquesta web ha resultat ser un exemple perfecte de perquè s'han de desenvolupar les aplicacions d'una determinada manera, en el nostre cas seguint el model MVT de Django, molt semblant al model MVC.
Aquesta web va començar a gestar-se al final de l'any passat. Partíem d'una web en PHP que s'havia de migrar i millorar. El projecte estava ja pràcticament acabat quan Fiesta va decidir fer-hi una repensada i mentre anar prioritzant altres projectes.
La repensada finalment va donar com a fruit un redisseny complet de la web, amb més continguts, més fotografies i detalls, però amb una funcionalitat que era al 80 o 90% semblant a l'anterior.
Les aplicacions que desenvolupam tenen un component de backoffice, un CMS si voleu, on es posen les dades i continguts i es manté el model de dades a partir del models d'aplicacions que hem definit a Django.
Les urls es mapegen amb vistes, que no són més que codi Python que obté la informació dels models, la manipula i la retorna per a que es pugui muntar la plana web mitjançant una plantilla, que per construcció no té regles de negoci incorporades.
És a dir, tenim una clara separació entre com es gestionen els continguts al CMS, i com es presenten, entre la URL i l'HTML final que veurà l'usuari al seu navegador.
Per a aquest projecte això ha significat un estalvi directe de temps i diners. Hem pogut reaprofitar-ho gairebé tot el que no era la capa de presentació, i no tan sols això, sinó que s'ha pogut avançar en paral·lel entre la programació i la gestió dels continguts.
El model MVC ho implementen molts bastiments Java, PHP, Ruby. Però el que per mi és important és que Django ho fa realment fàcil. La manera natural de fer aplicacions amb Django és la de fer aplicacions fàcils de mantenir, on el nivell de cerimònia que s'ha de fer per reutilitzar components o crear-ne de nous es mínim. He tingut l'ocasió de treballar amb Struts, Spring per Java o Symfony amb PHP i són un malson comparats amb la facilitat de Django.
Per mi aquest projecte demostra que la inversió en fer les coses com toca compensa. Segur que un projecte típic de PHP on hi ha codi, accés a la base de dades i codi mesclat pot aconseguir el mateix que el que hem fet. El que no hauria pogut aconseguir mai és aquest nivell de reutilització. Separar en capes compensa, utilitzar Django i Python en les aplicacions web també.
Pels qui us atracau a aquest blog des d'altres tecnologies i estau pensant amb Django per al proper projecte, esper que aquesta història us pugui servir d'inspiració. Pensau que els començaments són sempre durs, però que el camí es prou suau. Pensau que allà on inicialment us pensau que el desenvolupament és més lent, adonau-vos de com n'està de protegida la inversió que fa el vostre client en tecnologia i en com es poden anar canviant parts de la web i evolucionant-la sense morir a l'intent.
Posar PHP dins el codi HTML sols duu al costat fosc, perquè fosques seran les nits que us passareu depurant l'aplicació.
Traducciones/Translations by apertium
0 comentaris, 0 trackbacks (URL) , Tags: Python Django
Filtres a l'admin de Django 1.4
Escrit per Aaloy a 31 de July , 2012 a les 9:42 p.m.
En una de les darreres aplicacions que hem fet, la d'apibaleares hem tirat força de l'administrador de Django. Segurament i donades les característiques que volem que tengui l'apliació al final, anirem cap a un administrador ad-hoc, però ara per començar va força bé.
Un dels primers problemes va sorgir amb els filtres. Amb l'admin de Django és molt senzill fer un filtre per qualsevol camp, però té l'emperò que si el camp és una clau forana cap a una altra taula amb molts elements Django et pinta tots els elements, amb la qual cosa la plana creix molt i el llista es fa mal de manejar.
Amb Django 1.4 tenim l'opció de crear-nos els nostres propis filtres i dir-li a l'admin que els faci servir. Per posar-vos en situació el que voldríem és que enlloc del filtre com a llista per la clau forana, poguéssim seleccionar l'element a filtrar des d'un desplegable. És veritat que perdem la selecció múltiple, però a l'usuari no li fa res, el que no vol és tota la llista al costat dret del llistat.
Així doncs, el primer que farem serà veure què hi ha respecte als filtres. La documentació de Django es minsa, potser perquè ja avisen que això pot canviar en un futur proper. Ens arriscarem per ara!
El filtre que ens interessa el derivarem de RelatedFieldListFilter
que és qui se n'encarrega de pintar les claus foranes. Val a dir que no
necessitam massa cosa, volem pintar d'una manera diferent els elements,
així que únicament hem de sobreescriure la part que se n'encarrega de
la visualització:
Dins l'admin.py del nostre projecte, per exemple, fem
from django.contrib.admin import RelatedFieldListFilter
class SelectFilter(RelatedFieldListFilter):
template ='filter/select.html'
i ara li hem de dir a Django que el faci servir, per això basta fer que el list_filter agafi aquest classe. Per exemple, suposem que la clau forana es diu localitat i que filtram per altres elements com tipus, actiu. Inicialment tindríem:
list_filter = ('actiu', 'tipus', 'localitat')
llavors el que farem serà:
list_filter = ('actiu, 'tipus',
('localitat', SelectFilter)
)
és a dir, ara podem dir a Django a més del nom del cap a filtrar, la classe que farem servir per presentar el filtrat.
La nostra classe és molt simple, així que la única cosa que hem de fer és pintar la plantilla.
La plantilla base la podeu trobar al codi font de Django mateix, amb el nom de select.html. La modificarem per a que pinti un desplegable:
{% load i18n %}
<h3>{% blocktrans with filter_title=title %} By {{ filter_title }} {% endblocktrans %}</h3>
<select id="s_localidad" style="width:150px;margin-left:5px;">
{% for choice in choices %}
<option {% if choice.selected %} selected="selected" class="selected"{% endif %}
value="{{ choice.query_string|iriencode }}">{{ choice.display }}</option>
{% endfor %}
</select>
Nota: el codi ara per ara no és genèric, és la primera aproximació que vaig fer per sortir del pas :)
La idea però és veure com fa Django els filtres. De fet el que
està fent és mantenir tota la consulta als paràmetres, així que el
que es feia amb la llista ho podem aprofitar també, mostrant el
contingut i al value mantenir el link del filtrat.
Això però no acaba de funcionar, ja que ara no tenim un link i necessitam dir-li a Django que apliqui el nou filtre. Com que l'admin de Django duu jQuery per la part de javascript, doncs el podem aprofitar i afegir codi per a que quan canvii l'element cridi a la url amb el nou filtrat:
<script type="text/javascript">
(function($) {
$(document).ready(function($) {
$("#s_localidad").change(function(){
var ref = $(this, 'option:selected').val();
window.location.href = ref;
});
})
})(django.jQuery);
</script>
i ja ho tenim! Ara sols queda fer una cosa més genèrica i reaprofitable, però a mi al manco ja m'ha servit per veure que això dels filtres té molt bona pinta.
Traducciones/Translations by apertium
0 comentaris, 0 trackbacks (URL) , Tags: Django
apibaleares.com
Escrit per Aaloy a 20 de July , 2012 a les 6:01 p.m.
El dimecres passat vàrem posar en producció la nova web de portal immobiliari del Col·legi Oficial d'Agents de la propietat immobiliària de les Illes Baleares, que es mostra baix el domini http://www.apibaleares.com. Com és habitual és una aplicació feta amb Django, i amb la idea de que sigui una aplicació escalable i sobretot mantenible.
El sector immobiliari no passa pel seu millor moment, tot val a dir-ho, però és un projecte que m'ha fet especial il·lusió. Per una part significa tractar amb molta gent que fa servir el programa, cada un amb les seves pròpies sensibilitats, i en un sector on la tecnologia informàtica s'obre pas molt a poc a poc.
És un dels primers projectes que fem amb Django 1.4, així que aprofitaré per fer cinc cèntims del que ha representat. Django 1.4 canvia un poc l'estructura dels projectes per a fer-los més modulars. Per la nostra banda hem aprofitat per també seguir amb el camí iniciat amb PropietariosOnline i SpokenPic i desenvolupar tota l'estructura de vistes mitjançant les class bassed views.
Tot i que hi ha gent a favor i en contra, he de dir que ara per ara, i pels tipus de projectes que fem les CBV són una benedicció. Podem estructura la informació molt millor i fer herència quan convé. En una web com http://www.apibaleares.com on hi ha un cercador adaptat per a cada API i on a més havíem de mantenir la compatibilitat cap enrera, l'herència ha representat poder reaprofitar molt de codi, i sobretot poder tractar amb les complexitats de fer enginyeria inversa de les cerques i continguts d'una manera molt més simple.
La recuperació de les claus d'usuari va representar un problema interessant. Per una part no volíem tenir que donar una clau generada per nosaltres a cada API, i per altra banda volíem que la recuperació de les claus fos el menys intrussiva possible. És a dir, que si algú volgués resetejar la clau ho pogués fer, però si un tercer intenta canviar la clau no ho pogués fer, ja que l'usuari rebrà un e-mail i la clau no es canviarà fins que es faci servir el link que s'envia.
Això ja es podia fer amb django-registration, i val a dir, que encara que està poc documentat, també es pot fer amb les darreres versions de Django. De fet basta un:
<a href="{% url django.contrib.auth.views.password_reset %}">Forgot your password?</a>
i si voleu, personalitzar o sobreescriure les plantilles dins registration (podeu copiar-les del codi font de Django i treballar a partir d'aquí):
login.html password_reset_done.html
password_reset_complete.html password_reset_email.html
password_reset_confirm.html password_reset_form.html
és el mètode que vaig seguir per afegir aquesta funcionalitat a PropietariosOnline. En el cas d'apibaleares, però, vaig preferir fer servir un altre tipus de recuperació, funcionalment idèntica, però que ja fa servir les noves característiques de criptografia de Django. Això em va dur a github i al code de https://github.com/brutasse/django-password-reset. Va ser un procés en paral·lem amb les necessitats d'SpokenPic.
Inicialment django-password-reset tenia una mancança (al meu entendre), que no feia un redirect després de fer un POST amb el canvi de clau. Vaig fer un primer fork i enviar el pegat a Brutasse. Val a dir que era el meu primer fork a github, que jo sóc més de mercurial i per tant de bitbucket. Tot i això després d'alguns intents vaig poder deixar un codi prou decent per a que Brutasse acceptàs el pegat i a més i fes millores addicionals. I vaig aprendre molt pel camí del codi de Brutasse, de com organitzava els unit-tests i del nou mòdul de criptografia de Django 1.4.
És per mi una de les meravelles del codi lliure. Una utilitat que a l'autor "ja li anava bé", la troba algú altra, li fa una millora, l'autor la considera i torna a repensar part del codi. Quan surt la nova versió el que passa és que tots tenim un codi millor que el que hi havia abans. Tothom hi guanya!
No sé si ho heu notat, però m'ho pas molt bé amb la meva feina. Tot i ser de tipus "passador de pena", escriure codi, gestionar projectes i aprendre coses noves m'encanta. En la nostra feina mai ho arribes a saber tot, sempre pots aprendre de com fa les coses un altre. Per mi un dels secrets de passar-ho bé és intentar millorar un poc a cada projecte. Aprofitar la benentesa per anar un pas més enllà, passeta a passeta, sense posar en risc el projecte cercant la bala de planta, però aprenent coses noves a cada passa.
Traducciones/Translations by apertium
0 comentaris, 0 trackbacks (URL) , Tags: Django
Receptes de South
Escrit per Aaloy a 27 de May , 2012 a les 11:58 p.m.
Pels aquelles que encara no el coneixeu, permeteu-me que us presenti south una eina per a controlar els canvis que es van fent al model i poder-los aplicar a la nostra base de dades.
South és una eina fantàstica, però no substitueix ni la necessitat de fer còpies de seguretat abans de fer algun canvi que pugui significar la destrucció o alteració de dades, ni la necessària coordinació entre els diferents membres de l'equip de desenvolupament.
Al tutorial des south està molt bén explicat tot, així que aquest apunt miraré de posar les receptes que he trobat més interessants, bàsicament per a no oblidar-me'n.
Canvi de nom d'un camp
South intentarà esborrar el camp i crear-ne un de nou. No sap que el que volíeu era un canvi de nom així que:
- Fem la migració de la manera habitual
python manage.py schemamigration app --auto -
South generarà una nova migració. Amb el vostre editor preferit, editau-la i eleminau les referències a l'eliminació i creació del camp. En el seu lloc, utilitzau directament l'API de South, per escriure
db.rename_column(table_name, column_name, new_column_name)
amb les columnes en sentit contrari per desfer la migració, obviament...
Hem modificat un camp a la BD directament
Les primeres vegades que es fa servir South costa que tot l'equip s'hi acostumi. Si la gent estava acostuamada a passar scripts sql, potser els ha passat i no ha fet ús de la migració.
South detectarà que la migració no està passada i intentarà passar-la, però com que els canvis ja hi són (la taula que es vol crear ja existeix, o el camp, ...), South donarà un error i intentarà tirar la migració enrera. En el cas de Postgres no hi haurà problemes, però amb bases de dades com el MySQL que no suporten la transaccionalitat d'esquemes donarà error.
No passa res si detectau que és això el que ha passat. Però hem de dir-li a South que consideri que la migració ja està feta. Per axixò farem:
python manage.py migrate app --fake
suposant que és la darrera migracío, o bé especificant quina migració s'ha de considerar aplicacada.
python mangae.py migrate app num_migració --fake
Ja tenim la base de dades i volem començar a fer feina amb South
Per a convertir una aplicació de la qual ja teniu la base de dades creada hem de fer
python manage.py convert_to_south app
això posarà l'aplicació sota el control de south, crearà la migració inicial i la marcarà com a aplicada.
Traducciones/Translations by apertium
7 comentaris, 0 trackbacks (URL) , Tags: Python Django APSL
Nous projectes i nous reptes
Escrit per Aaloy a 19 de May , 2012 a les 8:06 p.m.
Aquestes darreres setmanes, mesos fins i tot, han estat força intenses. Amb projectes que han duit moltes hores i que al final han sortit.
Rasec de Guillermo i companyia va representar posar en marxa una aplicació que va duu més de tres mesos de feina, però amb uns resultats inicials més que encoratjadors.
Pel nostre client de referència en el món turístic Fiesta Hotel Group hem posat en marxa tres noves webs en poc temps:
-
http://www.ushuaiabeachhotel.com/. Unes fotografies realment precioses que fan ganes d'anar-hi.
-
Sa Talaia Boutique Villa. En la mateixa línia que l'anterior i amb la idea de donar aquesta sensació de proximitat i unió entre els dos conceptes d'hotel.
-
Hotel Mallorca Rocks Com concepte d'hotel-concert, que tans bona acollida ha tingut dins Palma com a alternativa al sol i platja tradicional.
Tot això amb Python i Django com podreu suposar. Pel tipus de feina en que ens anam especialitzant cada projecte és un poc diferent. Al nostre voltant hi ha empreses que se dediquen a "fer planes web" que duen molts anys i quan veus el que han fet te n'adones que han anat repetint el mateix patró, el mateix disseny una i una altra vegada. És una altra manera de fer feina, a mi el que m'agrada és la varietat, que cada projecte representi quelcom nou.
I com que després de fer tanta feina ens fa ganes divertir-nos, surten idees com l'ensaimeitor una aplicació que parteix d'una llibreria que vaig fer per us intern i que mig en broma mig seriosament hem posat a la web per si serveix a algú més. Es tracta de generar informació per omplir i testejar aplicacions web.
I com no, també he de parlar d'un altra projecte que ens fa il·lusió per la novetat que representa per nosaltres i per la gent que hi ha implicada. És el projecte Spokenpic que fem com a "joint-venture" amb la gent de Menéame. Està resultant un projecte força divertit, que fem gairebé al 100% com a projecte fora d'hores habituals. I el que més en @gallir, que té uns horaris de feina molt desbaratats, programant a les 3 i a les 4 de la matinada.
Fa poc Spokenpic va fer-se públic. El "secret" ja no se podia mantenir per més temps, així que ara anam fent canvis i millores. Trob que per la gent que programau i llegiu aquest blog segurament també us serà interessant seguir-ne l'evolució del projecte, com a poc a poc es van afegint funcionalitats a partir de l'estructura bàsica.
Com a aventura que és Spokenpic no sabem on arribarà, però el que és ben cert és que ens ho estam passant d'allò més bé amb el projecte. Fer feina amb gent que es tant o més friki que nosaltres ens diverteix molt. El projecte és un repte molt interessant, ja que comporta la coordinació de dos equips de programació, amb dos llenguatges de programació diferents. On tothom tenim altres feines i la coordinació és fa al 90% amb mails i el gestor de projectes. Amb els horaris de Ricardo és com si féssim feina a franges horàries separades! :)
Encara hi ha moltes coses que resoldre, moltes característiques que ens agradaria posar-ho, però ara per ara el més important és veure què opinen els nostres primers pre-alfa-testers. A poc a poc anirem ampliant els cercles de testejadors. Trobam que pot ser una eina molt interessant per certs sectors de gent i certes ocasions, però qui ha de validar si la idea és bona o no, la idea és mostrar el que hi ha fet i validar la idea, si funciona fantàstic, si no té bona acollida, doncs mira, ens haurem divertit molt de totes maneres. En Ricardo ho conta molt bé a spokenpic fotos relatadas.
I això és tot, projectes i més projectes. Estam entretinguts darrerament i això és el que més m'agrada.
Traducciones/Translations by apertium
0 comentaris, 0 trackbacks (URL) , Tags: Python Django APSL
Django class based views - Index
Escrit per Aaloy a 12 de May , 2012 a les 8:15 p.m.
L'altra dia un amic em va dir que li resultava complicat anar navegant pel conjunt d'articles de les Class Based Views de Django, així que el que he pensat és fer una mena d'índex amb aquest apunt i posar-ho a la portada.
Traducciones/Translations by apertium
0 comentaris, 0 trackbacks (URL) , Tags: Python Django
vim tips
Escrit per Aaloy a 09 de April , 2012 a les 11:01 a.m.
He actualitzat la configuració que faig servir per a fer feina amb Vim per programar amb Python i Django a partir de l'article de sontek, llevant coses que no faig servir o que m'agrada més tenir en una consola: py.test, git, etc. i afegint algunes definicions que m'han anat molt bé a llarg dels anys. Ho podeu trobar a http://code.google.com/p/trespams-vim/
La millora més important és la descoberta de Pathogen, que ens permet gestionar millor els plugins i del plugin gundu que ens permet veure els canvis a un fitxer, com si fos un control de versions local.
Manteng cla configuració de backup i com que tinc força memòria als ordinadors que faig servir tenc llevat també el swapfile.
Aquest punt em servirà com a recordatori de combinacions de tecles útils a més de les habituals de vim.
- Tecla leader. Surt per tot a vim. Mapejada a coma (,)
- Tancar la finestra quickfix
,cc - Anar a una finestra:
ctrl+jklh - Veure els registres:
,r - Copiar a un registre texte seleccinat: "
y - Aferrar des d'un registre
"<registre>p" - Mostrar la finestra de canvis: `g
- Canvia el directori de treball:
,. - Executa el validador PEP8
,8 - Veure els marcadors:
:marks - Estableix un marcador m
- Ves a una marca
'<lletra>(o accent greu + marca per anar al lloc exacte on s'establí. - Obrir un buffer: :e
- Tancar un buffer:
:bqo bé:bw
Vim és tot un món, hi ha combinacions i plugins a voler. Personalment crec que el truc éstà en anar aprenent a poc a poc, perimer amb el més bàsic i després amb totes aquestes virgueries fins anar configurant un entorn on ens hi trobem còmodes.
Posant la nostra configuració a un sistema de control de versions, disposar de la nostra configuració local és trivial.
Traducciones/Translations by apertium
0 comentaris, 0 trackbacks (URL) , Tags: Python Django Vim IDE
Emprenedors
Escrit per Aaloy a 19 de March , 2012 a les 9:20 p.m.
Aquests darrers dies he tingut moltes reunions, la majoria emprenedors que volien demanar un pressupost per al seu projecte. I és aquest fet el que motiva aquest article, ja que amb totes aquestes reunions i amb altres que he tingut abans, hi ha força punts en comú i convé reflexionar-hi
Sóc un emprenedor
Enhorabona! Jo també. Per mi un emprenedor és un projecte d'empresari. És a dir, algú que té una idea de negoci i que ha de demostrar-ne la seva viabilitat i aconseguir posar-lo en marxa.
El que he vist és que hi ha gent que fa servir aquest concepte com a sinònim de "no tenc pressupost" o "no et puc pagar per la teva feina". Amb això ja començam malament. Primer perquè per poder menjar i mantenir el meu propi projecte, jo necessit cobrar la feina, que això de la informàtica potser t'omple espiritualment, però no t'omple la panxa.
Més enllà de l'anècdota, crec que tota aquesta publicitat de converteix-te en emprenedor més enllà de la visió romàntica també hauria de tractar aspectes pràctics. Que en les n-mil conferències i trobades es tracten molt per damunt els problemes reals. Emprendre també té un component de viabilitat econòmica, el pla de negoci que s'ha d'avaluar, s'han de saber d'on sortiran els recursos. Suposar que algú ens farà la feina gratis és, ja d'entrada, menysprear la feina que farà l'altra.
Vull dir, si la teva idea és molt bona, però no hi vols arriscar doblers, demanar a algú que faci feina per tu, i que hi posi la seva feina (que vol dir doblers indirectament) a canvi de un futurible, o d'un "ja cobraràs quan el negoci funcioni" per mi ja directament descarta el projecte. Si tu ja arrisques doblers, jo puc veure que t'ho la prens seriosament, i llavors puc contemplar fer feina per manco de la tarifa habitual o a posar-hi hores de manera gratuïta a canvi d'una participació a l'empresa. Però com dic, el risc ha d'estar equilibrat, i el programador o l'empresa de programació no pot assumir el risc del negoci.
Emprendre un negoci en el món de la informàtica amb unes mínimes garanties d'èxit no es pot fer sense pressupost. Segons el projecte serà més gran o més petit, però si no ets directament que fa el producte i el comercialitza, hauràs de pagar algú per a que et desenvolupi el producte. Però no tan sols això, has de pagar les despeses de l'empresa, de hostejar els servidors, del lloguer, telèfon, ...
Si la idea és molt bona, llavors hem de pensar que segurament algú en aquell precís moment potser també la té. I per tant, treure el projecte o no és una qüestió de qui serà el primer, o de qui pot suportar millor les pèrdues.
Personalment crec que la gent que va muntar Amazon per exemple, també eren emprenedors, però la seva idea de negoci i de ser els primers era tan clara que els primers anys gastaren gran quantitat de diners per tal de garantir que ningú altra podria seguir el seu ritme.
Així doncs, emprendre en un negoci online, sense tenir un mínim pla de negoci i uns recursos per a subsistir i aguantar una temporada és poc menys que suïcida. La setmana passada, i em va saber molt greu, em vaig trobar fent un mini-pla-de-negoci en 5 minuts per tal de demostrar a aquella persona, que venia tota il·lusionada que la seva idea sense un bon finançament no era viable. Tenir que ser tu que facis tocar de peus a terra la gent que te ve amb una idea no és gens divertit.
Senyors que organitzau conferències de recolzament a emprenedors, per favor, no aneu dient que tot és un camí de roses. Parlar de rondes de finançament, de grans números us fa quedar molt bé, però la crua realitat és una altra. Al nostre país és molt complicat trobar inversors, és molt complicat muntar un negoci online, tot són entrebancs: des de les passarel·les de pagament del segle passat, a la legislació que et ferma de peus i mans i no et deixa competir amb legislacions molt més orientades a negoci com l'anglosaxona.
El projecte el farà un freelance.
Tens una idea, la idea es bona i hi ha un poc de finançament. Però com que ets un emprenedor la feina te la fa un freelance. Eps! Cap problema, conec freelance força bons. El problema, és que això és sinònim de dir que "m'ho fa un paio i surt a un preu molt baix".
S'està pensant a molt curt plaç. No vull dir que no es tengui que mirar el preu, sinó que si el teu negoci dependrà del desenvolupament que et faci algú extern s'ha de pensar també amb la continuïtat del projecte. Els freelance bons són cars i van cercats. Si no és així és que quelcom falla. A un sector on no hi ha pràcticament atur, que algú faci feina per molt menys del preu de mercat a mi personalment em feia sospitar quan era a "l'altra costat".
Quan un empren i una bona part del seu negoci estarà basat en una aplicació web s'ha d'assegurar en el que pugui la continuïtat del desenvolupament. S'ha de preveure que hi haurà canvis, errors, adaptacions que algú haurà de fer. I en desenvolupament hem de pensar que agafar codi d'algú altra és molt més costos que si has de mantenir codi que ja coneixes o que està fet d'acord amb uns estàndards definits.
Potser pel teu negoci un freelances serà el que necessites, però al manco ho has de tenir clar i avaluar-ne els riscs. Contractar algú com a freelance sols pel fet que surt més barat és estar assumint un risc molt gran que pot posar en perill el negoci.
Com m'ajudarà Django?
Ja ho tenim clar, tenim pressupost i tenim clar el perquè hem triat aquella empresa o aquell desenvolupador freelance. La tecnologia amb el que un desenvoluparà l'aplicació web de la seva startup també té importància. Hem de tenir en compte dues coses: la facilitat per fer modificacions i l'escalabilitat.
Pensem que quan posam en marxa el nostre negoci poques vegades el que hem pensat funciona a la primera. Hem de fer adaptacions tant al model de negoci com a la tecnologia. I això ho dic també per pròpia experiència. La manera de fer les coses que teníem fa 4 anys no és la mateixa que tenim ara. Ens hem tingut que anar adaptant a les necessitats dels nostres clients per a poder donar-los servei. En alguns casos amb inversions internes de mesos i és un procés que no s'acaba mai (o que no s'hauria d'acabar mai).
Per això és molt important que la tecnologia que facem servir ens permeti desenvolupar ràpid, però sobretot que ens deixi fer modificacions i posar-les en producció tant o més ràpid que fent el desenvolupament des de zero.
Per què hem triat i recomanam Django? Doncs per això mateix. Django separa el que és la base de dades, del model de dades, regles de negoci i capa de presentació. Això vol dir que podem minimitzar l'impacte dels canvis, limitant-los a la capa necessària de l'aplicació. Ens permet escalar amb gent i separar rols de desenvolupament, la qual cosa ens permet desenvolupar i mantenir aplicacions grans amb un equip reduït de gent i mantenir els costs continguts.
L'estructura de desplegament de Django ens permet escalar l'aplicació afegint més servidors. Utilitats com Celery ens permeten distribuir tasques entre servidors, uWSGI, ngnix, redis, memcached, ... Hi ha tot un ecosistema de petites aplicacions altament especialitzades que treballen de manera harmoniosa i que ens permetran anar afegint més servidors i més potencia a la nostra aplicació així com aquesta ho necessiti, escalant per alt però també per baix.
Quan un desenvolupa amb Django de fet està desenvolupant amb Python (més HTML, més CSS, més Javascript) i si ha una cosa que caracteritza Python és la seva legibilitat i facilitat de manteniment.
Amb una estructura adequada per a dur els projectes, tenir que fer modificacions a un projecte passa de ser un malson a convertir-se en una tasca més. Acabam interioritzant dins l'empresa (la del client) que evolucionar és normal, que no hi ha problema, que no és un trasbals. Que es poden fer canvis, veure com funciona i si no ho fan desfer-los, ...
És la tecnologia elegida, junt a l'equip que la fa servir, el que ens permet estar tranquils quan hem de fer un canvi. Hi ha tecnologies que cada pas a producció és un esdeveniment que s'ha de planificar amb molta antelació, on sols es poden provar canvis cada cert temps perquè fer el desplegament és molt car, que no escalen cap avall el cost de mantenir un entorn de proves és prohibitiu.
Python i Django eliminen molts maldecaps i ens permeten estar tranquils. Però no us vull enganar, fer les coses pensant en el futur sempre té el seus cost. S'han de fer plans, s'han de pensar les coses per a que siguin testejables i mantenibles, no és un codificar com a un boig i ja veurem què sortirà, o codificar pensant que ja ho mantindrà un altra.
Emprendre amb seny
Així doncs si la vostra nova startup té un component informàtic important, sia web o no, pensau que hi ha un munt de coses a tenir en compte a més de la idea. Que la tecnologia compta, que l'equip que tens al darrera compta i que encara que no es digui massa a les conferències fer números i tirar de fulla de càlcul és molt important.
Per la meva banda potser em seguirà tocant de tant en tant desil·lusionar algú, però crec que ja he s'ha pres la molèstia de venir a fer una xerrada o demanar un pressupost, el mínim que puc fer és avisar-lo si veig alguna cosa que no encaixa.
No sé si fer d'advocat del diable, però el cert és que crec que si el projecte ha d'anar endavant un punt fonamental de la relació client-proveïdor ha de ser l'honestedat, i això fa que si veig alguna cosa que pot posar en risc el projecte o que el projecte no és viable, convé posar-ho damunt la taula tan aviat com es coneix. Pens que tract a la gent com m'agrada que em tractin a mi.
Traducciones/Translations by apertium
4 comentaris, 0 trackbacks (URL) , Tags: Informàtica Python Django APSL
Se buscan buenos programadores
Escrit per Aaloy a 04 de March , 2012 a les 4:36 p.m.
Hi ha una frase que diu que "a Internet ningú saps que ets un ca". El mateix es pot dir actualment del llenguatge de programació que mou una plana o aplicació web, mentre la plana faci el que ha de fer, a l'usuari que l'està utilitzant no l'interessa el més mínim amb què està feta.
Que avui en dia les planes acabin en php, asp, .do, no deixa de ser anecdòtic. Els bastiments de programació més moderns fins i tot amaguen amb què està feta la web a simple vista, a l'usuari no li cal la informació i potser estàs donant massa informació a algun visitant no desitjat.
Aquest apunt ve arrel d'un post a bonillaware, titulat se buscan buenos programadores. El post és força intressant i l'oferta de feina crec que també, però allà faig una petita reflexió: no entenc com una companyia que va de cools pot fer un error tan de base com cercar bons programadors en un llenguatge concret. Bé, ho entenc si el que cerques no són bons programadors, sinó gent experimentada amb una tecnologia en concret.
Vuit anys d'experiència no et converteixen en un bon programador en res. Si no has après el que s'havia d'aprendre els dos primers anys, afegir anys d'experiència sols pot fer que segueixis repetint sempre els mateixos errors.
Però bé, fent aquesta reflexió va hi bota algú que es sent profundament al·ludit. Potser no m'he explicat bé, però la idea és que si t'agrada la programació has de ser capaç de veure les mancances tant de Java com de qualsevol altra llenguatge de programació. Mancances i punt forts és clar. I si t'agrada molt programar i tens un mínim d'inquietud pel que fas, arribes a la conclusió que Java no és ni d'un bon tros el millor llenguatge per fer programació web.
Al comentari diu "de lo que se trata es de abstraer la realidad y modelarla en un lenguaje que las máquinas sean capaz de procesar".
Cert, això queda molt bé, però per la mateixa raó que descartam fer-ho amb binari, l'eina que triam per fer una tasca concreta té molta importància. No es tracta d'això de fet, es tracta de fer la feina de manera que sigui divertit fer-la, però també que sigui rendible, és a dir, que no ha de tenir un cost per damunt del retorn de la inversió i a més s'ha de poder mantenir i depurar amb facilitat.
En Bonilla no s'atura al fons de la qüestió, que és el perquè l'empresa que fa l'oferta ho fa en termes que contradiu la seva pròpia imatge, sinó que entra en la "guerra" del llenguatge "Yo prefiero divertirme haciendo cosas, no con el lenguaje con el que las hago".
Si hem d'anar per aquí jo vull triar les dues coses. Vull divertir-me amb els projectes, però a més vull divertir-me fent servir les eines que m'agraden. Per això faig feina amb Python, Django i Linux i no amb altres eines, perquè amb això amb diverteixo fent els projectes. Segurament podria guanyar molt més com a programador o cap de projecte Java (de fet guanyava més que ara), però no em divertiria tant.
Personalment em motiva molt que els temps entre la idea i la posada en producció s'acurcin, no tenir que fer sobrearquitectures, poder encaixar i triar les millors peces. Tenir eines potents de desenvolupament i depuració.
Java no és un mal llenguatge, o no ho era, ara ja l'han complicat massa. Per a la web la cosa encara està pitjor, es pot fer de tot, és veritat, però no amb la facilitat i eficiència d'altres llenguatges. Si anau pels apunts antics del blog hi veureu posts dedicats a Hibernate i Spring, escrits quan la gent ens mirava com a "rarets" quan dèiem que Struts no era "la manera" de fer les coses i que hi havia tecnologies millors.
En l'època que vaig estar a càrrec dels projectes webs de TUI España, record que fins i tot ens vàrem fer la típica "encerrona" amb gent de Thales per tal d'esbrinar si el que deiem era cert o no. Això de fer servir Ant pels desplegaments, control de versions, Tomcat enlloc de JBoss, Linux per als servidors, amb Apache al davant, Hibernate per a la part de persistència, Spring com a MVC i IoC i JSP-EL a la capa de presentació, era massa per gent acostumada a fer feina amb Oracle Forms i on el concepte de codi compartit que es tenia era una carpeta compartida Windows amb una fulla de càlcul.
Amb el vist-i-plau de Thales que no va tenir més remei que reconèixer que ells estaven començant a fer servir el mateix (quan nosaltres ja dúiem un grapat de projectes entregats) vam anar desenvolupant webs per TUIE.
La cosa, però és que cada cop el negoci requeria d'un temps de resposta més curt. El negoci turístic, com tants d'altres, necessita les coses, per ahir, i tot i que el nivell de desenvolupament Java-J2EE era prou bo, començarem a mirar cap a altres tipus d'eines que ens permetessin donar un temps de resposta millor. Rails començava a apuntar maneres i tot l'equip va anar a fer un curs a la CAEB, que per cert impartia Guillem Cantallops.
També era l'època de l'alliberament de Django. També apuntant molt bones maneres i fet amb Python. Vam avaluar les dues possibilitats, PHP ho descartàrem gairebé des del principi, massa emperons. Havíem fet el curset de Rails i els exemples i vam fer els primers prototips amb Django.
Juan (a.k.a @morenosan), com jo mateix, ja teníem experiència fent feina amb Python i sabíem de les possibilitats d'aquest llenguatge. Històricament les projectes Python van evolucionant molt bé, ja que el llenguatge és molt llegible i facilita la incorporació de nous programadors als projectes. Podíem haver tirat cap a Ruby i Rails, però ens sentíem més còmodes amb Python i Django així que tiràrem cap allà. El nombre de projectes webs que poguérem treure és multiplicà i el temps de posada en producció era senzillament ridícul.
Alguns projectes els reférem des de zero per a reduir-ne el manteniment. A més de poder-los fer en una fracció del temps i de codi, resultà que a més el rendiment era molt millor. La sobrearquitectura de Java estava fent molt de mal.
Va ser una època molt bona, però hi havia maror per ca'n TUI. Consultors de recursos humans que deien als desenvolupadors que "la mejor opción es irse" es van sumar a tot el que significa un procés de fusió amb HotelBeds.
A HotelBeds hi vaig trobar gent molt bona, però després d'estar-hi gairebé mig any em vaig trobar com al principi amb TUI, sols que aquesta vegada l'immobilisme era encara major. Condemnats a no fer res nou, a fer feina amb tecnologia obsoleta i/o propietària em vaig plantejar un canvi radical d'aires. APSL va sorgir de l'aposta per la tecnologia, per divertir-se programant i fent projectes.
Què el llenguatge de programació no importa? Sí que importa, importa si t'agrada el que fas i la programació no és una manera de passar hores davant un teclat com qui veu créixer l'herba. Importa si ets una consultora interessada en facturar el més possible, en aquest cas millor triar Java, .Net o posar TIBCO per orquestrar serveis que encara no tens. Importa si et dediques a la programació Copperfield, "entrego el programa y desaparezco", en aquest cas segurament el PHP t'anirà d'allò més be.
Però si t'agrada la programació la teva maledicció serà que no et conformaràs mai. Sempre cercaràs maneres de fer millor les coses, llenguatges que provar per si són millors que el que fas servir ara. Noves llibreries, nous editors, noves eines. Seràs un inconformista, un tocacollons si m'apures, però recorda que l'evolució tècnica (potser tota l'evolució) prové precisament de gent que no es conforma amb el que hi ha.
Traducciones/Translations by apertium
3 comentaris, 0 trackbacks (URL) , Tags: Informàtica Python Django Java
Django class based views (VII) - Epíleg
Escrit per Aaloy a 24 de February , 2012 a les 12:02 a.m.
El món de les class based views dóna per molt, la possibilitat de sobreescriure funcions, canviar paràmetres i anar combinant mixins fins a obtenir el que necessitam ens permet reutilitzar molt de codi i de manera elegant.
En aquest darrer post de la sèrie veurem algunes de les situacions més habituals en les que ens podem trobar i la facilitat amb que es resolen.
El formulari per defecte no és el que vull
Sovint quan editam un objecte mitjançant un formulari ens trobam que hi ha camps que no volem que s'editin. A l'exemple que hem fet servir fins ara imaginem que no volem que una vegada s'ha crear l'slug es pugui modificar.
Per fer això el que hem de fer és crear un formulari nou. Com que es
bo tenir les coses organitzades ho farem dins el fitxer forms.py
class SampleEditForm(forms.ModelForm):
class Meta:
model = Sample
exclude = ('slug',)
i llavors a la classe que implementa l'edició li direm que agafi aquest formulari per a editar l'objecte
from forms import SampleEditForm
class SampleUpdateView(UpdateView):
model = Sample
success_url = reverse_lazy('tutorial_list_sample')
form_class=SampleEditForm
Volem poder filtrar/ordenar els resultats
El ListView és un component idial per a fer cerques i filtres. Amb una fulla d'estils i un poc de javascript podem fer gairebé de tot. Per a no complicar-ho massa suposem que el que volem és establir dos filtres: un per quantitats positives i un de negatives, a més de l'opció per defecte que serà la de mostar-ho tot.
Afegirem els links dels filtres:
<a href="{% url tutorial_list_sample %}">All</a>|
<a href="{% url tutorial_list_sample %}?filter=positive">Positive</a>|
<a href="{% url tutorial_list_sample %}?filter=negative">Negative</a>
i ara a la classe el que farem serà sobrescriure el mètode
get_queryset de manera que tengui en compte si estam filtrant o no i
que apliqui el que correspon
class SampleListView(ListView):
model = Sample
paginate_by = 5
def get_queryset(self):
queryset = super(SampleListView, self).get_queryset()
filter = self.request.GET.get('filter')
if filter == 'positive':
queryset = queryset.filter(ammount__gt=0)
elif filter== 'negative':
queryset = queryset.filter(ammount__lt=0)
return queryset
Com es pot veure als exemples al final es tracta de'anar cercant el que s'ha de sobreescriure o extendre per a adaptar-lo a les nostres necessitats.
No vull acabar la sèrie sense referir-me a tota la part de Class Based
Views orientades a mostrar informació que ha d'estar relacionada amb
dates. Aquestes classes es troben a django.views.generic.dates i amb
el que hem vist de ben segur que no tindreu cap dificultat per a
fer-les servir.
Esper que us hagi agradat la sèrie i que us sigui profitosa. L'objectiu ha estat introduïr aquesta manera de fer les coses i organitzar el codi, que per projectes amb un bon grapat de models i manteniments, resulta amb menys codi, més ordenat i sobretot més mantenible i reaprofitable.
Traducciones/Translations by apertium
0 comentaris, 0 trackbacks (URL) , Tags: Python Django
Django class based views (VI) - Lists
Escrit per Aaloy a 23 de February , 2012 a les 8:44 p.m.
En aquesta sisena entrega (que hi ha algú per aquí ho ja us heu dormit tots?) veurem com podem mostrar llistes d'objectes que sovint podem trobar amb els CRUD.
Per a mostrar llistats (paginats o no) Django ens proporciona la
classe ListView que es pot trobar a dango.views.generic.list.
Aquesta classe és hereva de MultipleObjectTemplateResponseMixin i de
BaseListView.
BaseListView és el que la la feina, ja que és fill de
MultipleObjectMixin i de View implementant-ne el mètode get.
Com les vistes orientades als CRUD també en aquest cas tenim una plantilla per defecte, formada pel nom del model i el sufixe '_list'.
El que més ens interessa són doncs els mètodes i atributs de
MultipleObjectMixin. Primer de tot, però anem a fer-ho fàcil,
mostrarem la llista dels objectes que hem creat al post anterior:
A l'url:
url(r'^tutorial/sample/list/$', SampleListView.as_view(),
name='tutorial_list_sample'),
i al views.py podem fer
from django.views.generic.list import ListView
class SampleListView(ListView):
model = Sample
i ara haurem de definir la plantilla que tindrà com a nom
sample_list.html, primer, però és convenient conèixer quines
variables es passen al context, a saber:
- paginator
- page_obj
- is_paginated
- object_list
Els valors poden canviar en funció de si tenim paginació o no, però
les variables que tenim són aquestes. El que ens interessa per ara és
object_list que conté la llista d'objectes del model. De manera que
podem fer una plantilla del tipus:
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN"
"http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en">
<head>
<title>test</title>
</head>
<body>
<h1>Sample Detail List</h1>
<table border="1">
{% for sample in object_list %}
<tr>
<td><a href="{% url tutorial_update_sample sample.slug %}">
{{sample.slug}}</a><td/>
<td>{{sample.name}}<td/>
<td>{{sample.ammount}}<td/>
<td>{{sample.comments}}<td/>
<td>
<a href="{% url tutorial_delete_sample sample.slug %}">Delete</a>|
<a href="{% url tutorial_create_sample %}">Create</a>
</td>
</tr>
{% endfor %}
</tr>
</table>
</body>
</html>
Fixau-vos que no estic fent herència, l'html és molt simple etc. L'important aquí és veure com podem recórrer la llista d'objectes per montar les files d'una taula, i com amb el que ja sabem del CRUD podem enllaçar amb els manteniments.
Ara a l'hora de crear, editar o esborrar un registre podem elegir entre mostrar el registre o anar al llistat, afegint
success_url = reverse_lazy('tutorial_list_sample')
a les vistes que ens interessa ja ho tindriem.
Paginació
Com podem tenir molts resultats és comú que les llistes es mostrin
paginades. Per fer això el que hem d'afegir l'atribut paginate_by
que ens determinarà el nombre màxim d'elements que es mostraran per
plana.
class SampleListView(ListView):
model = Sample
paginate_by = 2
Si fem això i no modificam la plantilla, sols veurem dos registres, el que ens falta ara és afegir els controls de plana anterior i plana següent. Hi ham moltes maneres de fer la paginació i a la web en trobareu un bon munt, però per senzillesa aquest
{% if is_paginated %}
{% if page_obj.has_previous %}
<a href="{% url tutorial_list_sample %}?page={{ page_obj.previous_page_number }}">
Previous</a>
{% endif %}
<!-- current page -->
Page {{ page_obj.number }} of {{ page_obj.paginator.num_pages }}.
<!-- end current page -->
{% if page_obj.has_next %}
<a href="{% url tutorial_list_sample %}?page={{ page_obj.next_page_number }}">
Next</a>
{% endif %}
{% endif %}
Recordau que hem dit que al context es passava page_object? Doncs
fixau-vos com es fa servir per saber les propietats de la plana on
estam, el nombre de planes total, si la plana té una plana anterior o
una plana següent i el nombre corresponent a aquesta plana.
També fem us del is_paginated de manera que no mostrem el selector
de plana anterior i següent si sols hi ha una plana.
I ja ho tenim!
Al proper post, que tancarà la sèrie, veurem com podem limitar la informació que mostram als formularis d'edició
Traducciones/Translations by apertium
0 comentaris, 0 trackbacks (URL) , Tags: Python Django
Django class based views (V) - CRUD
Escrit per Aaloy a 22 de February , 2012 a les 9:38 p.m.
Creant un CRUD
En aquesta cinquena part veurem com podem crear tot el relacionat amb un manteniment, el famós CRUD (Create, Retreive, Update, Delete).
La part de Retreive ja l'hem vista a l'article anterior, però hi tornarem per a que ens quedi un exemple complet.
Partirem del següent model:
class Sample(models.Model):
"""this is just a sample model"""
slug = models.SlugField(max_length=50, unique=True)
name = models.CharField(max_length=100)
ammount = models.IntegerField()
comments = models.TextField(blank=True, null=True)
class Meta:
verbose_name = 'Sample'
verbose_name_plural = 'Sample'
def __unicode__(self):
return self.name
Django a més de DetailView ens proporciona les següents vistes ja
construïdes per fer aquest tipus de tasques:
* `CreateView` que gestiona la creació
* `UpdateView` que gestiona l'actualització
* `DeleteView` gestiona l'esborrat
Comencem!
Creació
Definim primer la url
url(r'^tutorial/sample/create/$', SampleCreateView.as_view(),
name='tutorial_create_sample'),
Hem anomenat a la nostra classe SampleCreateView així que la
definirem al views.py
from django.views.generic.edit import CreateView
from main.models import Sample
class SampleCreateView(CreateView):
model = Sample
Amb això Django ja ens crearà internament un formulari i l'intentarà
presentar a una plantilla que es construeix amb el nom del model i el
sufixe _form, és a dir, en el nostre cas sample_form.html passant
com a variable de context el formulari creat form
Si volem fer via amb :
<form action="."
method="post" accept-charset="utf-8">
{% csrf_token %}
<table>
{{form}}
</table>
<p><input type="submit" value="Save →"></p>
</form>
ja ens anirà bé. Django ja se n'encarrega de validar el formulari
contra el nostre model. És a dir, si posam quelcom que no sigui un
nombre sencer a amount ens generarà un error.
Si creau la plantilla i posau dades vàlides i intentau guardar us donarà un error: "No URL to redirect to. Either provide a url or define a get_absolute_url method on the Model.", poc a poc i ara hi anirem a això.
Mostram l'objecte
Encara que ja ho vàrem veure com es feia al post anterior, per completitud, ho farem també ara:
La url:
url(r'^tutorial/sample/retrieve/(?P<pk>\d+)/$',
SampleView.as_view(),
name='tutorial_view_sample'),
i al views.py
class SampleView(DetailView):
model = Sample
Recordau l'error del que hem parlat abans? Doncs el que farem és modificar el model per definir el `get_absolute_url, també podíem haver definit success_url o sobreescrit get_success_url però això ja ho vàrem veure, així que variarem un poc.
Modificam el nostre model
class Sample(models.Model):
"""this is just a sample model"""
slug = models.SlugField(max_length=50, unique=True)
name = models.CharField(max_length=100)
ammount = models.IntegerField()
comments = models.TextField(blank=True, null=True)
class Meta:
verbose_name = 'Sample'
verbose_name_plural = 'Sample'
def __unicode__(self):
return self.name
@models.permalink
def get_absolute_url(self):
return ('tutorial_view_sample', [self.id, ])
També podíem haver fet el mateix fent servir l'slug, amb la qual cosa
tindríem urls més amigables. Per això hem de canviar tant la url com
el get_absolute_url
class Sample(models.Model):
"""this is just a sample model"""
slug = models.SlugField(max_length=50, unique=True)
name = models.CharField(max_length=100)
ammount = models.IntegerField()
comments = models.TextField(blank=True, null=True)
class Meta:
verbose_name = 'Sample'
verbose_name_plural = 'Sample'
def __unicode__(self):
return self.name
@models.permalink
def get_absolute_url(self):
return ('tutorial_view_sample', [self.slug, ])
i la url:
url(r'^tutorial/sample/retrieve/(?P<slug>[-\w]+)/$',
SampleView.as_view(),
name='tutorial_view_sample'),
Què fa el permalink? doncs, com veis, crea una url per al nostre
objecte a partir del nom que li hem donat a la url que serveix per
visualitzar-ho.
Això ens permet no anar posant les urls a foc, sinó tenir-les definides amb un nom dins tota l'aplicació.
Si ara provau de fer anar el formulari veureu que ja funciona. Podem crear-lo i en guardar mostrar el resulta. Ja tenim dues de les quatre lletres, anem a veure les que falten.
Actualització
L'actualització és també molt senzilla. Farem servir UpdateView
però a diferència de la creació a la url li hem de passar bé la
clau primària de l'objecte o bé l'slug (sempres suposant que aquest
sigui únic).
la url:
url(r'^tutorial/sample/update/(?P<slug>[-\w]+)/$',
SampleUpdateView.as_view(),
name='tutorial_update_sample'),
pràcticament semblant a la de visualització, i ara a la part de la vista bastarà fer:
from django.views.generic.edit import UpdateView
class SampleUpdateView(UpdateView):
model = Sample
Ara sols ens falta lligar la url per poder anar a l'edició de
l'objecte, ho podem fer directament des de la barra de navegació,
però millor posar-ho també a la plana web, per exemple amb un link a
la plantilla de visualització per a que ens dugui a l'edició de
l'objecete. Ja que hi sóm posarem també el link de creació. Molt
resumidament, el nostre sample_detail.html quedaria de la forma:
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN"
"http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en">
<head>
<title>test</title>
</head>
<body>
<h1>Sample Detail</h1>
{{sample.slug}}<br/>
{{sample.name}}<br/>
{{sample.ammount}}<br/>
{{sample.comments}}<br/>
<a href="{% url tutorial_update_sample sample.slug %}">Update</a>|
<a href="{% url tutorial_create_sample %}">Create</a>
</body>
</html>
Eliminació
Ja hem arribat a la darrera lletra de l'acrònim. Per a esborrar un
objecte Django ens proporciona la vista DeleteView. Abans d'esborrar
es convenient demanar confirmació i a la implementació de Django es
té això en compte.
La url serà de la forma:
url(r'^tutorial/sample/delete/(?P<slug>[-\w]+)/$',
SampleDeleteView.as_view(),
name='tutorial_delete_sample'),
i a la view.py
from django.views.generic.edit import DeleteView
class SampleDeleteView(DeleteView):
model = Sample
si volem que tot funcioni per defecte, hem de crear una plantilla construida amb el nom del model i el sufixe "_confirm_delete".
En aquesta plantilla hem de demanar la confirmitat per esborrar el registre, enviant-ho a la mateixa URL d'esborrat però fent un POST. És a dir la url amb GET presenta la confirmació i amb POST fa l'acció.
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN"
"http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en">
<head>
<title>test</title>
</head>
<body>
<h1>Sample Delete</h1>
{{sample.slug}}<br/>
{{sample.name}}<br/>
{{sample.ammount}}<br/>
{{sample.comments}}<br/>
<p>Are you sure?</p>
<form action="."
method="post" accept-charset="utf-8">
{% csrf_token %}
<p><input type="submit" value="Delete →"></p>
</form>
<p><a href="<a href="{% url tutorial_view_sample sample.slug %}">Return</a>
</body>
</html>
A l'actualització o a la creació no feia falta dir-li a Django on volíem anar, per defecte anava a la visualització de l'objecte. Ara l'estam esborrant, així que no hi ha objecte. Així que obligatòriament li hem de donar la url a la que ha d'anar en cas que l'objecte s'hagi eliminat. Ho podem enviar a una plana amb el llistat de tots els registres, però com que encara no hem vist com es fa, ara per ara l'enviarem al formulari de creació.
class SampleDeleteView(DeleteView):
model = Sample
success_url = reverse_lazy('tutorial_create_sample')
I ja tenim el CRUD complet. Ens falta poder visualitzar un llistat paginat d'objectes i fer algunes accions bàsiques amb formularis. Ho veurem al proper apunt.
Traducciones/Translations by apertium
0 comentaris, 0 trackbacks (URL) , Tags: Python Django
Django class based views (IV)
Escrit per Aaloy a 21 de February , 2012 a les 11:31 p.m.
Començam a lligar les class views amb els models de dades, que això es posa cada cop més interessant.
Fins ara hem vist un ús de les class views molt genèric, és a dir, amb el que sabem podem mostrar planes webs i gestionar el treball amb formularis.
Es comú en la majoria d'aplicacions web modernes que les dades venguin d'algún lloc, d'una base de dades per exemple. Així que la gent de Django han creat tota una sèrie de mixin i vistes específices per a simplificar-ne el treball.
Mostrar un objecte
Suposem que volem mostrar les dades que tenim d'un usuari. Com sabeu
el model User de Django conté les dades dels usuaris i es troba a
django.contrib.auth.models.
Voldríem que en especificar-ne la clau primària a la url poder mostrar les dades de l'usuari corresponent.
És a dir, es tracta de mostrar informació que es correspon a urls d'aquest tipus
'^prefix/(?P<pk>\d+)/$'
o també
'^prefix/(?P<slug>[-w]+)/$'
Django ens proporciona la classe DetailView per fer precisament
això. És a dir, per estalviar-nos la major part de la feina de cercar
l'objecte que es correspon amb la clau primària o l'slug i passar-ho a
la plantilla per a la seva presentació. Per si us ho demanaveu
DetailView es troba a django.views.generic.detail.
Anem a veure un poc DetailView per dintre:
Aquesta classe hereta de SingelObjectTemplateResponseMixin i de
BaseDetailView. La primera a la seva vegada és fila de una vella
coneguda TemplateResponseMixin i en sobrescriu el mètode
get_template_names per tal d'establir un nom de plantilla per
defecte, que en el cas dels models és de la forma app/nom_detail.html,
és a dir, per al nostre exemple, si no posam cap nom de plantilla
agafarà per defecte auth/user_detail.html.
Per la seva banda BaseDetailView és filla de SingleObjectMixin i de
View i implementa el mètode get i li passa al contexte de la
plantilla la variable object amb el contingut de l'objecte que es
correspon a la clau primària.
Com que object pot resultar un tant confús, Django també passa el
mateix contingut a una variable amb el nom de l'objecte si el model té
el nom posat al verbose_name o bé farà servir el que li indiquem a
l'atribut context_object_name de la Classe.
SingleObjectMixin és la classe encarregada de fer el gruix de la
feina, i com no, també implementa el mètode get_context_data per a
poder passar més informació a la plantilla.
Així doncs, mostrar un objecte pot ser tan senzill com això:
al urls.py afegim:
url(r'^user/(?P<pk>\d+)/$', UserView.as_view(),
name='main_user'),
i no oblidem importar UserView, que crearem dins views.py i que pot
ser tan simple com:
class UserView(DetailView):
model = User
Com que User té assignat un verbose_name='User' Django passa a la plantilla
tant la variable object com la variable user i per tant en
condicions normals podríem fer servir tant un nom com l'altra.
En el nostre cas, i si com jo teniu dins el context processors la línia 'django.contrib.auth.context_processors.auth' us trobareu que la variable "no funciona". Això és perquè encara que se li passi, el context processor crea també una variable anomenada 'user' amb les dades de l'usuari autenticat (si existeix), i com s'assigna just abans de generar la plantilla, el contingut que prevaleix és el del context processor.
Una cosa que em va sorprendre del SignleObjectMixin és que a més del
mètode get_object implementa també get_queryset. De fet
get_objectcrida a get_queryset i li aplica un filtre per clau
primaria (pk) o bé per slug.
Què vol dir això, doncs que tenim un nivell extra de flexibilitat a
l'hora de determinar si un objecte és presentable per pantalla o no.
Basta sobreescriure el mètode get_queryset i independentment que la
clau primària (o l'slug) existeixi en la base de dades, podem fer que
l'objecte no es mostri. Per exemple, suposem que en el nostre exemple
anterior no volem mostrar l'usuari admin.
Podem fer
class UserView(DetailView):
model = User
def get_queryset(self):
query = super(UserView, self).get_queryset()
return query.exclude(username='admin')
de manera que UserView ens mostrarà la informació de qualsevol usuari llevat de l'usuari admin, en aquest cas mostrarà un error 404.
SingleObjectMixin com hem dit, pot tornar-nos un objecte a partir de
la seva clau primària o bé a partir de l'slug. Normalment en farem
servir un o altre. L'ordre de prioritat és primer el pk i si aquest
està buid, es mirar l'slug.
Al proper post farem una ullada a les facilitats que ens donen les generic class views per a gestionar els manteniments i veurem com es pot fer un cicle CRUD.
Traducciones/Translations by apertium
0 comentaris, 0 trackbacks (URL) , Tags: Python Django
Django class based views (III)
Escrit per Aaloy a 20 de February , 2012 a les 9:09 p.m.
En aquest apunt veurem com fer servir les generic class views per a fer feina amb formularis.
Seguirem la documentació de Django que tracta dels formularis. Allà ens fa referència a un formulari de contacte creat com:
from django import forms
class ContactForm(forms.Form):
subject = forms.CharField(max_length=100)
message = forms.CharField()
sender = forms.EmailField()
cc_myself = forms.BooleanField(required=False)
i la vista associada
def contact(request):
if request.method == 'POST': # If the form has been submitted...
form = ContactForm(request.POST) # A form bound to the POST data
if form.is_valid(): # All validation rules pass
# Process the data in form.cleaned_data
# ...
return HttpResponseRedirect('/thanks/') # Redirect after POST
else:
form = ContactForm() # An unbound form
return render_to_response('contact.html', {
'form': form,
})
El que farem ara és fer el mateix però fent servir les generic class views.
El primer que hem de veure és què hem de fer servir. Necessitam gestionar un formulari, la seva validació i tornar la redirecció cap a una nova plana web en cas que tot vagi bé.
Si feim una ullada a la jerarquia de classes del mòdul edit podem
veure que la classes que compleix tot el que volem es diu FormView.
FormView està composada pel mixin TemplateResponseMixin que ja
coneixem i BaseFormView que a la seva vegada està format per dos
mixins més FormMixin i ProcessFormView.
FormMixin ens proporciona els mètodes per a gestionar el form i
ProcessFormView que descendeix de View ens proporciona les respostes
a les cridades get, post i put.
L'equivalent fent servir les generic class views ens queda:
class ContactView(FormView):
form_class=ContactForm
success_url = reverse_lazy('main_thanks')
template_name = 'main/index.html'
def form_valid(self, form):
# process data
print form.cleaned_data
return super(ContactView, self).form_valid(form)
és a dir, hem de dir li a la classe quin formulari farem servir, això
ho feim a form_class o bé amb la rescriptura del mètode
get_form_class que ens ha proporcionat el mixin FormMixin o fin i
tot instanciant el formulari directament si fem la rescriptura de
get_form
Els mètodes form_valid i form_invalid ens permeten definir què em
de fer amb el formulari. Normalment ens bastarà sobreescriure el
mètode form_valid per a adaptar-lo a les nostres necessitats.
I qui crida al mètode is_valid del formulari? Doncs el post que
prové de ProcessFormView. Recordem que un mixin té accés a tot
l'objecte que el fa servir, i per tant també té accés als mètodes
el mixin FormMixin.
Suposem ara que volem que el nostre formulari agafi uns certs valors
inicials, això ho podem fer amb l'atribut initial definit a
FormMixin o bé a partir de la reescriptura del mètode
get_initial, segons la nostra aplicació ens convindrà un mètode o
un altre.
class ContactView(FormView):
form_class=ContactForm
success_url = reverse_lazy('main_thanks')
template_name = 'main/index.html'
initial = {'subject': u'This is a test form'}
def form_valid(self, form):
# process data
print form.cleaned_data
return super(ContactView, self).form_valid(form)
Si a més hem de passar dades addicionals a la plantilla Django,
FormMixin ens proporciona un altre vell conegut, el mètode
get_context_data que fa el mateix que feia al TemplateView. Això
sí, ara hem de tenir cura de cridar al mètode pare, ja que als kwargs
hi ha la definició del formulari. Per a evitar-nos conèixer què fa i
que no fa, millor cridar al mètode pare i a partir d'aquí afegir-hi
les variables que necessitem:
class ContactView(FormView):
form_class=ContactForm
success_url = reverse_lazy('main_thanks')
template_name = 'main/index.html'
initial = {'subject': u'This is a test form'}
def form_valid(self, form):
# process data
print form.cleaned_data
return super(ContactView, self).form_valid(form)
def get_context_data(self, **kwargs):
context = super(ContactView, self).get_context_data(**kwargs)
context['msg'] = u'Hello World'
return context
I no hem de perdre de vista que estam fent feina amb classes. Podem anar afegint els mètodes que necessitem a la classe, de manera que cada funció ens quedi manejable.
I ja per acabar un petit avís, he fet servir reverse_lazy per a
obtenir el valor de la url, però aquesta funció no hi és a Django
1.3. Per utilitzar el nom de la url enlloc de la url en sí, en Django
1.3 o bé hem de sobreescriure get_success_url o bé utilitzar
l'snippet que apareix a Django
Snippets i que resol això d'una manera
molt elegant:
from django.utils.functional import lazy
from django.core.urlresolvers import reverse
reverse_lazy = lambda name=None, *args : lazy(reverse, str)(name, args=args)
Això encara dóna per més, fins al proper article!
Traducciones/Translations by apertium
0 comentaris, 0 trackbacks (URL) , Tags: Python Django
Django class based views (II)
Escrit per Aaloy a 20 de February , 2012 a les 12:16 a.m.
En aquesta segona part veurem alguns dels usos més interessants de les class based views per a presentar informació i estalviar-nos feina.
TemplateView
Com ja vàrem veure a la primera part el TemplateView ens estalvia molta feina respecte a la manera tradicional de fer les coses, sols pel fet de passar ja el RequestContext ja recomanaria passar-nos al nou món de les Class Based Views, però encara hi ha més!
TemplateView incorpora dos mètodes més get_context_data i get.
Amb el get el que fa es cridar al mètode render_to_response que
ens proporciona el mixin TemplateResponseMixin i passant-li com a
arguments el diccionari que ens ha de tornar el mètode
get_context_data.
Per defecte get_context_data ens retorna un diccionari amb els
paràmetres que venen de la url, així si la nostra url és
url(r'^test3/(?P<id>\d+)/$', Test3View.as_view(),
name="test3-class-view"),
llavors get_context_data conté el diccionari {'id': 3} i a la
plantilla es passarà una variable anomenada params amb aquest
diccionari.
Suposem però que no és això exactament el que volem. Volem que passi
a la plantilla una variable anomenanda msg amb una salutació per als
lectors del blog.
El que farem serà sobreescriure get_context_data per a que passi la
informació que nosaltres volem a la plantilla.
class Test3View(TemplateView):
template_name = 'main/index.html'
def get_context_data(self, **kwargs):
context = super(Test3View, self).get_context_data(**kwargs)
context['id'] = self.kwargs.get('id')
context['msg'] = u'Hello blog!'
return context
Estrictament parlant no faria falta cridar al mètode pare de
get_context_data i de fet estam passant també la variable params
però fer-ho no fa mal i ens proporciona un cert grau de protecció
davant possibles problemes si un dia decidim que Test3View ja no
heretarà de TemplateView sinó d'una altra classe on ja s'hi posi
altra informació al contexte.
Al context_data i podem posar tota la informació que necessitem,
fixem-nos que és el mateix que feiem amb les funcions que acabaven
cridant al render_to_template sols que ara tot està més estructurat.
Suposem que volem que els missatge no estigui prefixat sinó que es generi a partir del resultat d'una funció. Abans hauríem creat la funció en algun lloc, a un mòdul apart, abans del mètode que renderitza la plantilla, ... Ara ho podem encapsular dins la mateixa classe
class Test3View(TemplateView):
template_name = 'main/index.html'
def get_message(self):
return u'Hello blog'
def get_context_data(self, **kwargs):
context = super(Test3View, self).get_context_data(**kwargs)
context = dict()
context['id'] = self.kwargs.get('id')
context['msg'] = self.get_message()
return context
I encara tenim més flexibilitat, gràcies al mixin
TemplateResponseMixin podem definir quina plantilla s'utilitzarà
segons ens convingui
Suposem que el paràmetre id ens serveix per a definir el nom de la plantilla, de manera que si existeix una plantilla que hi casi la presentarà i si no es quedarà amb la plantilla per defecte. Podríem fer
class Test3View(TemplateView):
template_name = 'main/index.html'
def get_message(self):
return u'Hello blog'
def get_template_names(self):
plantilla = 'main/index%s.html' % self.kwargs.get('id')
return [plantilla, ] + super(Test3View, self).get_template_names()
def get_context_data(self, **kwargs):
context = super(Test3View, self).get_context_data(**kwargs)
context['id'] = self.kwargs.get('id')
context['msg'] = self.get_message()
return context
Com veis la potència i la flexibilitat del que es pot fer amb les class based views no té res a veure amb el que es podia fer abans. Ara tenim molta més flexibilitat, més encapsulació i menys codi a escriure.
Al propert post parlarem de formularis...
Traducciones/Translations by apertium
0 comentaris, 0 trackbacks (URL) , Tags: Python Django
Django class based views (I)
Escrit per Aaloy a 19 de February , 2012 a les 8:26 p.m.
Class based views
Amb Django 1.3 s'introdueixen les "Class based views", una funcionalitat que ens permet modelar les nostres vistes com a classes i que a més intenta solucionar el no tenir que escriure sempre el mateix tipus de codi quan mostram una plana web o fem un manteniment lligat a un model de dades.
Les class based views ens permeten un nivell més alt de reutilització del nostre codi i a més ens permeten de mantenir-ne la cohesió. Fins ara o bé teníem que anar creant les funcions dins la mateixa vista, dins la mateixa funció o tirar de mòduls independents.
Amb les class based views pode anar creant funcions per a la nostra vista, de manera que després sigui més bo de fer reaprofitar-les extenent la classe.
En poques paraules, les class based views requereixen un temps d'adaptació i alguns diuen que aprendre un nou DSL (Domain Specific Language). Potser sí, però s'ho paga.
Abans de començar
Encara que podem utilitzar les nostres pròpies class based views, Django ens dóna ja fetes les més freqüents i un conjunt de classes que podem fer servir en cas que cap ens encaixi.
És en aquestes classes on hi ha gran part de la potència de les class based views. Per a fer-les servir hem d'entendre el concepte de Mixin, ja que això són aquestes classes: Python mixins.
Què és un mixin?
Un mixin no és res més que una classe que no està concebuda per tenir entitat per sí mateixa, sinó per extendra la funcionalitat d'altres classes fent servir l'herència múltiple de Python.
Un mixin, per tant, afegeix funcionalitat a les classes. Podem crear Mixins com si de mòduls Python es tractàs i aplicar-los a les nostres classes per dotar-les de més funcionalitat.
Hem d'entendre també com funciona l'herència múltiple en Python, de manera resumida: s'executarà el primer mètode que es trobi anant d'esquerra cap a dreta en la definició de les classes. És a dir, si tenim:
class Test(OneMixin, AnotherMixin):
pass
a l'hora d'executar un mètode, Python primer cercarà a OneMixin i si no ho troba anirà a AnotherMixin. Així doncs, al tanto que l'ordre en que posam les classes és important.
A diferència dels mòduls els mixins tenen accés a la instància
self de la clases, i per tant poden fer servir informació associada
a l'objecte i altres mètodes.
La classe View
Aquesta classe és el bessó de tot el muntatge, i si cap de les funcionalitats que ens proporciona Django ens serveix gairebé segur que començarem a crear la nostra pròpia extenent aquesta classe.
Anem a fer un exemple molt senzill, suposem que volem mostrar una
plana web, que anomenarem index.html i que posaré dins la carpeta
main. Com que començam amb això de les generic class views, anam
llegit i ens n'adonam que la classe View el que fa és capturar els
noms dels mètodes http i executar-ne una funció associada amb el
matix nom.
Ja està, ens deim, el que hem de fer es crear una classe que hereti de
View i escriure'n un mètode que respongui a la creidada GET i que ens
renderitzi la plana. Dit i fet, cream l'aplicació main i la
registram al settins.py i anam per feina:
Al mòdul views.py
from django.shortcuts import render_to_response
from django.views.generic.base import View
from django.template import RequestContext
class TestView(View):
def get(self, request, *args, **kwargs):
return render_to_response('main/index.html', {},
context_instance=RequestContext(self.request) )
i a urls.py de l'aplicació main
from django.conf.urls.defaults import *
from views import TestView
urlpatterns = patterns('',
url(r'^test2/$', TestView.as_view(), name="test-class-view"),
)
Executam i efectivament això funciona. Ens mostra la plana. Però la cosa no ens acaba de convèncer, no?, ja que si fem una ullada a la manera anterior de fer les coses, això ben bé es podria fer com:
from django.shortcuts import render_to_response
from django.template import RequestContext
def test(request):
return render_to_response('main/index.html', {},
context_instance=RequestContext(request) )
hem escrit fins i tot més, on està el guany, doncs? La resposta està en la reusabilitat.
Aquest patró de mostrar planes web senzilles es repeteix molt en les
aplicacions web, de manera que la gent de Django ja ha creat una
classe que hereta de Views i que fa precissament això i d'una manera
més genèrica, la classe TemplateView
Aquesta classe hereta de TemplateResponseMixin i de View.
TemplateResponseMixin és un mixin que té dos mètodes:
render_to_response i get_templates_names i dos atributs:
template_name i response_class. El que ens interessa ara és que
TemplateResponseMixin ens permet afegir a la nostra classe la
renderització de la plantilla que passem dins template_name o bé
que tornem a partir de la sobreescriptura de get_templates_names
Si fem servir TemplateView el nostre codi queda com:
from django.views.generic.base import TemplateView
class IndexView(TemplateView):
template_name = 'main/index.html'
i a urls.py
from django.conf.urls.defaults import *
from views import IndexView
urlpatterns = patterns('',
url(r'^$', IndexView.as_view(), name='main_index'),
)
Fins i tot podríem prescindir del codi de view.py i podríem fer:
from django.conf.urls.defaults import *
from django.views.generic.base import TemplateView
urlpatterns = patterns('',
url(r'^$', TemplateView.as_view(template_name='main/index.html'),
name='main_index'),
)
Com podeu veure l'estalvi en línies de codi comença a ser notable.
L'important, a més de veure com es pot extendre la funcionalitat de
Views és que vegem què senzill és separar la funcionalitat lligada
al GET de la funcionalitat lligada al POST, al PUT, o a qualsevol
cridada HTTP. Sols hem de crear una funció amb aquest nom i Django la
cridarà sempre que la nostra classe hereti de View.
Suposem doncs que el que volem ara és mostrar una plana diferent si algú ens intenta fer un POST contra la nostra plana. Django té un mecanisme per protegir-nos d'això és el CSRF o Cross Site Request Forgery protection, ara el que voldriem es desactivar aquest mecanisme per la nostra classe i mostrar una plana diferent d'avís.
Amb el mètode classis hauríem de fer un
if request.method=='POST':
#fes alguna cosa
else:
#fes una altra cosa
amb les Class Based Views la cosa queda molt més elegant:
class TestView(View):
def get(self, request, *args, **kwargs):
return render_to_response('main/index.html', {},
context_instance=RequestContext(self.request) )
def post(self, request, *args, **kwargs):
return render_to_response('no_post_allowed.html')
Això tal com està no funcionaria, ja que tenim pel mig la protecció de Django.
Per saltar-nos aquesta protecció li hem de dir a la vista que no la
volem i per fer-ho hem de sobreescriure el mètode dispatch que se
n'encarrega de la fontaneria de verificar quin mètode http
ens ve i la funció que hem de cridar.
from django.views.generic.base import View
from django.views.decorators.csrf import csrf_exempt
from django.utils.decorators import method_decorator
class TestView(View):
def get(self, request, *args, **kwargs):
return render_to_response('main/index.html', {},
context_instance=RequestContext(self.request) )
def post(self, request, *args, **kwargs):
return render_to_response('no_post_allowed.html')
@method_decorator(csrf_exempt)
def dispatch(self, *args, **kwargs):
return super(TestView, self).dispatch(*args, **kwargs)
Hem vist fins ara com fer servir la classe View i la classe
TemplateView, hi tornarem en els propers apunts, estau atents.
Traducciones/Translations by apertium
3 comentaris, 0 trackbacks (URL) , Tags: Django
Dajaxaproject, segona part
Escrit per Aaloy a 12 de February , 2012 a les 2:26 p.m.
Abans d'escriure aquest apunt de m'he adonat que era el post amb l'identificador 500. Un nombre rodó i que potser mereixeria algun tipus de celebració, però tanmateix no és l'apunt que fa 500, ja que en les proves del codi del blog, segurament vaig afegir i esborrar apunts que han fet anar cap endavant el comptador, realment encara falten prop d'una trentena d'apunts per a arribar al que fa 500, així que deixaré el cava a la gelera.
Hi va haver una proposta de celebrar-ho fent un regal, però pareix que la meva proposta de regalar una capsa d'un Nexus (tot i que comptava amb un possible patrocionador) no ha arribat a cuallar :-P
Així doncs el que faré és reprende l'apunt sobre el dajaxproject, i em centraré en l'altra component, anomenat Dajax.
Dajax
Ja vàrem veure com dajaxice ens deixava fer cridades AJAX cap a la nostra aplicació Django i organitzar-les. Dajax va més enllà in ens permet interactuar amb la interfície d'usuari des de codi Python. Fet servir amb mesura i seny ens pot ajudar també a fer molt més legible el nostre codi i a organtizar millor les accions derivades d'una cridada AJAX dins el mateix codi que les gestiona. No us penseu però que es tracta de quelcom com Pyjamas sinó que són més bé un conjunt de primitives que ens permetran agilitar la feina.
Per començar a jugar-hi necessitareu haver llegit el meu apunt
anterior (o la documentació del projecte) ja que Dajax fa servir la
seva llibreria germana dajaxice i també tenir instal·lat algun dels
frameworks javascript suportats, en el meu cas faré servir jQuery.
Recepta
A la part de servidor
-
Instal·lau django-dajax i posau
dajaxcom a aplicació alsettings.py. -
Posau
from dajax.core.Dajax import Dajaxa l'arxiu ajax.py de la vostra aplicació, junt ambfrom dajaxice.decorators import dajaxice_registerque ja havíem vist abans per a registrar la petició. -
A la funció AJAX instanciarem la classe,
dajax=Dajax() -
Farem el que tinguem que fer dins la funció AJAX i després anirem executant els mètodes de
dajaxque ens interessin per a interaccionar amb la part HTML de l'aplicació. -
Finalment retornarem el json amb
return dajax.json()
A la part de client
Hem de afegir l'arxiu javascript que correspongui segons la llibreria
que volguem fer servir. En el nostre cas jquery.dajax.core.js que es
troba a django-dajax/scr/. En el meu cas la cosa queda com:
{% dajaxice_js_import %}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.7.1/jquery.min.js"></script>
<script src="{{STATIC_URL}}/jquery.dajax.core.js"></script>
En executar-se l'event que ens interessi (un clic normalment) cridarem
la función AJAX que hem registrat de manera semblant a com ho feiem a
l'exemple de Dajaxice, però ara a la funció li passarem com a
paràmetre Dajax.process i un diccionari java de valors opcional, si
la funció té paràmetres.
Important Un ús molt habitual de Dajax es dona en combinació amb els formularis Django, en aquest cas el paràmetre de les dades del formulari. Per a que Django les pugui interpretar bé com a provinents d'un formulari hem de serialitzar-les, si fem servir jQuery això necessita d'una llibreria addicional anomenada serializeobject.
Què es pot fer?
Les funcions que ens proporciona Dajax ens permeten tant manipular l'arbre DOM de la plana web com cridar a funcions javascript ja que tinguem fetes.
*alert(message) Com el seu nou indica hi haureu endivinat,
mostra una alterta javascript amb el missatge que li indiquem.
-
assign(selector, attribute, valor)Donat un selector (id, classe, tipus, ...) i un atribut li asigna el valor el valor que li passam al paràmetre a tots els elements que casin amb el selector. Seria l'equivalent a fer amb jQuery per exemple$('.test').attr('title', 'hola')però enlloc de aplicar-ho sols al primer element com fa jQuery ho aplicaria a tots els elements que tinguin assignada la classetest. -
add_css_class(selector,value). Assigna tots els elements que casin amb el selector la classe css especificada a paràmetrevalue.valuepot ser una cadena o bé una llista de cadenes, en cas de ser una llista s'assignaran totes les classes de la llista. -
remove_css_class(selector,value). Funció complementaria de l'anterior, és a dir, enlloc de posar lleva. -
append(selector,attribute,value)
afegeix a tots els elements que casin amb el seletor l'atribut donat amb el valor indicat avalue`. -
prepend(selector,attribute,value)Afegeix a l'inici a tots els elements que casin amb el selector l'atribut especificat amb el valor que li passam com a paràmetre. -
clear(selector,attribute). Netja l'atribut especificat de tots els elements que casin amb el selector. -
redirect(url,delay=0). Redirecciona la plana cap la nova url després del temps especificat en el paràmetredelay. -
script(code). Executa el codi javascript especificat en el navegador. Per exemple, podem executar l'alert de l'exemple anterior. Hem d'anar un poc alerta amb aquesta funció i evitar fer-ne un mal ús posant-hi tot el codi. Si hem de fer crides, millor posar-les dins el seu corresponent arxiu javascript i fer tan solsscript(la_funcio()). -
remove(selector). Elimina tots els elements que casen amb el selector especificat. -
add_data(data,callback_function). Executa la funció que li especificam en el segon paràmetre amb les dades que hem posat al primer paràmetre. Com el cas d'scriptalerta amb abusar-ne.
Algunes recomanacions
Django té un sistema de plantilles molt potent, es poden fer servir
junt amb assign i remove per a posar els continguts que ens
interessin. Vull dir que no fa falta que una plantilla estigui lligada
a un view sinó que es pot fer servir directament. Mirau-ne
l'API.
Podem generar contingut HTML (o js) directament o fer servir una
plantilla a un fitxer i utilitzar el render_to_string.
Tampoc estam limitats a fer servir el sistema de plantilles de Django per a generar l'HTML segons el que es tengui que tornar potser un sistema com Mako us va millor. Personalment sóc partidari de no mesclar massa.
Errors típics
Sovint et pots passar una bona estona demanant-te perquè no funciona la teva cridada AJAX, així que anem a fer una llista de recomanacions:
-
Reinicia la consola de desenvolupament. A vegades la nova función no es registra bé i no es torna a generar el codi javascript. Si això passa, el símptoma és que tens un error dient que no es troba la funció.
-
Hi ha un error que diu que no es troba Dajax al javascript. Típic de quan t'has oblidat de incloure la llibreria específica pel frameworks que fas servir.
-
Es fa la cridad però no s'executa res. Doncs que t'has deixat el retorn a la funció AJAX. Recorda que normalment hi haurà un
return dajax.json()o si no les cridades no s'efectuaran. -
Django no és capaç d'interpretar el que li enviam al formulari. Ens hem deixat la llibreria
serializeobject -
Teníem events lligats a un element i ara no responen. Típic de quan hem generat nou contingut. Els events s'han de tornar a lligar amb els nous continguts encara que tenguéssin el mateix identificador. Potser fer servir .live() o .delegate() de jQuery ajudaria, però ara per ara jo el que he fet és tornar-los a lligar posant els elements a una funció d'inicialització i tornar-la a cridar amb
script.
Traducciones/Translations by apertium
2 comentaris, 0 trackbacks (URL) , Tags: Python Django
Dajaxproject, primera part
Escrit per Aaloy a 30 de January , 2012 a les 11:38 p.m.
Una de les preguntes més recurrents en les llistes de Django, junt amb la de quin editor fer servir és la de com fer cridades AJAX i quina llibreria utilitzar.
Django és agnòstic en el que fa a les llibreries javascript, tot i això la part de l'admin fa servir jQuery, però oficialment no hi ha cap llibreria especialment recomanada, Django és pot utilitzar amb qualsevol llibreria i dependrà del projecte que en triem una o altra.
El problema de que no es faci cap recomanació de com integrar cridades AJAX amb Django ens dóna molta llibertat, però també te l'emperò de que no hi ha cap guia de bones pràctiques sobre com fer-ho, des d'on posar la llibreria, quin nom li hem de donar, si la posam al views.py o no.
El la comunitat de codi obert quan hi ha un buid així i una necessitat ben aviat sorgeixen projectes que intenten donar una resposta. Un d'aquests projectes és el que us present en aquest article el dajaxproject.
Djaxaproject són de fet dues llibreries, dajaxice i dajax, la primera podríem dir que respon a la necessitat que us comentava de fer més senzilla la comunicació asíncrona entre les aplicacions Django y el codi javascript, la segona, dajax està molt més orientada cap a la capa de presentació i sobretot està lligada a un bastiment de javascript en concret, encara que tenim certa llibertat a l'hora de triar-lo.
En aquest apunt veurem primer Dajaxice i en un següent apunt faré també cinc cèntims de dajax.
Instal·lació
Suposaré com tantes vegades que teniu un virtualenv creat i Django
instal·lat. També hem de partir d'un projecte base.
El primer que hem de fer és instal·lar la llibreria, per això bastarà fer un
pip install django-dajaxice
La documentació explica força bé el procés, així que resumeixo:
- Posar
dajaxicedins la secció INSTALLED_APPS desettings.py - Assegurar-nos que tenim eggs.Loader descomentat als TEMPLATE_LOADERS
- Assegurar-nos que
django.core.context_processors.requestés dins el TEMPLATE_CONTEXT_PROCESSORS, si no ho heu fet ja, el meu consell és que copieu tot el TEMPLATE_CONTEXT_PROCESSORS que hi ha a la documentació, ja que a poc que l'aplicació cresqui segur que necessitareu afegir-ne algun. - Afegim als settings.py el prefixe que farem servir per distingir les urls de
dajaxice de les de urls normals de la nostra aplicació, per exemple la
documentació ens diu posar
DAJAXICE_MEDIA_PREFIX="dajaxice".
Amb això ja hem fet par de la fontaneria, ara ve la part més interessant i la màgia d'aquesta aplicació.
Organitzant l'AJAX
Dajaxice el farà és utilitzar el prefixe que hem configurat per tractar certes urls com a cridades AJAX. Djaxice el que ens permet és registrar automàticament les funcions que es publicaran com a cridades AJAX utilitzant un mecanisme semblant al que fa dervir Django per a l'admin.
D'aquesta manera, una vegada modificat l'arxiu urls.py podem anar
creant les funcions a publicar dins un arxiu ajax.py i registrar les
funcions. Així l'arxiu urls.py ens ha de quedar com:
#!/usr/bin/python
from django.conf.urls.defaults import patterns, include, url
from dajaxice.core import dajaxice_autodiscover
from django.contrib import admin
from django.conf import settings
admin.autodiscover()
dajaxice_autodiscover()
urlpatterns = patterns('',
url(r'^admin/', include(admin.site.urls)),
(r'^%s/' % settings.DAJAXICE_MEDIA_PREFIX,
include('dajaxice.urls')),
)
Utilitzant AJAX a les nostres plantilles
Dajaxice es capaç de posar dins la nostra plantilla el codi que necessitam per fer les cridades AJAX i publicar les funcions que farem servir a partir del codi Python (ja ho veurem un poc més endavant). Per fer-ho fa servir una llibreria que s'ha de carregar.
Així a la part de càrrega de llibreries de la nostra plantilla haurem
d'afegir: dajaxice_templatetags. Si encara no heu carregat cap
llibreria doncs quedaria com {% load dajaxice_templatetags %}.
Una vegada carregada la llibreria posarem el tag que genera el codi
javascript {% dajaxice_js_import %}. Ho podeu posar a la capçalera o
al peu de la plana. Personalment, com que ho sol fer servir junt amb
jQuery, ho pos al peu, abans de l'utilització de les llibreries, per tal de millorar la velocitat de càrrega de les planes.
Aquest codi es gener dinàmicament mentre estam desenvolupant. Us avís que de tant en tant es queda carregat en memòria i certs canvis requereixen reiniciar el servidor de desenvolupament. Si veis que heu publicat una funció i no surt, reiniciau el servidor.
En producció la llibreria té una opció per generar directament l'arxiu javascript i poder tractar-lo com un fitxer estàtic més.
L'estructura d'una plantilla bàsica amb djaxice seria doncs
{% load dajaxice_templatetags %}
<html>
<head>
<title>My base template</title>
<!-- també al peu depen de l'aplicació -->
{% dajaxice_js_import %}
</head>
...
</html>
Creant la nostra primera cridada AJAX
Com us comentava més amunt, una de la gràcia de dajaxice és que ens
permet organitzar les nostres cridades AJAX. Així el que farem serà
crear un arxiu ajax.py dins la nostra aplicació, de manera que podem
agrupar cridades per aplicació.
Suposem que hem creat una aplicació anomenada main, la posam al
settings.py del projecte i cream l'arxiu ajax.py
Crear una funció i publicar-la per a que la servim via AJAX és tan senzill com això:
#!/usr/bin/python
# -*- coding: utf-8 -*-
from django.utils import simplejson
from dajaxice.decorators import dajaxice_register
@dajaxice_register
def hello_world(request):
return simplejson.dumps(
{'message':'Hello World'})
És a dir, importam simplejson per a fer la transformació cap a json,
que és el que volem servir (o xml, o html, tant fa), cream una funció
com ho feim al views.py de Django i la decoram amb
dajaxice_register. Això junt el autodiscover que hem posat a
urls.py fa que ara la nostra aplicació pugui respondre a cridades
AJAX.
Cridant des de l'html
Per cridar a la nostra funció es pot fer directament amb l'event onclick directament a l'HTML,però a mi particularment em fa molta grima tenir aquesta mescladissa, així que prefereixo tirar de jQuery encara que per fer la crida AJAX no el faci servir. Recordem que l'important no és el mètode amb que es fa la crida, sinó l'organització i facilitat de mantenimient (i depuració) que ens dóna la llibreria.
Així, en el meu cas quedaria:
<script src="https://ajax.googleapis.com/
ajax/libs/jquery/1.7.1/jquery.min.js">
</script>
<script type="text/javascript">
$(function(){
$('#testme').click(function(event){
event.preventDefault();
Dajaxice.main.hello_world(test_callback);
});
function test_callback(data){
if (data==Dajaxice.EXCEPTION){
alert('Opps, alguna cosa dolenta ha passat');
} else {
alert(data);
}
}
});
</script>
on #testme fa referència a un enllaç al qual hem assignat l'event.
L'interesant d'això és que tenim a la nostra disposició una funció javascript que mapeja la que tenim al codi Python i a la qual podem cridar amb uns paràmetres. El primer d'ells és la funció de tornada (el callback) que utilitzarem per gestionar la resposta i l'altra és un paràmtre codificat com json que passarà com a paràmetre cap a la funció.
Hem de dir que dajaxice sempre fa un POST contra la url, gestionant els problemes de CSRF.
Si posam DAJAXICE_DEBUG=True al settings.py o millor dit, ve així
per defecte, a la consola de desenvolupament de Django podem veure
tant la cridada que fem com la resposta i els errors que hi pugui
haver.
La funció de tornada es defineix amb un paràmetre que contindrà el valor de retorn de la nostra cridada AJAX. Si és un json podrem accedir als valors directament, però com he comentat abans, no estam limitats a tornar json, podem tornar una cadena de text, xml, html, ... el que necessitem i poguem tractar amb javascript.
És interessant veure com es tracten els errors o les excepcions. Si hi ha problems al nostre codi Python i es llança una excepció, dajaxice la capturarà i llavors data contindrà el valor Dajaxice.EXCEPTION, amb la qual cosa sabem que quelcom ha anat malament.
He de fer servir sempre Dajaxice?
Depèn, algunes vegades serà més convenient fer una cridada directa amb jQuery, per exemple quan volem tenir més control del que passa quan s'inicia la cridada, o volem poder-la cancel·lar.
El cert, però, és que dajaxice ens permet organitzar millor el codi i gestionar d'una manera sistemàtica les nostres cridades AJAX. Com sempre no hi ha cap veritat absoluta, potser servirà en el 80% dels casos i això ja significa una gran ajuda.
Traducciones/Translations by apertium
0 comentaris, 0 trackbacks (URL) , Tags: Python Django
Fakedata i Faketpv
Escrit per Aaloy a 20 de January , 2012 a les 6:07 p.m.
Fa temps que estic fent feina a hores mortes amb un projecte que m'ajudi (que ens ajudi) en el nostre desenvolupament d'aplicacions, en una de les tasques més ingrates: tenir que testejar l'aplicació i afegir dades de prova.
Un dels projectes amb que estic jugant és SST un envolcall per Selenium que fa molt senzill fer scripts per testejar webs des del navegador i de manera desatesa. He fet les proves inicials i la veritat és que promet molt el projecte.
En aquests moments tenim moltes de les nostres aplicacions monitoritzades fent servir Twill i connectades a Nagios, de manera que podem saber quan l'aplicació cau o no dóna la resposta esperada i que ens avisi mitjançant una alarma de Nagios. SST no està cridat a substituir Twill de moment, però si que ajudarà molt a evitar repeticions de tasques ja que és força més àgil que Selenium, i al meu punt de vista més entenedor.
L'altra línia de treball va dirigida a la tasca de generar dades de prova per les aplicacions. Supòs que també us heu vist amb la necessitat de testejar una aplicació en condicions semblants al que ens podem trobar a la realitat, i això vol dir omplir la base de dades amb cents o mils d'usuaris, d'adreces, ...
Vaig estar cercant eines que em deixessin fer això però no vaig trobar res que m'agradàs totalment, així que vaig optar per l'opció del mig, és a dir, tenir un bon conjunt de funcions que pugui cridar quan vulgui, que pugui créixer sense massa problemes i que pugui utilitzar per fer els programets per omplir de dades les meves aplicacions. Així va néixer Fakedata.
Fakedata és un conjunt més o manco organitzat de funcions per generar dades aleatòries però amb criteri per a les aplicacions, orientat sobretot a Espanya. Hi ha funcions per a generar noms de persona, per a generar CIF, NIF, nombres de telèfon, codis postals, targes de crèdit, ... El gruix de rutines les he anat trobant d'Internet o a altres llibreries i les he pogut adaptar, algunes les he creat des de zero. Em sembla bona idea tenir-ho tot organitzat, agrupat i a un únic punt, de manera que es pugui fer servir ràpidament.
Amb la mateixa idea de testejar aplicacions he afegit un mòdul nou al projecte anomenat Faketpv, amb la idea d'emular les principals passarel·les de pagament espanyoles. De moment ja tinc CECA i SERMEPA.
La idea és que quan estam en mode desenvolupament tenir que connectar a les passrel·les de pagament, encara que sigui al seu entorn de test, et fa perdre un temps preciós i sovint dificulta la depuració. Faketpv està pensat per ajudar al desenvolupament: mostra els paràmetres que reb el TPV i torna a generar la signatura per a que es pugui comparar amb la que generaria el TPV si li passàsim aquests paràmetres. A més retorna la resposta com si la venda s'hagués produït realment, sense tenir que posar cap tarja i fent-ho tot en local.
Com que funciona com a un servei es pot fer servir des de qualsevol aplicació, talment seguint la documentació de cada passarel·la però substituint el seu entorn de test per l'aplicació local.
El projecte està a Bitbucket i està obert a contribucions. A veure si entre tots en feim un de bo.
Traducciones/Translations by apertium
0 comentaris, 0 trackbacks (URL) , Tags: Codi lliure Python Django
Resum del 2011
Escrit per Aaloy a 24 de December , 2011 a les 5:53 p.m.
Ara que ja estam a punt d'acabar el 2011 faré el que toca fer per aquestes dates no és més que fer un resum de l'any que acaba i fer un poc d'avançament del que esper en el 2012.
Com bé sabeu, darrerament per mi parlar d'informàtica i feina és parlar d'APSL, el projecte que iniciàrem fa uns anys amb un grapat de socis, parlar de Python, de Django i de GNU-Linux.
APSL
El febrer de 2012 ja farà tres any de la posada en marxa del projecte APSL, que per qui no ho sap és l'acrònim d'Advanced Programming Solutions SL. El nom el pensarem a un dinar entre en Bernat, en Xus i jo mateix, a partir d'un domini de quatre lletres que vaig poder comprar gairebé de casualitat. Primer va ser el domini i després va ser el nom. Ara no record ben bé qui va proposar la versió final, però record clarament que en parlàvem amb Xus.
Sigui com sigui enguany ha suposat passar dels 2 membre amb que iniciàrem el 2010 a passar a quatre i tenir que canviar d'oficina, passant de la incubadora del Parc Bit a un despatx compartit a l'edifici NTIC del mateix parc.
De fet, gairebé es podria dir que hem acabat l'any amb 5 persones, ja que Xus s'ha incorporat como a freelance també al projecte.
El 2011 ha estat un any dur i divertit a la vegada. Hem tingut prou feina per mantenir-nos ocupats amb el que més ens agrada. Hem fet feina amb projectes grans, que implicaven a tot l'equip durant mesos. Però no tot han estat alegries, com per tot la crisi s'ha fet notar i els plaços de pagament s'han allargat algunes vegades fins a límits que particularment m'han fet passar més pena de l'habitual, i qui me coneix ja sap que sóc passador de pena de mena.
En la part de projectes estic molt satisfet de la feina que hem fet amb la gent d'e-comerce de Fiesta, l'encaix ha estat molt bo, i en aquests moment tenim la web que desitjava el client i a més corrent damunt Django com a CMS. Passaren de un gestor de continguts genèric de PHP a un gestor de continguts fet a mida de les seves necessitats. El canvi no va ser gens traumàtic i ens ha permès no tenir que dir "això no es pot fer" o "això durà molt de temps". Ens ho passam molt bé amb aquest projecte, tant pel client/amic com perquè tenim una gran llibertat per fer optimitzacions. La darrera ha estat l'actualització de Varnish a la darrera versió, amb plugins especials adaptats a les necessitats de Fiesta.
Encara que més petitó, també ens ho passàrem molt bé amb la web de Sa Talaia del mateix Fiesta, aquí perquè va suposar poder retirar una web feta en flash que hi havia i desenvolupar-la amb Django i jQuery. Unes fotografies magnífiques de l'hotel fan que sigui una web que crec que s'ho paga visitar, i ja no dic rec d'anar a l'hotel (llàstima que no m'hagi tocat la loteria).
També estic molt content de la col·laboració assolida amb Xavi i el seu equip en el desenvolupament de la plataforma Txerpa. Content pel que significa particpar en projectes emprenedors i pel muntatge tècnic que hi ha al darrera. La web de Txerpa ja pot donar una idea del que hi ha al darrera, però la part més important és la que no es veu, i que permet donar d'alta i gestionar els usuaris de l'aplicatiu i la seva interacció amb el sistema.
El projecte que s'inicià l'any passat de Globalbooking pareix que poc a poc es va consolidant. En Rafa, l'emprenedor que hi ha al capdavant del projecte és un caramull d'idees i periòdicament anam fent canvis a la web. També s'ha convertit en un amic i company de batalletes. Tant amb ell com amb l'equip de Jesús de Valadis fa molt bon fer feina. Una vegada més ens sentim molt lliures de proposar millores i optimitzacions cercant el millor pel seu negoci. Sé que insisteixo amb això de que proposis millores i que la gent se les escolti, però si heu fet feina per grans empreses sabreu que sovint això no és així. Per mi estar a APSL està significant a més d'un repte personal, una gran satisfacció en veure que pots aportar solucions.
A finals d'estiu llançarem propietarios online, una aplicació web destinada a la gestió de comunitats de propietaris, que decidírem fer al vaixell de tornada d'Eivissa. Obrirem un període de proves i dins el 2012 es començarà amb l'explotació comercial del producte amb col·laboració amb una altra empresa. És un projecte propi que esperam que sigui útil a molta gent i que ens ha permès fer feina provant noves idees a l'hora de programar.
El final del trimestre ha estat mogudet. Projectes que potser s'aniran concretant dins el 2012, molt d'ells lligats a emprenedors, tant en el sentit d'aquell qui creu en el projecte i es llança a l'aventura com projectes novedosos que volen posar en marxa empreses ja consolidades. M'encanten aquests tipus de projectes, sempre suposen un repte, hi ha molta cosa nova, molta implicació tecnològica. Són projectes en els que t'impliques molt personalment, en algun d'ells si l'economia m'ho permetés fins i tot seria capaç d'invertir-hi, però per ara ens hem de conformar en fer-lo el millor possible per a que puguin arribar bé a port.
Dels darrers projectes destacar un de molt divertit Regala unas vacaciones, va ser un mini-deathmarch que em va deixar fora pont, però on m'ho vaig passar molt bé, tant pel bon rollo amb el client com per la web en si mateixa. El projecte va suposar posar a prova una nova manera de generar pdfs que no havíem provat fins a les hores. Ideal per quan hi ha poc text i molt component gràfic. Miraré de parlar-ne en aquest blog.
El 2012 es presenta també mogudet, projectes en marxa que han de finalitzar dins el primer trimestre, projectes propis com el de Propietariosonline que volem dur endavant, i un projecte nou que es va perfilant a poc a poc gràcies a l'ajuda i els comentaris de gent com Sebas o Jordi. Volem posar en marxa un servei d'instal·lació i configuració d'equips per a l'explotació d'aplicacions web fet a mida de les necessitats de programadors, dissenyadors i clients. És a dir, enlloc d'anar a serveis de hosting tipus macdonals, anar al restaurant a la carta, configurant un servidor a la mida de l'aplicació, amb configuracions diferents depenent de l'aplicació o aplicacions que hagi de dur el servidor: configuracions per Django, per Rubi, per Drupal o Wordpress, ... Pens que pot ser un servei molt útil, però el temps dirà si té la resposta que esper.
No puc acabar el capítol APSL sense fer referència a la quantitat d'amics que ens van donant suport i que s'interessen pel que feim. Menció especial per la gent de l'Ibit que ens ha convidat als seus saraus, als quals sempre hi anam amb la màxima il·lusió. A amics com Pau, Benjamí, Ricardo, Marcos, Joan, Sebas, Jordi, Pep, Xus, Eugeni, Paco, Eduard, Suki, Bruno, Xisco, ... (me deix gent, segur) que de tant en tant s'han passat per l'oficina a fer un cafè i a compartir amb nosaltres penes i alegries. He comanat cafè per un grapat de mesos, ja sabeu que ca nostra és ca vostra. Moltes gràcies pel vostre suport amics!.
El 2012 ja veurem com anirà, com us dic, molts projectes a concretar, però després ja veurem. El que sí m'agradaria és poder consolidar bé el projecte APSL i d'una manera o d'altra poder anar aglutinant al voltant del projecte al tipus de gent que m'agrada: gent apassionada per la seva feina i pel codi lliure i la informàtica i amb unes ganes d'aprendre coses noves que no s'acaba mai.
Python i Django
Encara que mantenim aplicacions amb la versió 1.2 de Django ja desenvolupam les noves aplicacions amb la versió 1.3, fent us de les "generic class views" tant com podem, i de utilitats com crispy forms. Que Twitter alliberàs el seu bastiment css i utilitats com crispy han fet que desenvolupar aplicacions de backoffice sigui molt més senzill que abans.
Hem incorporat un bon grapat de llibreries i utilitats a la nostra borsa de programació durant el 2011. Una de les més interessants és Redis, una base de dades NoSQL que poc a poc va reemplaçant a Memcached en els nostres projectes i que ha resultat un complement ideal pel sistema de coes Celery.
Sentry ha resultat un complement ideal per a la monitorització de les web i la gestió dels error no controlats (que sempre n'hi ha). El django-constance ens ha anat fantàstic per a mantenir webs on es tenia que canviar la configuració molt ràpidament sense reiniciar, django-compressor ens ha permès arribar al plus de velocitat de descàrrega que necessitàvem en algunes aplicacions empaquetant css i javascript.
Al primer trimestre del 2012 ja es preveu que surti la versió 1.4 de Django. Hi ha força novetats però la majoria són correcció de bugs. La que més ens afectarà en positiu serà la possibilitat d'interncionalitzar els noms de les urls. Fins ara ho fèiem amb una utilitat apart. Encara que el soport per a la internacionalització de Django sempre ha estat molt bo, trob que li faltava aquesta part dins el nucli del bastiment per passar de ser bo a excel·lent.
Hi ha eines que s'han consolidat com a imprescindibles aquest 2011: south per a la migració d'estructures de dades, pip per a la insal·lació dels paquets associats a una aplicació, virtualenv i virtualenvwrapper per aïllar aplicacions dins el seu propi entorn i fabric, que hem configurat per a que el desplegament i actualitzacions sigui cosa de segons.
Al 2012 esperam molt més de Python i Django. Hem començat a desenvolupar webs per a dispositius mòbils i un dels propers projects ha d'estar adaptat a tablets. Això a més del repte del projecte ha suposat tenir que adquirir una tablet per hom, però sincerament crec que sols en necessitàvem l'excusa :D
Adéu al PPC
Feia estona que volia "jubilar" el PowerPC G5. És un ordenador que no m'ha donat el rendiment que m'esperava, la relació qualitat preu trob que ha estat força dolenta si la comparam amb un portàtil de la mateixa època. El rendiment de l'equip amb dos processadors i 3 Gb de RAM sempre ha estat molt per davall del rendiment d'un portàtil Dual Core amb 2 Gb de RAM.
Fa quinze dies el G5 va decidir que no es posava en marxa. No tenc ni idea del que li passa, però sospit que pot ser la fon d'alimentació, i això he llegit que pel cap baix eren 300 - 400 eur. Sumat a les ganes que li tenia varen motivar la renovació d'equip de casa, i com que la mitja de compra d'equips és d'un cada 6 anys, doncs ja he aprofitat per fer el canvi complet i posar-me dues pantalles planes de 24" que fan goig. Una oferta molt bona de Dell en té la culpa. L'ordinador és un T1600, potent, però que no m'està deixant molt satisfet pel renou que fa, es nota molt que està encès i el disc dur també és molt renouer.
Un amic al que li agraden molt els Range Rovers em va dir que la solució per a que un cotxe d'aquests no faci renou és posar-li uns altaveus més potents a l'equip de música. Potser m'hauré d'acostumar a fer el mateix amb l'ordinador i acostumar-me a fer feina amb els cascs de música posats, però la veritat és que m'esperava més per la qualitat d'equips a la que me tenia acostumat Dell.
I la joguina nova es diu Asus Transformer
Seguin la rocomanació de Sebas el meu tablet va ser un Asus transformer. La relació qualitat-preu és bona i per ara n'estic content. M'agradava molt també el Xoom, però això significava haver de comprar un portàtil addicional per als meus desplaçaments fora de l'Illa. El Latitude 2100 que tinc encara va bé, però amb 4 hores de durada de la bateria fa que tengui que anar carregat amb el transformador si vull llegir el correu sense passar pena.
L'Asus té una càmera pitjor que el Xoom i no té flash, però la incorporació del teclat i una durada d'unes 16 hores em van fer decidir-me. Soluciona tres problemes: tenir una tablet per a poder provar les apliciacions web, poder disposar d'un equivalent a una portàtil per escriure sense problemes i tenir una bateria amb una durada prou gran per no haver de passar pena.
Bones festes
I això és tot per ara, si no ens tornam a llegir al 2011 esper que passeu unes bones festes i un bon començament d'any. Gràcies per ser per aquí i passar l'estona amb mi. Esper que ens anirem retrobant tant per la xarxa com en persona. Volia dir en la vida real, però estic pensant que la xarxa és real i també ho són els amics que hi fas, si va bé ja ens coneixerem cara a cara i si no pot ser això no ha de servir d'excusa per minusvalorar la gent que coneixes mitjançant la web.
Acab, en serio,
BONES FESTES!!!!!
Traducciones/Translations by apertium
5 comentaris, 0 trackbacks (URL) , Tags: Informàtica Python Django APSL
Reflexions
Escrit per Aaloy a 20 de November , 2011 a les 2:13 a.m.
Reflexionant
Aprofitant que avui és una jornada de reflexió me pareix que aprofitaré per repassar un poc l'anecdotaria personal d'aquestes darreres setmanes, a veure si en puc treure també algunes conclusions.
No sé el que vull
Donc així ens va venir una persona a veure'ns i d'entrada em va amollar aquesta frase. Bé, res a dir, això és bastant típic quan es comença un projecte. El client no sap massa bé què vol, i la meva feina sovint consisteix en anar fer preguntes, clarificant el que el client vol. Al final, després d'hores de conversa vaig definint el que podria ser el projecte: gran, molt gran. El client resulta que nos sap què vol, però sap que ho vol tot!
Paralant parlant ens diu que ja té un altre pressupost. Diu que ens ho enviarà per a que li poguem pressupostar el mateix. Fantàstic, al manco hi ha un punt de partida. Al dia següent em pos a repassar les notes i començar a fer el pressupost. Quan m'arriba l'e-mail del client jo ja duc com a 8 fulles del pressupost, explicant què tindrà el programa d'acord amb les especificacions del client.
El pressupost que reb em deixa de pedra. Es tracta de dues fulles ròniques que sols inclouen un llista de manteniments, sense entrar en cap tipus de detall. Ja me fa mala espina que en la maraca d'aigua de la fulla hi ha un disquet (are you from the past?), doncs pareix que sí. Però el que em sobta més és el preu que hi han posat, com a deu vegades menys que el que a mi m'està sortint. Li faig una ullada, no s'incluen caps de les característiques que fan més complexa l'aplicació, pareix un conjunt de planes estàtiques amb un editor html al darrera.
Faig una ullada a la web de l'empresa i veig la feina que han fet. En aquests casos sempre pens que potser sóc jo qui m'he errat. També confirm amb el client que el que vol és realment el que m'ha demanat i no simples planes estàtiques o picades a un cms.
La web de l'empresa és per sucar-hi pa. Webs de fa un any o dos que estan fetes fent servir cgis. La plana 404 de les aplicacions apunta al proveïdor del hosting, que obviament es un d'aquests superpoblats, que aprofiten el darrer cicle de CPU per posar-hi usuaris. Anam a veure una de les webs i li coment a Juan lo del cgi i les petades. Ell a més me comenta que ha canviat un paràmetre de la url i li ha sorti un dump de base de dades. Aquesta gent no ha sentit mai parlar d'injecció de codi ni llegit Exploits of a Mom. Som bona gent i el seus clients no es mereixen tanta incompetència, així que ho deixam anar.
Ara ja tenim un problema important, que és el factor psicològic de la fixació de preu. Aquesta gent ha fet un pressupost sense tenir la mínima idea d'on s'estava ficant i del que realment volia el client i ha donat un preu fruit de la seva inexperiència. Ara quan el client vegi el que jo li presentaré es pensarà que li estic prenent el pèl encara que és just al contrari.
Primera conclusió: els clients en general estan molt verds a l'hora de demanar un programa. Quan un demana una casa s'informa del constructor, demana el que ha fet, en quin tipus de projectes ha participat, demana a les amistats i s'assegura que les cases que ha fet no han caigut. En el desenvolupament sols pareix que es fixa amb el color de la façana. Com a informàtics ens fa falta fer molta feina de pedagogia, d'ensenyar als nostres clients a comparar, a saber que hi ha feines més i menys complexes, a poder destriar amb qui se la juga.
El meu cap diu que té un conegut que ...
Aquesta també és una altra història en línia amb l'anterior. Faig un pressupost força ajustat, el projecte m'interessa, pot ser divertit. El client vol donar el pas cap el negocio online. Vol canviar la seva web actual potenciant-la i afegint-li venda on-line. Es preveuen cents de milers de planes servides al dia i la web hauria de funcionar 24x7 pràcticament.
Tot està tancat, els tècnics del clients entenen el que farem hi saben que ho podem fer bé. Han vist el tipus de feina que fem i s'ha generat aquella confiança que t'anima a col·laborar i a entendre el projecte com si fos teu. Però al darrer moment reb una telefonada: el meu cap ha decidit que ho farà algú que ell coneix. Deman per la tecnologia: punt net, amb asp, internet information server, Windows 2003 i Microsoft Sql 2008. Ni en els meus pitjors malsons recomanaria una tecnologia així pel projecte del client. A més jo li havia oferit un servidor dedicat, és el mínim per anar tranquils amb el tipus de dades que es tractaran i el tràfic previst.
Faig una ullada al mercat nacional de servidors. El fiera del Information Server els hi ha dit que molt millor un servidor nacional per millorar el posicionament. Obviament no ha tingut en compte que això és un factor de tercer ordre, i que si primer la plana no va prou ràpida i té prou ample de banda, ja no hi ha res a fer. De totes maneres faig una ullada al mercat nacional, a veure què hi ha amb aquestes configuracions. Vaig a parar a Arsys, que per 605 eur/mes t'ofereix una plataforma així pel`tot just 605 eur/mes amb llicència sql server express edition, si vols la "bona" són 350 €/mes.
Jo els estava oferint si també un servidor dedicat, amb el doble de prestacions que Arsys i també gestionat per nosaltres per 200 €/mes. Em diuen que la gent que els fa la web els hi han dit que el hosting serà de 60 eur/mes. Per aquest preu i a Espanya em temo que serà un hosting compartit, potser del proveïdor, amb el més nou de la versió patapalo-edition de Microsoft.
És una llàstima, la gent amb la que he tractat em cau molt bé i són els primers que no entenen la decisió. Sospit que pot ser un projecte molt problemàtic i que els acabarà costant sang i llàgrimes. Tant de bo no els costi l'empresa.
Segona conclusió Senyors directius, convindria que de tant en tant i en questions tècniques es fes cas als tècnics, que amb les coses de menjar no es juga.
Tercera conclusió Com el el cas anterior hi ha molt de risc de que el projecte fracassi i el client surti escaldat. Part de la responsabilitat és del client, que hauria de saber què compra, però també hi ha una gran responsabilitat del proveïdor, que està enganant al client.
Pero mira que som frikis
Aquesta setmana també m'han demanat un pressupost per a una connexió amb un servei web. Llegint la documentació veig que el WSDL (argh!) sols està certificat que funcioni (que és el mateix que dir que sols funcionarà) amb Java i .Net. Això se diu fer coses estàndard, sí senyor. És un servei complex i delicat, així que abans de pressupostar res convé fer un petit prototip. Li aplic suds, el client SOAP que feim servir habitualment per Python, i les sospites es confirmen, no és capaç de consumir el WSDL i transformar-lo amb Python. Odio els WSDL la S se suposa que és de Simple, no? Han conseguit fer un protocol infumable i que gairebé sols ho pot consumir la mateixa llibreria que l'ha creat.
Però bé, l'interessant de tenir una capça d'eines farcida és que hi ha alternatives. Feim un poc de brainstroming amb Juan. La primera opció és modificar el WSDL per a que el mapejador s'ho mengi, o bé anar donant ajudes a la llibreria. És una opció que no m'agrada, ja que si hi ha problemes el proveïdor del servei se'n rentarà les mans, fins i tot si la culpa és seva.
Una possibilitat seria fer el projecte an Java, però això significaria un cost molt més alt per al client, i sobretot una manca de flexibilitat a l'hora de fer modificacions, ja que el servei sols és una petita part del projecte. Python i Django ens permeten tenir un temps de resposta molt bo davant canvis i això és fonamental pel tipus d'aplicacions que fem.
L'altra possibilitat és fer un servei Java/J2EE que faci de proxy cap a l'aplicació Python. Amb un protocol de comunicació compartit com xml, json, yalm o un binari com el de Google la cosa pot funcionar. En Juan suggereix fer una ullada a jython, que ell li va fer una ullada i pintava molt bé. Li fem una ullada, el projecte està mantingut i és compatible amb Python 2.5, que ja ens va prou bé.
Fem el primer prototip. Cridam a la libria Java des de jython i fem la cridada al servei. Funciona a la primera. I això provant des de la consola de línia de comandaments de jython. Ja tenim part del problema arreglat, però encara no ens satisfà del tot. En nexe d'unió ha de ser net i jython, com aplicació Java que és necessita un temps considerable per iniciar-se.
Però vet aquí que Celery, el sisteme de coes de Python del qual ja us n'he parlat, resulta que soporta Jython. Podem crear el mapeig de la llibreria amb jython i fer que les peticions sian bé síncorones o asíncrones, però que s'executin com a una tasca Celery. Com que la petició es farà dins un worker i aquest està sempre aixecat, no tindrem problemes de temps d'inici una vegada pugi l'aplicació. Es monta el prototip amb Celery, Redis, Python i Jython, en Juan ho està disfrutant i jo també.
Ara puc fer el pressupost tranquil. Sé que el que podria representar el risc més gran ja no representa el problema, hem descober el boogeyman del projecte (l'home del sac) i l'hem exposat a la llum.
No sé si el projecte es farà, però tenc la conciència tranquila de que aquesta és la manera de fer les coses. A l'hora de fer un pressupost per a un client no es tracta sols de pegar una pedrada i a veure si hi ha sort, sinó en estudiar el projecte i veure'n els riscs que hi pugui haver. Per això sovint sóc un perepunyetes demanant informació abans de fer un pressupost. Sé perfectament que en aquesta fase tot pressupost té un marge d'error gran i que hi haurà variacions en el projecte, però crec que no és professional tirar-se a la piscina a l'hora de presentar un pressupost si hi ha un punt crític que no es té clar com es farà. Preferesc invertir uns dies de feina més (amb risc que el pressupost no surti i perdre la feina) que exposar-nos a nosaltres, al projecte i sobretot al client als maldecapts d'un projecte la viabilitat tècnica del qual no s'ha pensat a l'hora de fer el pressupost.
Ho deix aquí, que aìxò s'ha fet molt llarg. Em deixo parlar de coses igualment divertides, com la telefonia IP que estam posant a l'oficina amb Asterisk i el bé que se sent amb els mòbils Android, o la potència de Bacula per configurar les còpies de seguretat. Potser un altre dia ...
Traducciones/Translations by apertium
2 comentaris, 0 trackbacks (URL) , Tags: Informàtica Python Django Java Gestió de projectes Codi lliure Linux APSL
Generar arxius xls amb Python III
Escrit per Aaloy a 29 de October , 2011 a les 11:28 a.m.
En aquesta tercera entrega veurem com podem fer que des de la nostra aplicació Django es puguin generar i servir els arxius que hem generat amb la llibreria xlwt.
La mecànica és la mateixa que la que hi ha als tutorials de Django
que mostren com servir un arxiu csv o un pdf, així que per a que
serveixi d'alguna cosa més us mostraré como ho feim utilitzant les
generic class views incorporades a Django 1.3.
El que volem fer és poder mostrar els resultats per pantalla i després poder afegir un link que ens retorni les dades que tenim en pantalla en forma d'arixu descarregable.
ListView
Per el nostre propòsit farem servir la classe ListView que es troba
a django.views.generic
La utilització més típica d'aquesta classes implica sobreescriure el
mètode get_query_set de tal manera que ens retorni les dades que
mostrarem a la plantilla, i, com no, indicar-li el nom de la plantilla
a la qual s'han de mostrar els resultats.
El codi seria quelcom semblant a això per la part de views.py
class TestView(ListView):
"""Classe de proves per l'article"""
template_name='tests/user_list.html'
def get_queryset(self):
"""Listam tots els usuaris que no són superusuaris"""
return User.objects.filter(is_superuser=False)
a l'arxiu urls.py crearem la url que apunti a la nostra classe,
afegint
url(r'^test/$', TestView.as_view(), name='trespams-test'),
i important TestView des del mòdul views.py
la plantilla
{% extends "base.html" %}
{% block main %}
<table>
<a href="{% url trespams-test %}?exporta=1">Exporta</a>
{% for user in object_list %}
<tr>
<td>{{user.username}}</td><td>{{user.email}}</td>
</tr>
{% endfor %}
</table>
{% endblock %}
A la plantilla com podeu veure hi ha un object_list que no hem
definit enlloc. Això ho fa la pròpia classe, que passa tot el que ve
del get_querset al contexte de la plantilla dins una variable
anomenada object_list. Podem canviar aquest nom si no ens agrada.
<a href="{% url trespams-test %}?exporta=1">Exporta</a>
Hem definit un enllaç que apunta a la mateixa url i per tant a la matea vista, però afefint-hi un paràmetre que ens indicará que el que volem no és tornar a generar la plana sinó exportar el fitxer.
Si creau una aplicació de proves i provau el codi veureu que encara no fa res, tranquils, ara hi anam...
Canviam la renderització
El gran avantatges de les class views és que estan construïdes de
manera molt modular, de manera que podem sobreescriure tot allò que
convingui per a modificar-ne el comportament.
En el nostre cas volem que es renderitzi un contingut o un altre
segons tinguem o no un paràmetre concret a la url, i per axiò el que
farem serà sobreescriure el mètode render_to_response
class TestView(ListView):
template_name='tests/user_list.html'
def get_queryset(self):
return User.objects.filter(is_superuser=False)
def render_to_response(self, context, **response_kwargs):
if self.request.GET.get('exporta'):
return self.render_to_fitxer(context, **response_kwargs)
else:
return super(TestView, self).render_to_response(context,
**response_kwargs)
El que deim és, si hi ha el paràmetre exporta al GET llavors fas un
render_to_fitxer (que encara no hem definit), en cas contrari, fas
el que es suposava que havies de fer.
Definim el render_to_fitxer
Ja sols ens queda definir el render_to_fitxer per fer que enlloc de
l'HTML obtinguem un arxiu
class TestView(ListView):
template_name='tests/user_list.html'
def get_queryset(self):
return User.objects.filter(is_superuser=False)
def render_to_fitxer(self, context, **kwargs):
"Sortida cap a un fitxer xls"
import xlwt
from django import http
response = http.HttpResponse(
content_type='application/vnd.ms-excel; charset=utf-8',
**kwargs)
response['Content-Disposition'] = 'attachment; filename=usuaris.xls'
usuaris = self.get_queryset()
wb = xlwt.Workbook()
ws = wb.add_sheet('usuaris')
fila = 0
for usuari in usuaris:
ws.write(fila, 0, usuari.username)
ws.write(fila, 1, usuari.email)
fila += 1
wb.save(response)
return response
def render_to_response(self, context, **response_kwargs):
if self.request.GET.get('exporta'):
return self.render_to_fitxer(context, **response_kwargs)
else:
return super(TestView, self).render_to_response(context,
**response_kwargs)
Els imports que hi ha al mètode render_to_fitxer poden estar a la
capçalera del mòdul perfectament, els he posat aquí per mostrar
explícitament els mòduls que es necessiten dins el mètode.
De la resta de codi, fixau-vos com podem modificar les capçaleres de
resposta per indicar que el que volem és generar un arxiu el
content_type del qual és de tipus excel i que ho tornarem en format
utf-8.
A més al Content-Disposition hem indicat que el contingut es
servirà com a un adjunt i amb un nom específic.
Per a la generació de les dades hem reaprofitat get_querset,
tanmateix el que volem és el mateix que hi ha en pantalla. Es pot
arribar a optimitzar per posar el contingut dins una caché i evitar
que es torni a repetir la consulta, però això és una optimitzaició
prematura.
Finalment, cal notar que no es genera un arxiu temporal, sinó que la
resposta, que tenim dins la variable response és comporta ella
mateixa com si fos un fitxer. És la màgia del duck typing.
I això és tot! Fins la propera!
Traducciones/Translations by apertium
0 comentaris, 0 trackbacks (URL) , Tags: Python Django
Generar arxius xls amb Python II
Escrit per Aaloy a 20 de October , 2011 a les 9:27 p.m.
A l'article anterior havíem fet un petit "hello world" en format xls, ara toca fer alguna cosa més de profit, així que veurem com podem generar un full de càlcul a partir de dades.
En altres temps, quan donava formació ofimàtica, un dels exercicis que feia fer als alumnes quan tocava el tema de les fulles de càlcul era el de fer una taula de multiplicar, serveix per perdre la por a la cosa aquesta dels nombres i mostra els conceptes fonamentals, ara podem fer el mateix. Per no fer-ho molt complicat anem per la taula del dos
#!/usr/bin/env python
#-*- coding: UTF-8 -*-
import xlwt
fmt = xlwt.easyxf
h1 = fmt('font: name Arial, height 200, bold on;')
wbk = xlwt.Workbook()
full = wbk.add_sheet('Taula del 2')
full.write(0, 0, "Taula del 2", h1)
for x in range(1, 11):
full.write(x, 0, 2)
full.write(x, 1, "x")
full.write(x, 2, x)
full.write(x, 3, "=")
full.write(x, 4, xlwt.Formula('A%s*C%s' % (x+1, x+1)))
wbk.save('taula.xls')
Podem veure com hem creat fàcilment una fórmula per a fer la multiplicació. També podem veure com per posar les fórmules hem de tenir en compte que s'ha de fer en la notació de files i columnes pròpia de les fulles de càlcul, és a dir, les columnes s'anomenen a partir de la lletra A. Això xoca un tant amb tot el maneig de la llibreria que utilitza una notació de coordenades o la l'origen està en la cel·la superior esquerra.
Per tractar amb això la llibreria proporciona un mòdul anomenat Utils que ens proporciona les eines per fer les transformacions entre la notació de fulla de càlcul i la notació de coordenades de la llibreria.
#!/usr/bin/env python
#-*- coding: UTF-8 -*-
import xlwt
fmt = xlwt.easyxf
h1 = fmt('font: name Arial, height 200, bold on;')
wbk = xlwt.Workbook()
full = wbk.add_sheet('Taula del 2')
full.write(0, 0, "Taula del 2", h1)
for x in range(1, 11):
full.write(x, 0, 2)
full.write(x, 1, "x")
full.write(x, 2, x)
full.write(x, 3, "=")
num = xlwt.Utils.rowcol_to_cell(x, 0)
taula = xlwt.Utils.rowcol_to_cell(x, 2)
formula = xlwt.Formula("%s*%s" % (num, taula))
full.write(x, 4, formula)
wbk.save('taula.xls')
Formats
Quan tractam amb informació és important a més de presentar-la, fer-ho en un format entenidor. Els fulls de càlcul ho fan prou bé a això, i des de fa molt temps tenen la capacitat de presentar-nos les dates en formats legibles i no en el seu format intern, i les quantitats numèrics ben formatejades, per exemple.
Amb la llibreria xlwt podem utilitzar els formats de cel·la de la
fulla de càlcul, podeu consultar-los anant a les propietats d'una
cel·la o bé fer una ullada a l'exmemple num_formats.py que ve amb
la llibreria.
#!/usr/bin/env python
#-*- coding: UTF-8 -*-
import xlwt
import decimal
fmt = xlwt.easyxf
wbk = xlwt.Workbook()
full = wbk.add_sheet('Caixa')
moneda = fmt('font: name Times' ,
num_format_str=u'#,#0.#0 "€";[Red]-#,#0.#0 "€";')
full.write(0, 0, decimal.Decimal("11133"), moneda)
full.write(0, 1, -3939.93, moneda)
wbk.save('nombre.xls')
A l'exemple podem veure com xlwt tracta perfectament tant el format numèric sencer (o float) com el Decimal.
De la mateixa manera podem fer feina amb diferents formats de dates o formats de temps.
#!/usr/bin/env python
#-*- coding: UTF-8 -*-
import xlwt
import datetime
fmt = xlwt.easyxf
wbk = xlwt.Workbook()
full = wbk.add_sheet('Avui')
data = fmt(num_format_str=u'D MMM YYYY')
full.write(0, 0, datetime.date.today(), data)
wbk.save('dates.xls')
Ho deix aquí per avui, a la propera entrega veurem com passar tot això la web i fer que Django ens doni un petit informe creat amb xlwt amb les dades del model.
Traducciones/Translations by apertium
0 comentaris, 0 trackbacks (URL) , Tags: Python Django
Generar arxius xls amb Python I
Escrit per Aaloy a 17 de October , 2011 a les 9:25 p.m.
Una de les coses que volia que l'aplicació de gestió de comunitat de propietaris que fem és la de tenir algun tipus de via per a poder exportar la informació introduïda. D'aquesta manera el client no està captiu i les seves dades són realment seves, però a més també volia que aquesta informació es presentàs de manera que també pogués ser útil.
El format csv està prou bé, però és massa simple a l'hora de presentar les dades. Necessitava quelcom del format del qual fora prou potent per poder aplicar estils i format bàsic, i al mateix temps que no implicàs una despesa addicional per al client.
Cercant, cercant vaig arribar a la llibreria xlwt. Aquesta llibreria permet crear fitxers en format xls (Excel) d'una manera prou senzilla i ràpida com per a ser just el que necessitàvem. El format es pot llegir tant mitjançant aplicacions privatives, que de d'aquí no recomanaré, o des d'aplicacions lliures com l'OpenOffice o LibreOffice.
Per a qui vulgui saber-ho tot podeu fer una ullada al codi font, però si us conformau amb un poquet menys, podeu llegir-vos el tutorial. Si voleu anar per feina podeu seguir llegint i us ne faré cinc cèntims de la llibreria.
Hello world
El primer que hem de fer és instal·lar la llibreria. Així que si no heu instal·lat pip ja tardau i després no deixeu de fer el mateix amb el virtualenv i el virtualenvwrapper, ja que sempre, sempre, farem feina des d'un entorn virtual.
Així doncs:
mkvirtualenv fc
workon fc
pip install xlwt
El que farem serà crear un full de càlcul amb les paraules "Hello world" a la primera cel·la (A1)
#!/usr/bin/env python
#-*- coding: UTF-8 -*-
import xlwt
wbk = xlwt.Workbook()
full = wbk.add_sheet('full 1')
full.write(0,0,'Hello world')
wbk.save('hello.xls')
Ja està! Hem importat la llibreria, creat el llibre, afegit un full que hem anomenat "full 1" i segidament escreit a aquest full la frase mítica.
Podem afegir tans fulls com volguem sense problemes, però ens hem de fixar bé que la cel·la A1 correspon a les coordenades zero, zero del full de càlcul.
Un poc de format
Podem aplicar estils a una cel·la fent servir dos mètodes: podem utilitzar la classe XFStyle que ens permet crear l'estil dessitjat pas a pas, o bé aplicar un format molt més legible pels humans i molt més proper al que seria un full d'estil css: easyxf
En aquest article faré servir aquest darrer mètode, ja que està prou ben documentat i com us dic, és molt més legible. Així, el que faré serà fer que el nostre "Hello world" es present un poc més vistós.
#!/usr/bin/env python
#-*- coding: UTF-8 -*-
import xlwt
fmt = xlwt.easyxf
h1 = fmt('font: name Arial, height 400, bold on;')
wbk = xlwt.Workbook()
full = wbk.add_sheet('full 1')
full.row(0).height = 800
full.write(0,0,'Hello world', h1)
wbk.save('hello2.xls')
He creat un estil anomenat h1 amb font Arial i negreta. L'alçada l'he posada com a 400, és a dir 20px. Com a base preneu que un tamany de font 200 equival a una alçada de 10px.
Com que volem que la fila tengui més alçada que la font, hem
establert la propietat height de la fila a 800.
Això és tot per avui, al proper article (demàs segurament) ja un full complet, amb format numèric i alguna fórmula.
Traducciones/Translations by apertium
0 comentaris, 0 trackbacks (URL) , Tags: Python Django
Propietariosonline, la visió tècnica
Escrit per Aaloy a 08 de October , 2011 a les 11:01 a.m.
Aquesta setmana és obligat parlar del llançament de propietariosonline.com, una aplicació web per a la gestió de comunitats de propietaris que hem llançat des d'APSL. Junt amb la web de trobacasa forma el gruix dels projectes propis. Actualment estam en fase beta, és a dir, mirant de netejar tots els possibles errors que puguin haver passat els nostres testeigs inicials i sobre tot, copsant les opinions dels beta-testers, per tal d'anar incorporant els suggeriments de millora que ens facin. Personalment una de les coses que més em motiven a l'hora de fer un programa és que aquest sigui útil, i per això res millor que fer-lo en col·laboració amb els usuaris potencials del programa.
Com que supòs que no llegiu això per a que us parli de la gestió de les comunitats de propietaris, aniré entrant en matèria.
L'aplicació està desenvolupada amb Django en la seva versió 1.3. És la primera aplicació grossa en la que feim un ús intensiu de les generic class views. Com molts sabreu abans de la versió 1.3 la manera de passar del la petició (el request) cap a la sortida html era mitjançant una funció que es posava a l'arxiu views.py. Amb la versió 1.3 això també es pot fer, però hi ha la possibilitat de que aquestes funcions siguin classes.
Per una aplicació com propietariosonline això ha significat poder fer herència de classes i reutilitzar moltíssima funcionalitat. Ahir llegia un apunt on un usuari de Django es queixava que la quantitat de codi escrit utilitzant les classes era major que sols utilitzant les funcions. En casos puntuals potser veritat, però quan es pot fer ús de l'herència, la reutilització de codi i sobretot l'estructuració que tens compensa de sobres tenir que aprendre una nova manera de fer les coses.
Com es tracta d'un projecte propi, hem aprofitat per fer experiments i provar noves utilitats i llibreries. Ja se sap, els experiments a casa i amb gasosa, així que res millor que un projecte com aquest. Es prou gran com que l'experiència sigui extrapolable a altres projectes i no hi ha la pressió externa que representa un client que està pagant per hores.
Una de les llibreries que hem utilitzat és django-tables2 que ha significat passar de crear les taules de dades amb html a utilitzar codi Python per a enllaçar estructura i visualització. A l'aplicació es fan servir molts llistats tabulats i aquesta utilitat ens ha permès reutilitzar estructures de taula, definir formats de columnes i sobretot estalviar-nos una gran quantitat de codi HTML. De retruc les modificacions també són més senzilles, ja que per exemple modificar com apareixen les quantitat numèriques és tant senzill com modificar un tipus de columna que hem definit amb Python. Per exemple, per posar un check hem definit una columna com:
class BooleanColumn(Column):
def render(self, value):
valor = "on" if value else "off"
return mark_safe('<img src="%s/img/check-%s.png" />' \
% (settings.STATIC_URL, valor))
D'aquesta manera quan a un llistat es necessiti un camp booleà utilitzarem aquest tipus de columna. Si pel que sigui volem canviar com es presenta doncs no hem d'anar taula a taula a fer els canvis, sinó que podem anar directament al tipus de columna.
Una altra de les utilitat que hem fet servir es diu Sentry una utilitat que utilitza la gent de Quora per a monitoritzar els errors 500 i logs d'error. Fins ara aquests tipus d'errors els controlàvem amb els missatges d'e-mail que ens envia la pròpia aplicació de Django. El problema amb això és que no escala bé. L'altra dia de pagès ens trobàrem amb un problema on això es veu perfectament:
Un dels nostres clients està connectat amb una web amb molt tràfic que li envia peticions. Aquesta web va sofrir un atac i va començar a enviar peticions mal formades a tort i dret, i un dels afectats va ser el nostre client. Una de les màximes de Python és que les excepcions no ha de passar desapercebudes, així que personalment program de manera que si una cosa no ha de petar i peta, doncs me'n vull assabentar. Això va fer que rebéssim milers de missatges en poques hores. L'aplicació no es va veure afectada, però la bústia d'avisos feia goig! Al mateix temps un altra client va tenir una petada a l'aplicació, que també va enviar el corresponent e-mail. En condicions normals haguéssim vist l'error i l'haguéssim pogut solucionar en pocs minuts, però l'avís es va perdre entre els milers de missatges anteriors i no ens n'adonàrem fins passats un dia o dos. És veritat que es pot configurar el sistema de correu per a que distribueixi els missatges per client i per aplicació, però en condicions normals això no es tan còmode com tenir-ho tot centralitzat a un punt.
Aquí és on Sentry ens soluciona la vida. Hem configurat una aplicació amb Sentry que centralitza tots els missatges d'error. D'aquesta manera a la consola sols apareix el missatges i el nombre de vegades que s'ha produït l'error. Així encara que es produeixi una situació com la que explicava és molt més difícil que l'error passi desapercebut, ja que cada error idèntic s'agrupa dins Sentry. A més el format de visualització dels errors és fantàstic, molt semblant a com Django presenta els errors en mode depuració, la qual cosa fa que identificar el problema sigui encara més fàcil.
Propietariosonline va servir com a excusa i experiment, però a hores d'ara ja tenim el client de Sentry instal·lat al 90% de les aplicacions i la consola de Sentry com a una eina fonamental de monitorització, sols comparable en utilitat a Nagios en la monitorització de sistemes.
La resta d'utilitats que hem fet sevir ja són vells coneguts: django-nose per als tests unitaris, django-redis-cache, south, sorl-htumbnail, django-debug-toolbar, django-extensions, ipdb, robots etc. no s'han convertit en part fonamental de les nostres aplicacions.
Hores d'ara duim un mes just de dedicació al projecte. Fet i fet podem dir que hem dedicat dues persones a temps complet durant un mes. De fet és un poc menys, ja que hem fet manteniment d'altres projectes, però si sumam la feina de maquetació de la web principal i la feina de sistemes, doncs els resultat és si fa no fa aquest: 2 mesos-home de dedicació (encara que diferents perfils).
Posem-hi xifres! He utilitzat el programa cloc per comptar les línies de codi. He llevat la totalitat de codi javascript i css (i less), ja que fonamentalment hem utilitat llibreries jQuery i el bootstrap de twitter, el resultat és:
285 text files.
282 unique files.
1348 files ignored.
http://cloc.sourceforge.net v 1.53 T=1.0 s (248.0 files/s, 19589.0 lines/s)
-------------------------------------------------------------------------------
Language files blank comment code scale 3rd gen. equiv
-------------------------------------------------------------------------------
Python 147 1593 872 11995 x 4.20 = 50379.00
HTML 90 298 32 4088 x 1.90 = 7767.20
XML 11 7 0 704 x 1.90 = 1337.60
-------------------------------------------------------------------------------
SUM: 248 1898 904 16787 x 3.54 = 59483.80
-------------------------------------------------------------------------------
És interesant veure que aquesta eina (encara que agafat amb pinces) ens dóna també l'equivalent en línies de codi si haguéssim fet servir un llenguatge de programació menys potent que Python. Donat que el nombre de línies de codi que pot escriure un programador és constant, llavors això vol dir que el mateix programa d'haver-ho fet en un altra llenguatge ens hagués duit 4 vegades més de temps. Per pensar-hi!
A la part de sistemes hem aprofitat també per potenciar el Fabric per a tota la gestió i desplegament de l'aplicació, que corre amb uWSGI i ngnix. Això ens permet pujar modificacions a diari, mantenint el cicle d'entorns de desenvolupament, preproducció i producció.
Encara hi ha coses que es poden millorar (de fet sempre n'hi haurà). Quan l'economia ens ho permeti volem incorporar un servidor per a la integració contínua, però a poc a poc esperam anar arribant-hi. M'agrada pensar que hem aconseguit un procés de millora contínua, on a cada projecte aconseguim tenir les coses millor que al projecte anterior i anar incorporant el que hem après també a les aplicacions que ja hi ha en producció.
Si tot això tindrà viabilitat econòmica i ens permetrà sobreviure com a empresa el temps ho dirà. Però el que sempre he tingut el convenciment que conformar-se i no aspirar a millorar sols duu a l'empobriment espiritual i és tan perillós o més que el risc que corres intentant millorar.
Traducciones/Translations by apertium
6 comentaris, 0 trackbacks (URL) , Tags: Python APSL Django
Petit resum d'estiu
Escrit per Aaloy a 21 de September , 2011 a les 8:15 p.m.
Fa un grapat de setmanes que no escric res a aquest blog, tenc moltes coses pendents, però entre unes coses i altres el temps va passant i fins avui no he trobat una estona per a tornar-hi.
Han estat unes setmanes molt estranyes: problemes personals, alguns bons, com les noces de la germana i altres no tan bons, molta feina i com no, el començament de l'escola dels menuts i la festa de la Vermada que ja és aquí.
En el terreny de la feina tenc dues bones notícies: hem posat ja en producció un projecte que em fa una especial il·lusió txerpa i una web presencial fiscontrol.
txerpa és un projecte que hem desenvolupat per a una coneguda gestoria/assessoria de l'illa. Integra una web, un backoffice per a la gestió d'empreses via OpenERP i la integració amb el mateix OpenERP de manera que es poden crear molt fàcilment instàncies personalitzades per a poder dur una comptabilitat i gestió.
El projecte des del punt de vista tècnic ha estat molt engrescador. És un projecte amb un llarg recorregut en el qual esperam poder-hi afegint millores i nova funcionalitat. L'interessant, però, és veure que des d'aquestes illes nostres iniciatives com aquestes poden tirar endavant. Django i Python han permès que la integració dels diferents serveis que integren la plataforma es pogués fer d'una manera ràpida, mantenible i escalable.
Fiscontrol és un altre projecte que ha vist la llum aquesta setmana de manera oficial. En aquest cas és una web presencial, però ens ha fet il·lusió ja que és un projecte on el feeling amb el client ha estat molt bo i on les incorporacions del client han estat sempre per millorar el resultat final (ja sabeu que això no sempre passa). És de les poques webs on s'han demanat n idiomes i s'han posat continguts en aquests idiomes. En fi, que tot i que el projecte no té un component tècnic novetós, sí que ens hem divertit molt fent-la i veient els resultats.
Ja és la nostra tercera web d'assessoria, pareix que ens estam començant a especialitzar :)
Esper que en les properes setmanes hi haurà més novetats (com sempre en forma de projectes Django), ja que tenim un grapat de webs pendents de sortir.
Com sabeu els que us dedicau a la informàtica en el sector turístic, els mesos de temporada alta turística són mesos de temporada baixa en informàtica. He aprofitat aquest mig gas per engrescar a la gent d'APSL (o emmarronar-los, vet a saber) en un projecte propi. Permeteu-me de moment que us mantengui a l'espera de més notícis. L'interessant és que el projecte l'hem fet ja amb les noves generic class views de Django, incorporades a la versió 1.3.
En aquest projecte hem pogut veure la meravella de les class views, com tot el codi ens queda molt més compacte, com es pot aprofitar l'herència de vistes per escriure molt menys codi (sí, encara menys!).
De les class views en vull escriure un bon apunt un dia d'aquests. La documentació de Django que hi ha i els posts que he trobat sols fan referència al TemplateView, però la potència dels views de Forms, d'edició i borrat no s'han de menystenir. Així que es cosa de trobar-ne el temps per escriure l'apunt/tutorial i fer el codi d'exemple.
Per cert, que aquest blog ja corre damunt un altre servidor. Abans ho feia a un servidor dual core amb 2 Gb de RAM, però fa unes setmanes hem començat a migrar-ho tot a un servidor molt més potent, un i7 quadcore amb 12 Gb de RAM. Pingdom diu que la renderització ha passat de 4 segons a 1.5 segons. No he canviat el codi, sols és l'efecte d'un servidor que va més sobrat i d'una optimització de l'arquitectura: hem passat del WSGI de CherryPy que hi havia a l'inici al WSGI de uWSGI, de tenir la caché a memcached a tenir-la a Redis.
En Bernat ha fet molta feina deixant un entorn molt optimitzat per a les aplicacions Django. Tant que estam pensant en oferir un hosting gestionat per la gent que desenvolupi aplicacions Django i necessiti un entorn ben afinat. Temps al temps. El que tinc clar és que no vull que facem un projecte tipus Gondor, sinó quelcom on el valor afegit sigui la disponibilitat d'un tècnic de primer nivell com Bernat per a poder deixar ben fina l'aplicació.
Res, això és tot, i que no és poc.
Traducciones/Translations by apertium
6 comentaris, 0 trackbacks (URL) , Tags: Python Django
Redis vs Memcached [en]
Escrit per Aaloy a 05 de August , 2011 a les 6:17 p.m.
With some help of our friend "Google Translate" :)
As we all know that if you want a web application goes faster there is a secret cache as much as you can. Avoid to generate the page each time and search for the content in the database.
To archieve this the today standard is Memcached. Memcached allows us to scale our application a simple way. We can think it as a big hash table, written in C, very fast and with libraries to access to it in almost any language. But thre is e a new competitor in this kind of applications: Redis, and I've already talked about it in the Celery post.
Memcached is an application aimed at dealing with cache, Redis is a noSQL general purpose key-value database in a similar way as Memcached, but with possibilities that go far beyond a cache. Let's see a few differences:
-
We can not see the keys we have in Memcached. With Redis can do a search for keys, or see all the command KEYS *
-
Redis has persitence.
-
Memcached is limited to memory you allocate, Redis can also swapt to disk and just put the keys in memory.
-
In Memcached we have no true replication. Redis replication is real and configurable with a simple line.
-
Redis is quite configurable, we can define the page size of cache, store to disk, have a password protected database...
-
With Redis we can define as many databases as you want. We can clean all the keys in a database without affecting the others.
So the next step is to ask if we could use Redis instead of Memcached for our web applications. Redis plus Django could partially solve one of the biggest problems we have: cache invalidation. As we can have an independent database for each application or for each purpose, we can FLUSHDB the database our application to invalidate the whole cache, or just delete the keys with a single Redis command.
But in Science hypotheses have to be confirmed. So what I did was to build a little sandbox application to see how if Redis was as good as it seems.
The Sandbox
The machines availables to us for the experiment follows:
-
Dell Laptop Core 2 at 2:16 GH and 2 GB of RAM with Ubuntu 11:04 This is the Web application server and has the address 192.168.1.35
-
Virtual Server Ubuntu 10.04 with 512 MB of RAM on Virtualbox running on the laptop at 192.168.1.38. This server is 32 bit and will host Redis as Memcached.
-
Apple PPC 64 Dual Core with 3 GB of RAM, it will launch the tests.
The application is the one I created for creantbits. That is, to run it has to read BD for the last events and presents them in the page.
We will use two of Gunicorn workers to start the application, and we'll test the performance from the PPC with Apache Benchmark
ab -n 1000 -c 5 http://192.168.1.35:8000/
For each test case two consecutive tests are thrown and we discarded the first.
To test the Redis cache we have installed the application django-redis-cache
The test
First we have to determine the starting point. So what we did is to clear our application's cache and see how many requests we get.
no-cache: 120 req / s Mean: 41.4 ms
We set up cache site for Django configured as locmem. Locmem is not recommended in production environments as it can't share the cache, but as before, we used to set the starting point:
locmem: 1380 req / s Mean: 3.6 ms
We configure the the cache as memcached
memcached: 626 req / s Mean: 8.0 ms
Cache Redis configured with persistence to disk
Redis: 623 req / s Mean: 8.0 ms
Cache Redis without persistence. Is the nearest Memcached equivalent.
Redis: 632 req / s Mean: 7.8 ms
We install the hiredis package
Redis: 650 req / s Mean: 7.7 ms
Conclusions
I think the results speak for themselves. If we use persistence Redis is comparable in speed to Memcached, and for the same price we have a NoSQL database at our disposal.
If we don't need persistence Redis shows a 4% improvement over Memcached. This percentage is not significative, but at least we can see that Redis is in the same leage as Memcached.
For me Redis it too good to not use it!
Traducciones/Translations by apertium
4 comentaris, 0 trackbacks (URL) , Tags: Python Django
Redis vs Memcached
Escrit per Aaloy a 04 de August , 2011 a les 6:57 p.m.
Com tots sabem si un vol que una aplicació web vagi ràpida hi ha un secret: posar en caché tot el que puguem. Evitar tenir que fer càlculs i anar a la base de dades a cercar la informació.
Per fer això l'estàndard avui en dia és Memcached. Memcached ens permet escalar la nostra aplicació d'una manera molt senzilla. És una gran taula hash, del tipus clau valor, escrita en C, molt ràpida i amb llibreries d'accés en pràcticament qualsevol llenguatge.
Però en els darrers temps ha sortit un nou competidor dins les bases de dades de tipus hash, aquest competidor reb el nom de Redis, i ja us n'he parlat quan tractàvem el tema de Celery.
Així com Memcached és una aplicació orientada a tractar amb caché, Redis és una base de dades noSQL de propòsit general, amb format clau-valor com Memcached, però amb unes possibilitats que van molt més enllà de un simple magatzem de memòria. Anem a veure un parell de diferències:
-
No podem veure les claus que tenim dins Memcached. Amb redis podem fer una cerca per claus, o veure-les totes amb la comanda KEYS *
-
Podem configurar Redis per a que sigui persistent.
-
Memcached està limitat a la memòria que li assignem, Redis pot utilitzar també el disc i sols posarà en memòria les claus.
-
Memcached no té una vertadera replicació, encara que podem fer que hi hagi molts servidors Memcached. Redis té replicació real i configurable amb una simple línia.
-
Redis és força configurable, podem definir el tamany de pàgina de caché, quant guardarem a disc, si volem tenir la base de dades protegida, ...
-
Amb Redis podem definir tantes bases de dades com vulguem. Podem netejar totes les claus d'una base de dades sense afectar a les altres.
Amb tot això és lògic demanar-se si enlloc de Memcached no podríem fer servir Redis per a les nostres aplicacions web. Si ho ajuntam amb Django tenim resolt un dels problemes més grans, que és la invalidació de cachés. Separant cada una de les nostres aplicacions dins una base de dades, podem fer un FLUSHDB a la base de dades de la nostra aplicació per invalidar la caché, amb la seguretat que no afectarà a la resta.
Però a la ciència les hipòtesis s'han de confirmar. Així que el que he fet ha estat muntar un petit entorn de proves per veure com se comporta una aplicació senzilla.
L'entorn de proves
El maquinar de que disposam per l'experiment és el següent:
-
Laptop Dell Core 2 a 2.16 GH i 2 Gb de RAM amb Ubuntu 11.04 Aquest servidor té l'aplicació web, i té l'adreça 192.168.1.35
-
Servidor virtual Ubuntu 10.04 amb 512 Mb de RAM damunt Virtualbox, executant-se damunt el servidor anterior amb l'adreça 192.168.1.38. Aquest servidor és de 32 bits i tindrà tant Redis com Memcached.
-
Apple PPC 64 Dual Core amb 3 Gb de RAM, ens servirà com a màquina per a llançar els tests.
L'aplicació és la que vaig fer servir pel creantbits (http://creantbits.com). És a dir, fa un accés a BD per obtenir els darrers esdeveniment i presenta la plana.
Utilitzarem dos workers de Gunicorn per iniciar l'aplicació, i la testejarem des de el PPC amb l'Apache Benchmark
ab -n 1000 -c 5 http://192.168.1.35:8000/
Per a cada test es llancen dos tests ab consecutius i es descarta el primer. Es repeteix 2 pics i es fa la mitja, arodonint cap avall en nombre de peticions per segon.
Per la caché de redis es fa servir l'aplicació django-redis-cache instal·lada
des de PyPi.
Inici dels tests
Per començar hem de determinar el punt de partida. Així que el que farem serà desactivar la caché de la nostra aplicació i veure quantes peticions aconseguim.
no-cache : 120 req/s Mean: 41,4 ms
Activam la caché per site de Django i posam la caché a locmem. Locmem fa que la caché no pugui ser compartida entre processos i no és un opció recomanada per entorns de producció, però com abans, ens serveix per fixar el punt de partida:
locmem : 1380 req/s Mean: 3,6 ms
Posam la caché a memcached
memcached : 626 req/s Mean: 8,0 ms
Posam la caché a Redis configurada amb persistència a disc
Redis : 623 req/s Mean: 8.0 ms
Configuram Redis sense persistència. És l'equivalent a Memcached.
Redis : 632 req/s Mean: 7,8 ms
Utilitzam Redis amb el client hiredis
Redis : 650 req/s Mean: 7,7 ms
Conclusions
Crec que els resultats parlen per sí mateixos. Si volem persistència Redis és comparable en velocitat a Memcached, i a més pel mateix preu tenim una base de dades NoSQL en el nostre entorn i a la nostra disposició.
Si no volem persistència Redis supera per molt poc a Memcached i si aplicam totes les optimitzacions per tenir un entorn el més semblant possible a Memcached arribam a un 4% de millora. Aquest tant per cent no és significatiu, però si més no ens serveix per veure que Redis està al mateix nivell de rendiment que Memcached i ve amb tot el paquet d'opcions afegit.
Massa bo per no fer-ho servir!
Traducciones/Translations by apertium
8 comentaris, 0 trackbacks (URL) , Tags: Python Django
De la web al model
Escrit per Aaloy a 29 de July , 2011 a les 6:04 p.m.
L'scrapping
Ahir ja vàrem veure quin era el model de dades i el bé que va sorl per a manipular imatges, així com la utilització de la llibreria requests ens quedava veure una altra part important: com agafar el contingut de la web, parsejar-lo i obtenir-ne la informació que necessitam.
Per fer això hi ha diverses utilitats, algunes molt especialitzades com scrappy, i amb més solera és BeautifulSoup. Aquesta llibreria té la qualitat de ser molt permisiva amb l'HTML i hi ha poques planes que no pugui tractar d'una manera o altra.
La plana de Meneame té las notícies de portada dins un div anomenat
news-summari, així que el primer que farem serà carregar la plana
dins una instància de BeautifulSoup i cercar aquestes notícies.
page = requests.get('http://www.meneame.net')
if page.status_code != 200:
print "Ups! pareix que hi ha un petit problema"
return page.status_code
soup = BeautifulSoup(page.content)
noticies = soup.findAll('div', 'news-summary')amb això BS ens haurà donat tots els divs que tenen la classes 'news-summary' amb la qual cosa ja és sols cosa d'aplicar un tractament semblant per a obtenir la informació de cada notícia
for noticia in noticies:
titular = noticia.find('h1').text
texto = noticia.find('p').text
img = noticia.find('img', 'thumbnail')
if img:
src = img['src']
match_obj = compile_obj.search(src)
id = match_obj.group('id')
try:
NoticiasPortada.objects.get(identificador=id)
except NoticiasPortada.DoesNotExist:
print u"Tractant la noticia: %s" % id
noticia = NoticiasPortada(
identificador = id,
texto = texto,
titular = titular,
thumbnail = download_image(src,
'thumb-%s.jpg' % id)
)
noticia.save()El mètode find ens permet a accedir al primer tag que compleix la
condició i text ens en dona el contingut. Si com a segon paràmetre
hi possam una classe ens retornarà el primer element d'aquell tipus
que tengui la classe que li hem donat.
El problema ve quan volem identificar d'alguna manera les notícies. Volem guardar sols les que tenen una imatge. Podem veure que Meneame genera el thumbnail de la notícia amb l'identificador de la mateixa, així que podem fer us d'una expressió regular:
rawstr = r"""(?P<id>\d+).jpg$""" compile_obj = re.compile(rawstr)
així
src = img['src']
match_obj = compile_obj.search(src)
id = match_obj.group('id')ens donarà l'identificar de la notícia a partir de la informació de la url de la imatge. Hi ha una utilitat fantàstica per a la depuració i testeig d'expressions regulars anomenada Kodos, no us la podeu perdre.
El toc final
En una aplicació com aquesta la CPU està molt de temps sense fer res,
esperant que li arribi la informació. És un bon candidat per a que
l'aplicació faci ús dels Threads o del multiprocés. Una de le
maneres més senzilles de fer-ho és fent servir la llibreria Queue,
d'aquesta manera sols hem de definir quants Threads farem servir en el
processament i que aquests consumeixin el contingut (cada notícia) de
la cua.
Així el codi final quedaría si fa no fa:
import re
import cStringIO
import requests
from Queue import Queue
from threading import Thread
from BeautifulSoup import BeautifulSoup
from django.core.management.base import BaseCommand
from django.core.files.uploadedfile import SimpleUploadedFile
from t1.models import NoticiasPortada
rawstr = r"""(?P<id>\d+).jpg$"""
compile_obj = re.compile(rawstr)
class Command(BaseCommand):
"""Divertimento. Permet posar les notícies amb foto
de meneame dins una BD.
"""
def handle(self, *args, **options):
page = requests.get('http://www.meneame.net')
if page.status_code != 200:
print "Ups! Pareix que tenim un problema"
return page.status_code
#cream les coes
self.q = Queue()
for i in range(5):
t = Thread(target = self._importar_portada)
t.daemon = True
t.start()
soup = BeautifulSoup(page.content)
noticies = soup.findAll('div', 'news-summary')
for noticia in noticies:
self.q.put(noticia)
self.q.join()
def download_image(self, img_url, filename):
r = requests.get(img_url)
if r.status_code == 200:
return SimpleUploadedFile(content=r.content,
name=filename,
content_type=r.headers.get('content-type'))
else:
return None
def _importar_portada(self):
while True:
noticia = self.q.get()
titular = noticia.find('h1').text
texto = noticia.find('p').text
img = noticia.find('img', 'thumbnail')
if img:
src = img['src']
match_obj = compile_obj.search(src)
id = match_obj.group('id')
try:
NoticiasPortada.objects.get(identificador=id)
except NoticiasPortada.DoesNotExist:
print u"Processant la notícia: %s" % id
noticia = NoticiasPortada(
identificador = id,
texto = texto,
titular = titular,
thumbnail = self.download_image(src,
'thumb-%s.jpg' % id)
)
noticia.save()
self.q.task_done()I això és tot, com sempre amb Python l'explicació sol ser molt més llarga que el codi a executar, fins i tot amb els fils.
Esperant que us hagi agradat el divertimento.
Traducciones/Translations by apertium
0 comentaris, 0 trackbacks (URL) , Tags: Python Django
Imatges de la web al model Django
Escrit per Aaloy a 28 de July , 2011 a les 5:42 p.m.
L'altra dia estava fent una aplicació web part de la qual consisteix en aprofitar els continguts de la web anterior, continguts als que no tenim accés directe.
La part de text va ser senzilla, però obtenir una imatge de la web i associar-la a un model Django va resultar una mica més interessant del que suposava. Tant que vaig pensar que potser convenia posar-ho en forma d'apunt.
Per posar-ho en context vaig començar a fer una aplicació Django, la qual tenia que obtenir la imatge i guardar-la, però ja que hi era, vaig voler fer quelcom més educatiu, així que l'aplicació es va anant transformant amb codi per a descarregar-se les fotografies associades a la plana principal de meneame. Si provau el codi mirau de canviar la url que ja que amablement Ricardo em va dir que no hi havia problema en fer algunes proves, tampoc és cosa que anem putejant la web.
Així doncs, aquest apunt serà un poc mescladissa d'utilitats, d' screen scrapping, de thread, processos i altres herbes. Miraré d'anar a poc a poc, però ja us aviso que hi ha de tot i molt!
Definim el model
El més habitual quan hom tracta amb imatges és tenir-ne que fer algun tipus de conversió, si més no per presentar-les a l'administrador de Django. És tan habitual que quan he de fer servir un camp ImageField de Django ja ho converteixo directament a un camp ImageField d'una llibreria que va ideal per a la manipulació d'imagtes sorl.thumbnail.
Així doncs, el nostre model serà molt senzill, tindrà un identificador per poder distingir les notícies, el titular, el texte de la notícia i com no la imatge.
M'ha queda talment així:
!/usr/bin/env python
# -*- coding: UTF-8 -*-
from django.db import models
from sorl.thumbnail import ImageField
from sorl.thumbnail import get_thumbnail
class NoticiasPortada(models.Model):
"""Base de datos de noticias"""
def get_upload_path(instance, filename):
return "fotos/%s" % filename
identificador = models.IntegerField()
titular = models.CharField(max_length=200)
texto = models.TextField()
thumbnail = ImageField(upload_to=get_upload_path,
blank=True, null=True)
def img(self):
if self.thumbnail:
try:
im = get_thumbnail(self.thumbnail,
'50x50', crop='center', quality=80)
return '<img src="%s">' % im.url
except IOError:
return "error"
else:
return "%s" % self.identificador
img.allow_tags = True
img.short_description = 'img'
def __unicode__(self):
return self.titularCom es pot veure Sorl incorpora una sèrie d'utilitats per a la conversió d'imatges que ens van molt bé. Així podem definir el mètode img dins el model per tal de poder-ne fer referència a l'administrador de d'Django.
Fixem-nos també com li podem passar un mètode per tal de poder calcular a quin lloc es deixarà la imatge. Aquest mètode ha de tenir la signatura nomfuncio(instància, nom) on instància és la instància de l'objecte al qual s'associarà la imatge i el nom és el nom de la imatge en sí. Això és molt útil per poder deixar les imatges a diferents carpetes segons convingui.
A l'admin.py podem fer
from django.contrib import admin
from models import NoticiasPortada
class NoticiasPortadaAdmin(admin.ModelAdmin):
list_display = ('img', 'identificador', 'titular')
search_fields = ('titular', 'texto')
admin.site.register(NoticiasPortada, NoticiasPortadaAdmin)Sorl té opcions per a que al formulari se'ns presenti també la imatge, però per ara ho deixarem així.
La càrrega de les imatges
La càrrega de les imatges la podríem haver fet de moltes maners, però com que ja vaig posar un article de com fer comandes de Django, doncs ho aprofitaré. Crearem una comanda anomenada importar que es podrà cridar com
python manage.py importar
Per això s'ha de crear un paquet Python dins l'aplicació amb l'estructura
app
models.py
management
__init__.py
commands
__init__.py
importar.pySi feis servir django-extensions recordau que hi ha una comanda anomenada create_command que crea directament aquesta estructura.
Per a importar les imatges utilitzarem la classe InMemoryUpladedFile
que es troba a django.core.files.uploadedfile. Podem passar una
instància d'aquesta classe al nostre model dins l'atribut thumbnail i
Django se n'encarregarà de la resta. La complexitat addicional està
en que ens hem de davallar la imatge de la web.
def download_photo(self, img_url, filename, field_name):
img = cStringIO.StringIO()
image_on_web = urllib.urlopen(img_url)
while True:
buf = image_on_web.read(65536)
if len(buf) == 0:
break
img.write(buf)
image_on_web.close()
return InMemoryUploadedFile(file=img,
field_name= field_name, name=filename,
content_type="image/jpeg", size=img.tell(), charset=None)Per davallar-nos la imatge farem servir la llibreria urllib.
Passant-li una url es capaç de llegir-la i donar-nos el contingut. Com
que la memòria de moment no és un requeriment, el que farem serà
deixar el contingut dins un buffer que es comporta a tots els efectes
com un fitxer. Feis una ullada al duck typing per saber com és això possible.
InMemoryUploadedFile espera un fitxer (d'aquí el truc de fer servir StringIO), el nom del fitxer, el content_type, el tamany i el charset, així com el nom del camp.
De fet, però ho podem fer una mica més senzill, ja que no necessitam
el nom del camp. Django té una altra classe que fa el cid més
senzill, és el SimpleUploadedFile de manera que el codi queda un
poc més net:
def download_photo_simple(self, img_url, filename):
img = cStringIO.StringIO()
image_on_web = urllib.urlopen(img_url)
while True:
buf = image_on_web.read(65536)
if len(buf) == 0:
break
img.write(buf)
image_on_web.close()
return SimpleUploadedFile(content=img.getvalue(),
name=filename,
content_type="image/jpeg")Però encara així és molta línea per a tan poca cosa. Em feia ganes provar una llibreria que promet simplificar el procés d'accés als recursos web. La llibreria requests. Teniu en compte que el codi amb urllib encara es complicaria més en una aplicació real, ja que convé controlar les excepcions que hi pot haver. El tema està força ben explicat a l'apunt urllib2 - The Missing Manual, així que no m'estendré més.
L'avantatge de requests és que ens permet abstreure'ns d'aquests
excepcions i tractar únicament amb codis d'estat web. Així el que
ens interessarà és obtenir la imatge si el codi és 200 (tot bé) o
retornar un None si hi ha problemes. Així el codi es simplifica
encara més.
def download_more_simple(self, img_url, filename):
r = requests.get(img_url)
if r.status_code == 200:
return SimpleUploadedFile(content=r.content,
name=filename,
content_type=r.headers.get('content-type'))
else:
return NoneAmb això, crear una instància de NoticiasPortada pot quedar com
noticia = NoticiasPortada(
identificador = id,
texto = texto,
titular = titular,
thumbnail = self.download_more_simple(src, 'thumb-%s.jpg' % id)
)
noticia.save()on id és l'identificador de la notícia i src representa la url de la imatge.
Com ho treim a això? Doncs és una bona pregunta. A la segona part de l'article us ho explico. Fins demà!
Traducciones/Translations by apertium
0 comentaris, 0 trackbacks (URL) , Tags: Python Django
Creantbits un 15 de juliol
Escrit per Aaloy a 16 de July , 2011 a les 11 a.m.
Ahir divendres 15 hi hagué una nova edició del creantbits. Aquesta vegada enlloc de que la inscripció es fes en un comentari al blog, ho ferem amb una aplicació creada ad-hoc i que serví per experimentar amb un hosting de Python. El hosting va caure un pic en el procés d'inscripció (després de tot encara està en beta), però la gent d'Eldarion va respondre i en poques hores estava una altra vegada operatiu.
L'aplicació en si crec que ha respost bastant bé, tot i estar feta en quatre potades. Ha permés a la gent que s'havia inscrit prest i després no ha pogut venir, fer-ho saber ràpidament i comunicar-ho al següent de la llista d'espera. Tot d'una que tengui una estona més miraré de documentar l'aplicació (que el codi ja hi està) i posar-ho a bitbucket per tal que si hi ha més gent que s'animi entre tots poguem fer un bon programa de gestió d'events.
De l'event en sí poca cosa a dir, la sala plena d'amics, gent que ja coneixia personalment i gent que he pogut desvirtualitzar per primera vegada. Però la part important és amics, això fa que parlar en públic sigui molt menys estressant i també molt més divertit. Va venir molta gent que fa coses interessants en programari lliure, Python o no, però que té pànic escènic. Encara que ja sabeu no tenc cap problema en parlar de Python hores i hores, també m'agradaria que el Creantbits fos un punt de trobada on els amics s'expliquen tecnologies interessants. Pensau que no xerrau davant un auditori estrany, sinó a un grupet de gent, d'iguals, i allò que per vosaltres pot ser el dia a dia potser sigui una revelació per altra gent. Així que animau-vos, que llevar-se la por escènica és important i res millor que fer-ho entre gent de confiança.
La valoració de les xerrades no seré jo qui les faci, esper que us resultessin interessants. Era la primera xerrada de @morenosan, l'home passava pena per si els nirvis el traïen, però ja vàreu veure que se'n va desfer prou bé. En Juan té moltes coses a dir en el món de la programació i noves tecnologies i esper que ara que ja sap que no passa res, s'animi a preparar-nos altres matèries.
En Bernat també tenia els seus dubtes, al matí va començar a dir que igual si no hi havia temps la conferència de Varnish no era tan important, que a lo millor no calia, ... Però no em vaig deixar convèncer i crec sincerament que va ser una de les exposicions més interessants que hem fet al creantbits.
Ja veis, el que costa més d'un event d'aquest és que la gent perdi la por, però és important fer-ho, perquè personalment estic convençut que en aquesta Illa nostra hi ha molta gent que fa coses molt interessants i que no les valora prou. Contava l'anècdota d'un conegut empresari madrileny que es dedica a fer webs per hotels, anava dient a tort i dret que havien fet un cms que admetia llenguatges no llatins, i això ho deia al 2011, nosaltres mateixos ja havíem fet webs en xinès al 2006, i ja no us cont Juan que estigué 5 anys al Japó. És, però un bon exemple de que potser no li donam la importància que es mereix a fer aquestes coses.
I una altra anècdota de l'event. Ens vàrem deixar els curetes per remenar el sucre. Però potser la millor anècdota de totes va der la de @SebaSj , que ens va dir que no podia venir perquè la filla estava en camí. Hem estat molt contents de saber que n'Helena ha fet gairebé tres kilos i que han passat bona nit. L'enhorabona!
El proper creantbis no sé quan serà. Depèn de la feina i de les ganes de la gent, però al manco ja sabem que n'Antònia està disposta a parlar-nos de Python i càlcul numèric, que potser en Xesc també s'animarà a fer alguna coseta i que en Pau quan aprovi la pràctica, té moltes ganes de parlar de Haskell.
Una abraçada a tots el que vinguéreu i disculpes al que quedàreu fora. L'aforament de la sala és el que és i per ara no tenim un lloc millor. La sala gran de l'auditòrium estareu d'acord amb mi que imposaria massa a l'hora de parlar. La por escènica és molt menor quan tens la gent propera i els oients estan agafant gominolas!
Traducciones/Translations by apertium
2 comentaris, 0 trackbacks (URL) , Tags: Informàtica Python Django APSL
Redis
Escrit per Aaloy a 26 de June , 2011 a les 10:55 a.m.
Ja fa un grapat de mesos estic fent cosetes amb Redis, una base de dades de les anomenades noSQL, molt semblant en funcionament a Memcached.
Val a dir que a Redis hi vaig arribar a partir de Celery, la utilitat per crear i gestionar tasques per Python i Django. Vaig trobar la combinació Celery més Redis molt bona quan no necessites tota la potència, ni tota la complexitat que et dóna RabbitMQ.
La idea, una vegada hagi finalitzat les proves, és anar substituint Memcached per Redis com a sistema de caché per les aplicacións Django. També hi ha un projecte per fer que les sessions també puguin estar damunt Redis, així que crec que també li tocarà. A més, d'aquesta manera ja tenim una base de dades addicional per fer-la servir quan sigui necessari. Redis ofereix una cosa que Memcached no té, la persistència de la informació.
En el cas de la caché, Redis pot ajudar a solucionar un dels problemes més importants que hi ha quan un fa aplicacions grans, la invalidació de les cachés. Amb memcached la invalidació sovint és un tot o res, és complexa fer que s'eliminini sols una part de la informació si no saps ben bé quines són les claus exactes que s'han fet.
Redis té una velocitat de resposta i suport a la concurrència tan bona o millor que memcached, però a més ens permet fer consultes sobre claus que comencin per algún prefix, o per claus concretes, podem saber quin tems d'expiració té cada clau, veure les claus que hi tenim al sistema, etc.
Això ens dóna tota una nova via per dissenyar el sistema de cachés, on tant l'usuari com el propi sistema poden decidir invalidar la caché en funció de les necessitats de l'aplicació.
Podem eliminar completament el contingut de la base de dades amb una simple comanda, FLUSHDB. Posau-ho per exemple a l'abast de l'administrador de l'aplicació web. Quan fa un canvi important i no vol esperar podem posar-hi aquest link per a que llanci aquesta comanda contra Redis.
Si ho feim servir dins Django podem veure com aquest va generant les claus, així podem decidir millor què invalidam. És a dir, tenim tot el que teníem amb Memcached i un món nou de possibilitats.
A més l'API per Python és molt bona, tant que amb l'iPython i l'API no hi ha pràcticament necessitat d'anar a la pròpia consola de Reids (el redis-cli), per acabar-ho de rematar s'ha millorat molt el parseig de la resposta amb hiredis-py.
En resum, estic gratament sorprès amb aquesta eina i us animo a fer-li una ullada.
Traducciones/Translations by apertium
0 comentaris, 0 trackbacks (URL) , Tags: Python Django
Un creant bits d'estiu
Escrit per Aaloy a 17 de June , 2011 a les 8:09 p.m.
Fa just una estoneta he fet l'anunci per Twitter de que ens han confirmat des del Parc Bit que podem disposar de la sala de formacio pel #creantbits el dia 15 de juliol a les 16:00.
Aprofitant que és estiu i fa molta calor, i la gent tampoc està per xerrades molt llargues, hem pensat que estaria bé fer xerrades curtes i d'un bon grapat de temes.
La primera per anar fent boca serà de Python. Una introducció als nousvinguts en aquest llenguatge que serveixi per perdre-li la por. En una horeta es pot veure bastant bé el llenguatge i tenir un idea suficient per poder seguir la resta de xerrades, o al manco per adonar-se de la potència que s'amaga darrera el llenguatges.
Després en @morenosan ens parlarà mitja horeta de South i de les seves possibilitats quan fas desenvolupaments amb Django. South ens permet fer modificacions a la nostra estructura de base de dades, de manera que passar de desenvolupament a preproducció i després a producció sigui poc traumàtic, de fet per a que sigui gairebé automàtic. Per la gent que no ha fet servir mai una eina com aquesta segur que serà una revel·lació.
Mirarem que @bercab perdi la por escènica i ens faci una introducció a Varnish. Un sistema de caché del més potent que hi ha i que ben duit ens permet aguantar una quantitat ingent de visites. Estam parlant de 15.000 o més peticions per segon en servidors de gama baixa. Com tot sistema té avantatges i inconvenients. No es tractarà de dir com muntar Varnish, sinó de que la gent que ho vulgui montar per les seves webs tengui sàpiga amb què juga.
Com heu pogut veure en alguns apunts m'agrada molt Celery, així que m'haureu d'aguantar un poc presentant-vos aquesta llibreria. Parlaré de quan i com es pot fer servir, de diferents configuracions que es poden emprar i d'escalabilitat.
Estam mirant de tancar una altra ponència, això de parlar en públic, encara que sigui entre amics pareix que imposa bastant. Ja veurem, si no un poc de trobada social que tampoc no està malament.
No es tracta de fer un curs o una classe magistral, es tracta de perdre la por a la tecnologia, de veure llibreries i programes que potser us sonaran i potser no, però sobretot es tracta de conèixer-nos i trobar-nos de tant en tant.
I ja sabeu que si algú està interessat en preparar-se un tema, doncs a aquesta mateixa trobada o per la propera. Quan més rotació hi pugui haver en les presentacions millor, que si no sempre parlam els mateixos, i no em queix, el que és complicat és aturar-me una vegada he començat a xerrar. :-P
En aquesta ocasió i aprofitant l'entorn de Gondor, he creat una petita aplicació per les inscripcions, està en beta i serà la primera prova baix foc real que es fa.
Inscripcions: http://creantbits.com
Traducciones/Translations by apertium
0 comentaris, 0 trackbacks (URL) , Tags: Informàtica Python Django
Avaluant Gondor
Escrit per Aaloy a 15 de June , 2011 a les 9:08 p.m.
Estic a la segona tongada de beta-testers de Gondor un hosting per Django montat al núvol desenvolupat per la gent d'Eldarion.
L'aplicació vol simplificar la manera de desplegar les aplicacions Django a força de forçar una determinada configuració. És a dir, es simplifica el desplegament, però sempre que juguem amb les cartes que ens donen, que he de dir que no són dolentes.
La posada en marxa és senzilla, el programa que fa la interacció amb Gondor (una vegada t'has donat d'alta a la web) és un paquet que està al PyPi, així que és sols cosa de pip install gondor i gairebé ja pots començar a fer-hi feina.
Per provar he fet servir un codi que ja tenia mig fet i que ara s'ha convertit en la web d'inscripcions de creantbits. La conversió d'una aplicació tal com la desplegam a APSL habitualment a una aplicació capaç de ser desplegada amb Gondor és prou senzilla, i està ben explicada a la documentació. Si més no però, convé fer uns petits avisos per si algú també s'hi troba:
-
El teu projecte ha de ser gestionat per Git o Mercurial. Tant fa si no hi ha un repositori remot, però s'ha de tenir en compte que el desplegament es fa damunt una versió commitada del codi.
-
S'ha de crear una carpeta anomenada
requirementson hi hagi un arxiu anomenatrequirements.txtamb els requeriments de l'aplicació amb un fortat llegible perpip. No s'hi valen paquets que s'hagin de compilar llevat de lxml i PIL, la connexió a BD, Postgres per més senyes ja ens la donen ells. -
S'ha de crear una carpeta
deployamb la connexió WSGI. S'ha d'anar alerta amb els paths. En el meu cas no m'agrada tenir dependències del nom del projecte a les aplicacions ni a les urls, així que he modificat el WSGI (per cert, sabeu que es llegeix com a Whisky?). -
Alerta amb els STATIC_URL i MEDIA_URL Gondor he comprovat que dóna menys problemes si es fan servir les convencios d'static files, així que una adaptació que s'ha de fer a les aplicacions és substituir a les plantilles els MEDIA_URL per STATIC_URL (i configura el settings) i posar el contingut estàtic lligat a una aplicació per a que funcioni el
collectstatic.
Amb això i amb un parell (dues o tres) de proves la primera versió de l'aplicació ja funcionava, els que em seguiu per Twitter ja ho haureu notat, ja que he estat donat la murga amb això els darrers dies.
Gondor desplega l'aplicació creant un tar.gz del master del nostre repositori (el tip en terminologia Mercurial) i l'envia al seus servidors. Allà es desplega, es creea un virtualenv, s'instal·len les dependències i es crea la BD o es migra si és una actualització.
Això fa que el procés d'actualització, fins i tot per una modificació a una plantilla sigui un tant lent, ja que les velocitats de pujada de les ADSL són de vergonya. Estic mal acostumat als servidors dedicats que tenim, bé directament o amb fabric les actualitzacions ens duen segons. Està clar que la gent de Gondor ha tingut que arribar a un compromís entre velocitat de desplegament i estabilitat de l'entorn. Els vaig fer arribar aquesta "queixa" i em contestaren que estan treballant en un mètode diferencial per fer les actualitzacions, la qual cosa pareix que pot millorar molt el conjunt.
Encara no tenen a l'abast Celery, el sistema de cues, que és força important ja que no es té accés al cron del servidor i sovint són necessàries tasques periòdiques, com per exemple per netejar les sessions caducades.
Llevat d'això, la plataforma per ara és molt estable, quan hi ha errors les missatges se t'envien a la pròpia consola i quan fas l'actualització surt una plana 503 ben informativa.
Quan feia les proves vaig enviar uns quants missatges per aclarir conceptes o suggerir millores i em contestaren ràpidament, ja mitjançant e-mail o pel Twitter d'Eldarion.
Encar no sé el cost de la plataforma, ni les funcionalitats que tindrà en el futur. Però les impressions inicials són bones. Per molts dels projectes que feim Gondor no és una alternativa, però per projectes tipus la web del creantbits i semblants va prou bé.
Traducciones/Translations by apertium
2 comentaris, 0 trackbacks (URL) , Tags: Python Django
Geany, editor per a programació
Escrit per Aaloy a 25 de April , 2011 a les 12:23 p.m.
Aquestes festes he estat mirant-me l'editor Geany per veure com responia a les meves necessitats de tenir un editor per a programació fonamentalment Python i Django que fos a la vegada potent i que tingués baixos consums de memòria.
En un editor per a programació crec que és important seguir el principi de Pareto o regla del 80-20, és a dir, vull un editor que amb el 20% de comun de recursos tengui el 80% de les funcionalitats d'un IDE com Eclipse+Pydev o PyCharm per exemple.
Avui en dia el meu editor de capçalera és Vim/Gvim totalment personalitzat amb la configuració que podeu trobar a trespams-vim, crec que és una bona configuració, però tot i la potència de Vim tanmateix no faig servir ni un 20% de les possibilitats que m'ofereix l'editor. El que sí que crec que és important és dominar-lo, ja que de tant en tant convé editar via ssh i saber fer anar vim amb condicions és un avantatge.
La cosa doncs és que vaig decidir donar una altra ullada a Geany, ja que vaig veure a la web que havien llançat una nova versió, la 0.2 més moderna que la que tenia ja instal·lada a Ubuntu. Una cosa que a mi en va molt bé és el ressaltat de plantilles per Django (cosa que Gvim té), així que per a ser justs, el primer que vaig fer va ser mirar si Geany també ho tenia: requereix una mica de configuració, però sí, us explic:
Cercant per Google vaig arribar a la plana de drevans, on explica com podem activar el ressaltat, és prou senzill, basta fer
cp /usr/share/geany/filetypes.html ~/.config/geany/filedefs/
editar l'arxiu que acabam de copiar i cercar la secció lexer_properties i afegir
lexer.html.django=1
Amb això ja tenim colorejat per la sintaxi de plantilles Django. Una cosa ja feta.
Superat el primer obstacle ja sols és cosa de veure quin conjunt de característiques té Geany comparat amb altres editors o IDEs i quines no té, i el grau d'importància relativa que tenen per mi. Deix de banda coses que podem donar per pressuposades a un editor modern: ressaltat de sintaxi per múltiples llenguatges (fonamental ressaltat de javascript dins html) i edició de múltiples documents.
-
Gestió de projectes. És una característica que classificaria com a important. Estalvia molt de temps que l'entorn et situi directament al directori i mantengui la llista dels darrers arxius que es van obrir al projecte.
-
Treball amb UTF-8 i format Unix. La nostra configuració de feina per defecte és UTF-8, quatre espais per tabular i salts de línia en format Unix. Si ja no té això no me mir l'editor, així que és una característica obligatòria.
-
Autocompletat. Realment no necessit que tengui un autocompletat de llibreries (al cap i a la fi Python no és Java) però si ho té millor, i sobretot el que va molt bé és tenir un autocompletat basat en que un ja ha escrit en el document, ja que evita molts errors tipogràfics.
-
Reasaltat de sintaxi per HTML i Javascript dins el mateix document El ressaltat de sintaxi va molt bé a l'hora de programar, pots detectar errors sols pel colorejat de l'editor, per això és important que a l'hora d'editar HTML on cada cop és més comú que hi pugui haver javascript, el ressaltat sigui prou inteligent per detectar que estic a la part javascript del codi i adapti també el ressaltat.
-
Integració amb un comprovador de sintaxi per Python com pylint o pyflakes i pep8. Es pot fer la comprovació per línia de comandaments, però és interessant no tenir que sortir de l'entorn. És doncs una característica interessant però no fonamental.
-
Parseig de símblos Va molt bé que un editor per a programació sigui capaç de parsejar el codi font i et mostri les classes i funcions que tens definides dins el document, estalvia molta feina a l'hora de navegar pel codi o trobar el que t'interessa. No és una característica fonamental, però pot ajudar a decidir.
-
Maneig de bookmarks Cada cop faig servir més aquesta característica a Vim ja que em permet navegar ràpdament entre distintes seccions del codi. No és fonamental però també molt convenient.
-
Cerca potent De les millors que he vist són les d'Eclipse i Netbeans que et permeten cercar per tot el projecte.
-
Baix consum de recursos. Per mi és important poder fer servir l'editor en qualsevol dels equips que tinc. Fer feina sempre amb el mateix editor fa que a poc a poc un s'ho vaig fent seu i n'aprofiti millor les funcionalitats. Si triam un editor gràfic hem de tenir en compte que a més convindrà dominar un poc les quatre coses d'un editor en moda consola com Vim. Si l'entorn consumeix molts recursos ens podem trobar que no hi hagi prou memòria per engegar altres aplicacions que necessitam, sobretot en equips més vells. Vaig deixar de fer servir Eclipse i després Netbeans per aquest motiu. Per fer modificacions xorres necessitava esperar gairebé un minut per posar tot l'entorn en marxa. Netbeans a més ha deixat de suportar Python, així que ho he descartat tot i les darrers millores.
-
Integració amb control de versions Interessant però com en el cas del comprovador de sintaxi o precompilador és quelcom que sovint és més ràpid fer per línia de comandaments.
La resta de característiques que pot tenir un IDE poden estar molt bé i potser un les fa servir un cop o dos al llarg d'un projecte, però per mi i amb el tipus de projectes que feim, crec que no compensen el tenir que carregar amb un entorn feixuc.
Geany compleix amb gairebé totes les funcionalitats que he exposat aquí, fins i tot l'autocompletat va un poc més enllà i es capaç d'autocompletar a partir de les llibreries Python. La integració amb Pyflakes y Pep8 es pot fer i els missatges d'error o avís apareixen a la finestra de missatges. Tot i això he de dir que li faltaria ponder
fer clic damunt un missatge d'error i anar directament a la línia, per això s'ha de configurar un poc, anirem a la secció Munta i a on diu "Error regular expression" posarem (.+):([0-9]+):[0-9]+ això ens servirà tant per pyflakes com per pep8, de fent a la meva configuració actual tinc com a compilador pyflakes "%f" associa a la tecla F8 i associat a F9 pep8 "%f" per saber si es compleixen les convencions de codi.
El que he trobat molt úlil a Geany és que té una secció on pots veure tots els documents que tens oberts classificats en la carpeta on es troben. Quan fas feina amb Django on tots els models són models.py et permet navegar ràpidament pels arxius que tens oberts.
Genay també ve amb un conjunt de plugins per estendre la funcionalitat de l'editor. El més interessant que he trobat és un formatejador d'XML, ja que m'estalvia tenir que fer servir un altre programa i realment no carrega gens l'editor.
Geany té també la possibilitat de definir plantilles de codi, com entorns molt més grans i que és una de les característiques que m'agraden més de Vim. D'entrada el conjunt de plantilles que duu per defecte són molt poques, però és molt bo de fer crear-ne més.
En conclusió, Geany és un editor potent, senzill de fer anar i amb un consum de recursos de màquina realment petits. Me pareix que serà el meu proper editor de capçalera.
Traducciones/Translations by apertium
2 comentaris, 0 trackbacks (URL) , Tags: Python Django
Celery, Redis and Django
Escrit per Aaloy a 03 de April , 2011 a les 9:46 p.m.
Disclaimer: This is my first English post is a free translation of the original catalan post
In previous posts have written about Celery and Django Celery, a system to manage queues and tasks in Python and Django.
Celery in its documentation recommends RabbitMQ as message broker, that is, as the application that receives and distributes the tasks that the application sends between the different workers we have configured in our system.
Once the worker has done the task it leaves the result (if we have configured to do it) to the results backend, usually it is the same one as the message broker, that is RabbitMQ acts as a message broker and as a result backend.
The architecture of Celery is very powerful in the sense it allows us to scale up and down and replace the parts we need to configure the application to our needs. So we could have applications that needs some sort of message or task distribution, but they don't need to deal with the complexity nor the system requirements of RabbitMQ. With Celery we can even use a database as a message broker where could save the results, we can replace the serialization routines, the results storage. So, although in the documentations we have a prefered configuration we can change it the needs of our application.
In this post I'll try to present is a configuration that fits in the middle of the complexity of a RabbitMQ solution but enough powerful to fit most application needs.
What's the problem we'll try to solve?
We have an application that we want to run in an small server or in a shared server that needs some sort os distributed tasks or periodic task than we want to manage inside the application itself. We would like:
- Minimum complexity to install and configure the task system.
- We'd like not to have a dedicated broker.
- We'd like to monitor what's happening in our application
- We'd like to manage our system easily.
- We'd like to debug our application and run everything on a local server before run the application using a distributed task configuration.
- We'd like our task broker could have very low memory requirements
We can imagine lots of scenarios in what that requirements would fit, a news aggregation, an e-comerce application that needs to send the invoices, an small document management system that makes some sort of format translation. That is, systems that need a small response time to the user and that could make the heavy task in an asynchronous way, where the reliability of the tasks system is not critical for the application
For that kind of applications Celery with RabbitMQ is overkill, so we're going to diet it a bit
The broker
We want the distribution of tasks to be powerful and flexible, but without the complexity of RabbitMQ. So what will be do is install Redis , a NoSQL a database that works in a similar way memcached does.
Redis is very fast and comsumes few machine resources, allows the persistence of periodic data and the application is generic enough to be used in our applications in addition to task management. A [presentation by Simon Wilson] (http://simonwillison.net/static/2010/redis-tutorial/) summarizes very well the possibilities of this database.
Redis has, however, and important requirement that we must know: in its standard configuration requires that all data has to fit in memory and that periodically synchroniZes the changes to disk. So we must monitor our application to be sure that Redis does not grow without notice consuming all the available memory.
Installing Redis
Readis is present in major Linux distribution, and in Debian based distros is enough to type
sudo apt-get install redis-server
as we're going to use Redis in Celery we must install also the Python API
pip install redis
inside our virtualenv (I suppose everybody is using virtualenv ...)
In a brand new installation in a Ubuntu 10.10 redis consumes 3271B of virtual memory and 1516B of resident memory in a single process.
In a production environment for sure we would like to configure some parameters:
- bind, to link the redis instance to an IP
- loglevel is verbose in the default configuration, in production notice or warning would be enough.
The configuration file for redis is in /etc/redis/redis.conf in the Ubuntu, is extensively documented to allow us to adapt it to our needs.
The results storage
As mentioned Celery also allows us to define where to store our data. Redis is a general purpose database, so in addition to the tasks broker we can use it to save the results of the tasks. As pointed before, we have to monotor Redis if we plan to store lot of data o if we store big results. Redis mantains all the database in memory.
Usually in a task/queue system we want to keep the results a just the time enought to see that everything is going well and then we don't need the results anymore.. That is, the results do not necessarily have to remain in the database, the amount of time we need to keep the results in the database would greatly depend on our application.
Let me explai myself. We use Celery in a B2C application to update the information we have about the hotels. We launch the update information periodicaly to update a the information and each task is able to run another taks. Once the information is received the information is processed. So the results just needs to be in the database the time that a worker needs to process it, after that we can delete it. As the process is quite fast is much simpler to make the results expire in 60 seconds than to write the code to delete it.
If we're need to create a task to send an invoice we do not need to save the invoice in Redis, we just need to update our database to mark the invoice as sent once the worker has finished the pdf generation and the mail is sent.
So if we want to mantain our low memory requirements we have to tune our application to not store a lot of information in the Redis database.
Using Redis as a broker and as a database makes us to reach our objective of reusing the technology, but we can use Redis as a cache backend for Django and to [store sessions]((https://bitbucket.org/dpaccoud/django-redis-sessions/src).
Our settings.py
First at all in our INSTALLED_APPLICATIONS we have to add djcelery and now we have to configure Redis as a broker and database backend.
BROKER_HOST = "192.168.1.33" BROKER_BACKEND="redis" REDIS_PORT=6379 REDIS_HOST = "192.168.1.33" BROKER_USER = "" BROKER_PASSWORD ="" BROKER_VHOST = "0" REDIS_DB = 0 REDIS_CONNECT_RETRY = True CELERY_SEND_EVENTS=True CELERY_RESULT_BACKEND='redis' CELERY_TASK_RESULT_EXPIRES = 10 CELERYBEAT_SCHEDULER="djcelery.schedulers.DatabaseScheduler" import djcelery djcelery.setup_loader()
192.168.1.2 is the virtual image in which I have installed a fresh Ubuntu and that runs Redis, as in this post I'd like to emulate a simple production environment with two servers. As you can see I have no password protection and Redis runs in its default port.
Note that on BROKER_VHOST we have to configure the database Redis will use for the broker system. It can be the same one as the REDIS_DB but we could choose to have the results and the task communication in different databases. CELERYBEAT_SCHEDULER CELERY_TASK_RESULT_EXPIRES is just 10 seconds, time enough for our purposes. CELERYBEAT_SCHEDULER is configured to allow us to create periodic task from our Django application. As this needs new database tables, we would need to run syncdb to create the tables that the scheduler needs.
Development mode
On development on of the first goals is to be sure everything works properly, so we don't need the noise that the broker and storage puts on our development process. Celery has a special configuration
CELERY_ALWAYS_EAGER=True
so our application would not use nor the worker neither the broker and is executed as a common application, just invokes the task in a synchronous way.
Lets start the workers
When you start with Celery it's important to have a global vision about what's happening in our application. I have found that terminator is a good tool to run our console commands, splitting our terminals in order to see what's happening.
So lets open a console in our application environment and run
python manage.py celeryd -E -B --loglevel=INFO -n w1.d820
This will run a worker, configured to run the default number of processors, which depends on the number of CPUs available on our server. We have configured the worker to send monitoring signals (-S) and to run an additional process to deal with the periodic tasks (-B).
It's important to remark that just one worker can have the -B parameter, so perhaps is better to make this fact more visible and run the periodic task process using a dedicated command
python manage.py celerybeat --loglevel=INFO
Running celerybeat as a standalone process it will inform us about its configuration
[2011-04-03 11:16:46,808: WARNING/MainProcess] celerybeat v2.2.5 is starting.
[2011-04-03 11:16:46,863: WARNING/MainProcess] __ - ... __ - _
Configuration ->
. broker -> redis://@192.168.1.33:6379/0
. loader -> djcelery.loaders.DjangoLoader
. scheduler -> djcelery.schedulers.DatabaseSchedulerAs we want to monitor the tasks and have more than just one worker is important to name them. This can be done with the -n parameter. I like to add the worker number and the server name. In the example the name of my laptop.
Run a second worker is as easy as:
python manage.py celeryd -E --loglevel=INFO -n w2.d820 -------------- celery@w2.d820 v2.2.5 ---- **** ----- --- * *** * -- [Configuration] -- * - **** --- . broker: redis://@192.168.1.33:6379/0 - ** ---------- . loader: djcelery.loaders.DjangoLoader - ** ---------- . logfile: [stderr]@WARNING - ** ---------- . concurrency: 2 - ** ---------- . events: ON - *** --- * --- . beat: OFF -- ******* ---- --- ***** ----- [Queues] -------------- . celery: exchange:celery (direct) binding:celery
As we can see we have not add the -B parameter and Celery informs us that the beat process is off.
We can increase or decrease the number of default processes that the worker is going to star with the --concurrency parameter. The final number is a matter to test and see.
python manage.py celeryd -E --concurrency=10 -n w3.d820
Monitoring: what's happening in my application?
If we have added logs on our applications in each worker we can check and see the output, but perhaps we have no logs o our workers could be distributed in different servers. Celery provides us with some monitoring that are nice to know. To use such tools the first step is to start the monitoring service:
python manage.py celerymon
celerymon 2.2.5 is starting.
Configuration ->
. broker -> redis://@192.168.1.33:6379/0
. webserver -> http://localhost:8989
celerymon has started.As we can see Celery has started a server on port 8989. We can connect to that server and see the registered workers and tasks. It's some sort of raw information but it could be enough
[{"heartbeats": [1301824037.784225],"hostname": "w1.d820"},
{"heartbeats": [1301824018.90294], "hostname": "w2.d820"}]As we have configured the DatabaseScheduler we could see the tasks in the Django application itself, but there is another tool on colole mode that give us nearly realtime information, the celeryev
With python manage.py celeryev we will start an console application that will show us what tasks are being processed, we can see the result of each tasks and even revoke a task. If we want more control about the monitoring tools Celery provides an API to get the information, and of course you can look at the source code for celeryev.
It's important to monitor also the Redis server
sudo tail -n 100 -f /var/log/redis/redis-server.log
we'll see what's happening on the Redis side,
==> /var/log/redis/redis-server.log <== [677] 03 Apr 09:56:10 - Accepted 192.168.1.32:38923 [677] 03 Apr 09:56:10 - Client closed connection [677] 03 Apr 09:56:10 - Client closed connection [677] 03 Apr 09:56:10 - Accepted 192.168.1.32:38924 [677] 03 Apr 09:56:10 - Client closed connection [677] 03 Apr 09:56:10 - Client closed connection [677] 03 Apr 09:56:10 - Accepted 192.168.1.32:38925
Redis provides also a client console, redis-cli that we could use to get more information and make a lot of management task. Some useful commands are:
KEYS *shows us the active keysDBSIZEinforms us about the size of the active database-
INFOgive us a lot of information about our database, it's really useful to check the memory consumption
process_id:677 uptime_in_seconds:5349 uptime_in_days:0 connected_clients:14 connected_slaves:0 blocked_clients:4 used_memory:1551228 used_memory_human:1.48M changes_since_last_save:1111 bgsave_in_progress:0 last_save_time:1301824662 bgrewriteaof_in_progress:0 total_connections_received:509 total_commands_processed:7060 expired_keys:0 hash_max_zipmap_entries:64 hash_max_zipmap_value:512 pubsub_channels:1 pubsub_patterns:0 vm_enabled:0 role:master db0:keys=8,expires=0
-
FLUSHDBcleans all the database removing all the keys MONITORshows us in real time what's happening, what commands are being executed, and the keys and information that is stored in the database.
To summarize
With Django, Celery and Redis we have a simple task distribution, scalable and with very small server requirements.
We can use Redis as a broker, as as data store and in other tasks of our Django application: as another database, as a session database, as a cache server.
If we want to work using tasks to split the work we have to
- Develop our application thinking in tasks and asynchronous processes.
- Install and configure Redis
- Run the workers
- Run celerybeat if we have periodic tasks
- Run the monitor
And of course we have to monitor all the application. Enjoy!
Traducciones/Translations by apertium
0 comentaris, 0 trackbacks (URL) , Tags: Python Django
Celery i Redis
Escrit per Aaloy a 03 de April , 2011 a les 12:11 p.m.
En anteriors apunts he parlat ja de Celery i de Django Celery, un sistema per a la gestió de cues i tasques per Python i Django.
Celery a la seva documentació recomana RabbitMQ com a gestor de missatgeria, és a dir, com a aplicació que reb i distribueix les tasques que li envia l'aplicació entre els diferents worker que tenguem al nostre sistema.
Una vegada el worker ha realitzat la tasca deixa el resultat (si així ho hem indicat) al contenidor de resultats, que normalment serà el mateix que el broker de missatgeria, és a dir RabbitMQ.
L'arquitectura de Celery és molt potent en tant que ens permet escalar cap a baix i substituir peces segons necessitem. Així per aplicacions que necessitin d'un sistema de distribució de tasques però no de la complexitat de RabbitMQ podem utilitzar altres sistemes de notificacions, fins i tot fer servir una base de dades. On es guarden els resultats o com es serialitzen els missatges també es pot canviar. En definitiva, encara que hi hagi una configuració recomanada per a entorns que necessitin de molta potència en el sistema de distribució de tasques i coes, podem personalitzar Celery al nostre gust i a les nostres necessitats.
En aquest article el que presentaré és una configuració a mig camí entre el que seria la configuració més complexa de Celery i una configuració simple basada sols en base de dades.
Plantejant el problema
Tenim doncs una aplicació que no és molt gran, però a la que aniria molt bé tenir un sistema de distribució de tasques.
- Volem que la instal·lació i configuració sigui senzilla.
- El que instal·lem si pot ser hauria de poder aprofitar-se per quelcom més que no el sistema de tasques.
- Volem poder monitoritzar el que està passant.
- Volem poder gestionar fàcilment l'entorn
- Hem de poder depurar fàcilment el que està passant i assegurar-nos que l'aplicació funciona sense tenir que muntar tot l'entorn.
- El gestor de tasques ha de consumir pocs recursos i ser bo de controlar.
- El gestor de tasques ha de permetre l'execució de tasques periòdiques.
- Volem poder tenir accés als resultats.
Podem imaginar molts escenaris en que això s'ha de complir, per exemple un agregador de notícies, en un sistema de e-comerce l'enviament de factures, en un petit gestor documental la conversió de formats, ... És a dir, sistemes en els que volem que la resposta cap a l'usuari final sigui el més ràpida possible i que la feina feixuga no necessàriament es tengui que fer de manera síncrona, però on tampoc passa res si el sistema de missatgeria cau i perd alguna tasca, simplement es pot tornar a executar.
Amb aquestes condicions la configuració estàndard de Celery i de Django Celery és massa complexa, així que el que farem és aprimar-la un poc.
El broker
Volem que el sistema de distribució de tasques sigui prou potent i flexible, però sense la complexitat de RabbitMQ. Per això el que farem serà instal·lar Redis una base de dades NoSQL molt semblant al que seria un memcached.
Redis té l'avantatge de que és molt ràpid, ocupa molt pocs recursos de màquina, permet la persistència periòdica de les dades i és una aplicació prou genèrica com per poder ser utilitzada en les nostres aplicacions a més de per fer la gestió de les tasques. Una presentació de Simon Wilson crec que resumeix molt bé les possibilitats d'aquesta base de dades
Redis té un emperò important que hem de conèixer, és una base de dades que en la seva configuració estàndard requereix que totes les dades hi càpiguen en memòria i que aquesta es sincronitzi de manera periòdica al disc. Així doncs hem de procurar monitoritzar la nostra aplicació i l'ús que en fa de Redis per a no consumir més memòria de la que ens vulguem permetre.
La instal·lació de Redis
Redis està com a paquet en les principals distribucions, així que per distribucions basades en Debian bastarà fer:
sudo apt-get install redis-server
i com que també l'utilitzarem des de la nostra aplicació convé instal·lar també el l'API de connexió per Python, amb pip mateix
pip install redis
dins el nostre entorn virtual (perquè està clar que feis servir virtualenv a les vostres aplicacions, no?).
En una instal·lació neta dins una màquina virtual Ubuntu redis està ocupant 3271B de memòria virtual i 1516B de memòria resident a un únic procés.
En producció segurament ens interessarà tocar alguns paràmetres de Redis per fer la configuració més segura:
- bind ens permet lligar redis a una IP concreta.
- loglevel per defecte està a verbose, a producció amb notice o warning serà suficient.
l'arxiu de configuració de redis es troba a /etc/redis/redis.conf i està molt ben documentat per a poder adaptar-lo a les nostres necessitats.
On guardam els resultats?
Com hem dit Celery ens permet definir també on guardar els nostres resultats. Donat que Redis és una base de dades de propòsit general, a més de fer les tasques de missatgeria l'aprofitarem per a que guardi els resultats de les tasques.
Aquí hem de tenir en comptes el que dèiem de Redis, si guardam moltes dades i no pensam en anar fent neteja la memòria de Redis anirà creixent fins a ocupar tot el que tinguem.
Normalment a un sistema de tasques i cues ens interessarà guardar els resultats un temps per veure que tot està anant bé i després ja no els necessitam. Es a dir, els resultats no tenen perquè quedar forçosament dins la base de dades Redis, això ja dependrà de la nostra aplicació.
M'explicaré. Una de les aplicacions on feim servir Celery ens permet actualitzar la informació que tenim d'hotels. Es van llançant les peticions d'actualització i el worker se n'encarrega de baixar-se la informació llançant a la seva vegada una altra tasca i de processar-la una vegada l'ha rebuda. La informació sols necessita estar al magatzem el temps just i necessari per poder monitoritzar que tot va bé i que el worker ha rebut i processat la informació.
En el cas de la generació d'una factura no hem de guardar necessàriament el pdf de la factura dins Redis, basta poder dir que la factura s'ha realitzat correctament o directament suposar que ha anat tot bé i periòdicament tornar a llançar les tasques d'enviament per a tots aquells clients que han sol·licitat la factura i no se'ls ha enviada.
Així doncs utilitzarem també Redis per guardar els resultats i configurarem la nostra aplicació per a que no els guardi per sempre.
Amb això ja hem aconseguit que Redis faci una doble funció, però és que a més podem utilitzar Redis per a guardar les sessions de Django o fer-la servir com a Cache. Així doncs estam aprofitant els recursos que és un dels nostres principals objectius.
La configuració del settings.py
A INSTALLED_APPS hem afegit djcelery i per configurar Redis com a broker i com a magatzem de resultats feim:
BROKER_HOST = "192.168.1.33" BROKER_BACKEND="redis" REDIS_PORT=6379 REDIS_HOST = "192.168.1.33" BROKER_USER = "" BROKER_PASSWORD ="" BROKER_VHOST = "0" REDIS_DB = 0 REDIS_CONNECT_RETRY = True CELERY_SEND_EVENTS=True CELERY_RESULT_BACKEND='redis' CELERY_TASK_RESULT_EXPIRES = 10 CELERYBEAT_SCHEDULER="djcelery.schedulers.DatabaseScheduler" import djcelery djcelery.setup_loader()
El 192.168.1.33 és la màquina virtual on tenc instal·lat Redis, per tal d'emular un entorn de producció amb dues màquines. El port és el port per defecte i no he protegit redis amb usuari i password.
Important, el BROKER_VHOST és per redis el nom de la base de dades que es farà servir. Per defecte Redis fa servir la base de dades zero (0) però res ens impedeix tenir una base de dades diferent per les tasques i una altra pels resultats.
A CELERY_TASK_RESULT_EXPIRES podem veure com hem posat que els resultat expirin als 10 segons, més que suficient per la configuració de l'aplicació.
CELERYBEAT_SCHEDULER ens permet utilitzar la nostra aplicació d'Django per a gestionar les tasques periòdiques. Necessitarem fer un syncdb per a crear les estructures de dades, però a canvi podrem definir períodes i tasques.
Desenvolupant
En desenvolupament ens interessarà tenir molt present que tenim un sistema de coes i tasques, però realment el que més ens interessa és provar que tot funciona
CELERY_ALWAYS_EAGER=True
Aquesta configuració fa que l'aplicació s'executi com si no tingués coneixement del gestor de tasques, la qual cosa ens permetrà crear i depurar la nostra aplicació com sempre hem fet.
Posant en marxa els workers
Per tal de monitoritzar millor el que feim podem utilitzar una aplicació com terminator, una consola que ens permet agrupar consoles i veure d'una ullada el que està passant. A una d'aquestes consoles farem:
python manage.py celeryd -E -B --loglevel=INFO -n w1.d820
Això posa en marxa un worker, configurat amb un nombre de processos per defecte (2 en el meu cas) i li deim que envii els senyals de monitorització (això és el paràmetre S) i que a més engegui un procés addicional per a que gestioni les tasques periòdiques.
Hem de tenir en compte que sols hi pot haver un procés de gestió de tasques periòdiques, així que o bé es llança des d'un sol worker o bé podem fer servir l'aplicació celerybeat
python manage.py celerybeat --loglevel=INFO
Recordau! O una manera o l'altra però no les dues i mai per duplicat a dos workers.
Si l'executam per separat celerybeat ens informa de que està engegat i de las configuració que farà servir:
[2011-04-03 11:16:46,808: WARNING/MainProcess] celerybeat v2.2.5 is starting.
[2011-04-03 11:16:46,863: WARNING/MainProcess] __ - ... __ - _
Configuration ->
. broker -> redis://@192.168.1.33:6379/0
. loader -> djcelery.loaders.DjangoLoader
. scheduler -> djcelery.schedulers.DatabaseSchedulerCom que volem controlar i monitoritzar bé el que passa i potser tenir més d'un worker és convenient posar-los nom, això es fa amb el paràmetre -n, a mi m'agrada donarlos un nom junt amb el nom de la màquina.
Arrancaré un segon worker:
python manage.py celeryd -E --loglevel=INFO -n w2.d820 -------------- celery@w2.d820 v2.2.5 ---- **** ----- --- * *** * -- [Configuration] -- * - **** --- . broker: redis://@192.168.1.33:6379/0 - ** ---------- . loader: djcelery.loaders.DjangoLoader - ** ---------- . logfile: [stderr]@WARNING - ** ---------- . concurrency: 2 - ** ---------- . events: ON - *** --- * --- . beat: OFF -- ******* ---- --- ***** ----- [Queues] -------------- . celery: exchange:celery (direct) binding:celery
Fitxem-nos que en aquest cas ens diu que els events estan activat però que no hi ha el beat (el responsable de les tasques periòdiques actiu per aquest worker).
Podem ampliar o limitar el nombre de processos que arranca cada worker amb el paràmetre --concurrency, la configuració per defecte que depèn del nombre de CPUs disponibles sol anar bé, però sempre dependrà de la nostra aplicació i de les limitacions de recursos que li vulguem donar. Per exemple:
python manage.py celeryd -E --concurrency=10 -n w3.d820
Monitoritzant el que passa
Si a la nostra aplicació hem posat logs a cada worker podem veure el que està passant, però potser no sigui així, o tinguem els workers distribuïts a vàries màquines. Per això Celery ens dóna vàries eines de monitorització.
El primer que hem de fer és engegar el servei de monitorització:
python manage.py celerymon
celerymon 2.2.5 is starting.
Configuration ->
. broker -> redis://@192.168.1.33:6379/0
. webserver -> http://localhost:8989
celerymon has started.Si anam al servidor web que ha engegat Celery, podem veure un resum de les tasques i dels workers. Per exemple, en el nostre cas:
[{"heartbeats": [1301824037.784225],"hostname": "w1.d820"},
{"heartbeats": [1301824018.90294], "hostname": "w2.d820"}]Podem veure que hi ha dos workers actius.
Si hem fet servir el DatabaseScheduler veurem les nostres tasques dins la pròpia base de dades de Django, però a més hi ha una eina de consola d'allò més interessant celeryev
Amb python manage.py celeryev posarem en marxa una consola en la que ens informarà del que està passat, el nombre de tasques que hi ha executant-se i podrem veure informació damunt la tasca o eliminar (revoke) una tasca de la cua.
Si volem saber més coses o fer les nostres pròpies eines de monitorització Celery ens dóna tot una API per fer-ho, així que no estam limitats a les eines de sèrie.
El que ens queda també per monitoritzar és què està passant amb Redis
sudo tail -n 100 -f /var/log/redis/redis-server.log
ens informarà del que està passant i de les connexions que es realitzen.
==> /var/log/redis/redis-server.log <== [677] 03 Apr 09:56:10 - Accepted 192.168.1.32:38923 [677] 03 Apr 09:56:10 - Client closed connection [677] 03 Apr 09:56:10 - Client closed connection [677] 03 Apr 09:56:10 - Accepted 192.168.1.32:38924 [677] 03 Apr 09:56:10 - Client closed connection [677] 03 Apr 09:56:10 - Client closed connection [677] 03 Apr 09:56:10 - Accepted 192.168.1.32:38925
Si volem anar més enllà podem fer servir la consola de Redis, redis-cli
Algunes comandes molt útils:
KEYS *- ens monstra les claus que tenim actives.DBSIZE- ens diu com està el tamany de la nostra base de dades-
INFO- ens dóna tot un conjunt d'informació de com està la nostra bd, és especialment interessant veure i monitoritzar el tamany de la memòria.process_id:677 uptime_in_seconds:5349 uptime_in_days:0 connected_clients:14 connected_slaves:0 blocked_clients:4 used_memory:1551228 used_memory_human:1.48M changes_since_last_save:1111 bgsave_in_progress:0 last_save_time:1301824662 bgrewriteaof_in_progress:0 total_connections_received:509 total_commands_processed:7060 expired_keys:0 hash_max_zipmap_entries:64 hash_max_zipmap_value:512 pubsub_channels:1 pubsub_patterns:0 vm_enabled:0 role:master db0:keys=8,expires=0
-
FLUSHDB- per netejar tota la base de dades activa de Redis. MONITOR- ens mostra què està passant, les claus que es generen, els resultats i les sentències que va executant Redis.
Conclusions
Amb Django, Celery i Redis hem aconseguit tenir un sistema de distribució de tasques simple, escalable i amb molts pocs requisits de màquina.
Redis es pot aprofitar tant pel sistema de cues com per la nostra aplicació, obrint-nos tot un món de possibilitats i mantenint baixa la complexitat de tota l'arquitectura.
Posar en marxa tot el sistema implica:
- Desenvolupar l'aplicació pensant en les tasques
- Configurar Redis
- Executar els workers
- Executar celerybeat per les tasques periòdiques si en tenim
- Executar el monitor
I òbviament anar monitoritzant-ho tot. Que ho disfruteu!
Traducciones/Translations by apertium
1 comentari, 0 trackbacks (URL) , Tags: Python Django
Fer servir un ORM o no
Escrit per Aaloy a 19 de March , 2011 a les 12:49 p.m.
Arrel del darrer post s'ha establert un nou fil relacionat amb la conveniència o no de fer servir un ORM en les nostres aplicacions quan l'sql directe pot ser més eficient. En Jan Carreres, el "responsable" d'aquest article fa una sèrie d'apreciacions que crec que donen per un nou post, més que res per poder mantenir la discusió en baix el seu propi concepte.
Així doncs acomodeu-vos al seient, que començam ...
Però què dimonis és un ORM
ORM són les sigles de Object Relational Mapping, és a dir, s'anomena ORM a aquella llibreria o procés que fa la conversió d'estructures de dades relacionals cap a estructures de dades orientades a objectes. És a dir, passam de fer feina amb taules i files a fer feina amb classes i instàncies d'aquestes classes. Passam de fer feina de camps a fer feina amb propietats dels objectes.
No hi ha una única aproximació als ORM. Christian Bauer i Gavin King al llibre Hibernate in Action, anomenen quatre característiques comuns a tot ORM:
- Una API per executar operacions CRUD (Create, Retrieve, Update, Delete) en els objectes.
- Un llenguatge o una API per especificar les consultes que fan referència a les classes i a les propietats de les classes.
- Utilitats per especificar les metadades del mapeig entre el model relacional i el model d'objectes.
- Una tècnica dins l'ORM per crear transaccions, fer assosicacions
lazyi altres tipus d'optimitzacions.
A partir d'aqui la implementació de cada ORM potser completament diferent. Els autors, citant a Mark Fussel proposen la classificació dels ORMs en quatre tipus.
-
Purament Relacionals. L'ORM es construeix al voltant de la base de dades i es específic per a la nostra aplicació. Té l'avantatge que es pot optimitzar molt, però és difícilment portable entre bases de dades i presenta dificultats de manteniment. És típic d'aplicacions que tenen molta lògica dins la BD en forma de procediments amagatzemats.
-
Mapeig fi. Les entitats són representades com a classes i es mapegen manualment cap a les taules del model relacional. Un exemple d'aquest cas serien les aplicacions Java que fan ús del SQL/JDBC o bé en el cas de Python les que fan servir directament la DB-API de Python.
3. Mapeig mig. L'aplicació es dissenya al voltant del model d'objetes. És a dir, la base de dades és ara un suport per a la persistència dels objectes quan abans era gairebé la peça fonamental de l'aplicació. Aquest tipus d'ORM són especialment interessants quan l'aplicació té un tamany mitjà, amb transaccions complexes i sobretot quan la portabilitat entre bases de dades és un requeriment de l'aplicació.
4. Mapeig complet. S'hi arriba quan l'ORM suporta les característiques més importants i pròpies del model orientat a objectes: composició, herència, polimorfisme, ... i les implementa de manera transparent cap a la base de dades.
Si estam dins el món Java podríem dir que Hibernate està gairebé en el 4 d'aquesta classificació, Ibatis entre el 2 i el 3. En el cas de Python l'ORM de Django estaria començant a entrar en el 4 i SQLAlchemy estaria fregant el nivell d'Hibernate.
Per què fer servir un ORM
Per començar dir que un ORM no és una excusa per no saber SQL. Quan un fa feina amb aplicacions de bases de dades relacionals s'ha de conèixer al manco l'SQL estàndard, tenir molt clars els conceptes de taula, registre, selects, unions, claus primàries, etc. Es dóna per suposat que el tema de la normalització ja ho tenim ben clar.
El que fa un ORM és facilitar-nos la vida. Quan la nostra aplicació ja no es trivial, sinó que fa un ús intensiu de consultes, manipulacions de resultats, insercions, ens trobarem repetint una i una altra vegada el procés de conversió dels nostres objectes cap a sentències SQL. L'ORM fa tota aquesta feina per nosaltres, així que una de les raons més importants per a fer servir un ORM enlloc de sentències SQL a pèl per la nostra aplicació és la productivitat i evitar el DRY al llarg de la nostra aplicació.
Fer servir un ORM implica escriure menys codi nosaltres mateixos i sovint que el codi que hem escrit sigui més bo de llegir. Això es tradueix en una millor mantenibilitat de l'aplicació. Jan diu que si un es dedica a fer apliccions de BD ha de conèixer l'SQL i és veritat, però no té per què tenir a la ment tots els camps que hi ha a la taula. Imaginem-nos un entorn amb molts programadors fent feina a la mateixa aplicació. Aplicació amb moltes taules i amb taules amb molts de registres. Picant SQL a pel és tenim molts números de deixar-nos un camp a l'hora de fer una consulta. L'ORM manté la llista de camps per nosaltres, quan accedim a un objecte el ja se n'encarrega de fer les selects oportunes a la base de dades i obtenir-ne tots els camps.
Encara que hi ha casos en que escriure el codi SQL directament pot significar un guany considerable en el rendiment d'una consulta concreta, el més habitual és que l'ORM que facen servir ja tengui considerat dins la seva estructura les optimitzacions més habituals, de manera que l'sql que es genera pot ser fins i tot més eficient que el que generaríem a mà. Pensar que nosaltres sempre generarem millors sentències SQL és si més no agoserat, i per mi representa una optimització prematura. L'avantatge de productivitat i mantenibilitat que ens dóna l'ORM és el que s'ha de considerar primer. Després sempre hi som a temps d'optimitzar, però els nostres esforços s'han de concentrar en aquelles consultes o accions que torben més temps o que s'executen més sovint. És a dir, l'ORM i l'augment de productivitat que implica ens permet guanyar temps i poder optimitzar allà on interessar realment.
Molt relacionat amb tot això ens trobam la navegabilitat. La navegabilitat que ens permet el model orientat a objectes és d'aquelles coses que xoquen més amb el que ens proporciona el model relacional. Els ORM permeten navegar entre les associacions d'objetes de manera transparent, generant les sentències necessàries i sovint permetent-nos optimitzar aquesta navegabilitat, els select_related de Django n'és un exemple, o les cachés d'Hibernate. Pensem amb el senzill que és posar un objecte dins una plantilla i anar accedint a les seves propietat, anar fins a la propietat que representa una clau forana de la base de dades, fer-ne referència i obtenir-ne les propietats de l'objecte associat. Ara pensem en la quantiatat de codi SQL que ens hem estalviat.
I finalment l'altra gran motiu per fer servir un ORM és el d'independitzar-nos de la base de dades. Podria parèixer que quan un coneix SQL ja pot fer anar qualsevol base de dades, però realment això no és així, cada BD té petites diferències en la implementació de l'SQL que fan que sovint el codi SQL no sigui directament portable entre bases de dades. Un ORM crea una capa d'abstracció entre la nostra aplicació i la base de dades, generant i utilitzant les sentències SQL més adients a cada una.
L'argument que "un tria una BD i l'utilitzes sempre" per la meva experiència passa poc. Una empresa gran pot triar Oracle, però segurament per motius de cost algunes aplicacions les voldrà amb un altra BD. Si comercialitzam una aplicació limintar-nos a una base de dades concreta limita les possibilitats de comercialització. Fent servir un ORM podem desplegar la nostra aplicació contra diferents clients i fins i tot contra diferents versions d'una base de dades.
Resistir la temptació
Quan un comença a programar aplicacions de gestió és difícil resistir la temptació d'optimitzar-ho tot, d'escriure sql a pel optimitzant-ho per la nostra base de dades, és cert, però resistiu!
Si feim sql a pel a poc que l'aplicació creix el procés serà el seguent:
-
Ens adonarem que tenim sql repartits per tot. Així que farem una refactorització i posarem tot l'sql dins un sols arxiu que contindrà totes les funcions.
-
Després ens adonarem que aquest arxiu té molt de codi repetit, feim sempre la conversió de taules a objectes i ho refactoritzarem per a no repetir codi.
-
Després ens adonarem que feim sovint les mateixes consultes i que també es pot refactoritzar.
-
Seguirem i seguirem refactoritzant....
Al final acabarem amb un ORM del tipus 2 que ens haurà duit una feinada en refactoritzacions i que no té encara els avantatges d'un ORM del tipus 3 ó 4.
Costa resistir la temptació, fa peresa estudiar una llibreria d'ORM, però pensem que probablement el camí que seguirem serà aquest i s'ho paga l'esforç inicial.
Traducciones/Translations by apertium
2 comentaris, 0 trackbacks (URL) , Tags: Informàtica Java Python Django PostgreSQL
Solapament d'intervals ara amb Django
Escrit per Aaloy a 12 de March , 2011 a les 12:05 p.m.
Segueixo amb la vena friki, ahir damunt el solapament d'intervals de mode genèric i avui veurem la implementació d'aquesta idea a un model Django.
class Oferta(models.Model):
"""Una oferta que va per rang"""
nom = models.CharField(max_length=200)
inici = models.DateField()
fi = models.DateField()
activa = models.BooleanField(default=True)
class Meta:
verbose_name="Oferta"
verbose_name_plural="Ofertes"
def __unicode__(self):
return self.nomEl model com veis és molt senzill, una oferta pot estar activa o no i pertany sempre a un rang de dates. Queda fora de l'exemple la determinació de si admetem solapament de dates per una oferta, que es fareia de manera semblant al la cerca que ara farem.
La idea és que hem d'aconseguir que amb l'ORM de Django nodelar una consulta del tipus not ((x1<y0) or (y1<x0)).
Els filtres per defecte el que fan es un and, per la qual cosa no ens serveixen directament. El que farem és utilitzar una característica de l'ORM anomenada funció Q que ens permete afegir condicons de filtratge múltiples i fer que vaign per OR enlloc de per AND.
from django.db.models import Q
Ara sols queda fer la consulta. Particularment si són consultes que poden ser susceptibles de fer-se servir a varis llocs o que estan molt relacionades amb el model, m'agrada lligar-les al model amb un manager ad-hoc o bé amb un mètode estàtic, que és el que farme ara. Així:
@staticmethod
def ofertes_entre(pInici, pFi):
return Oferta.objects.filter(activa=True). \
exclude(Q(fi__lt=pInici)|Q(inici__gt=pFi)).order_by('-inici')Fitxem-nos que el NOT de la consulta s'aconsegueix amb l'exclude i que feim servir la fució Q per afegir les dues condicions de comparació dins aquest filtre i les lligam per OR (|).
He afegit un grapat de dades d'exemple, i executat la consulta, que queda com
Oferta.ofertes_entre(f1, f2)
Ho he fet des de línia de comandes, de manera que ara puc veure la query que es genera:
>>from django.db import connection >>>connection.queries
'sql': u'SELECT "oferta_oferta"."id", "oferta_oferta"."nom",
"oferta_oferta"."inici", "oferta_oferta"."fi", "oferta_oferta"."activa" FROM
"oferta_oferta" WHERE ("oferta_oferta"."activa" = True AND NOT
(("oferta_oferta"."fi" < 2011-03-20 OR "oferta_oferta"."inici" > 2011-04-20 )))
ORDER BY "oferta_oferta"."inici"
Com m'agrada Django!
Traducciones/Translations by apertium
6 comentaris, 0 trackbacks (URL) , Tags: Python Django
Karma
Escrit per Aaloy a 28 de February , 2011 a les 11:51 p.m.
Aquests dies estic bastant engrescat i enfeinat amb projectes molt relacionats amb Python i Django. Per una banda estam definit i creant el que serà la segona fase del projecte Clickote, una aproximació simpàtica a les reserves d'hotel en la qual estam passant molt de gust fent-hi feina. Per una altra banda estam migrant un aplicació web feta inicialment amb ezPublish, un CMS dels de tota la vida, cap a Django.
Clickote és un projecte en el qual m'està agradant molt fer-hi feina perquè a més del projecte i la tecnologia en sí, ve a demostrar que client i proveïdor de serveis no són antagonistes, sinó que són col·laboradors. Amb l'equip de Clickote ens hi sentim molt bé fent-hi feina. Hem topat amb gent que no tan sols coneix el negoci, sinó que a més és conscient de la part tecnològica i de les implicacions en temps i esforç del que demanen, i sempre hem arribat a solucions beneficioses per al projecte.
La migració d'un lloc web des del CMS ezPublish cap a un CMS fet a mida amb Django és també tot un repte i representa també tot una aposta de futur per part del client. A les primeres etapes del canvi ja hem notat un augment del rendiment en temps de renderització de les planes d'un 30% de l'aplicació Django, motivat fonamentalment pel gran grau de control que es té a Django damunt el que es passa a la plantilla i com es passa. ezPublish no és un sistema MVC i quan hi ha lògica complexa pel mig, es queda molt curt comparat amb les possibilitats que et dóna Django. La creació d'un middleware ad-hoc, tags, l'herència de plantilles, la facilitat per crear serveis Ajax, ... Django com a CMS no es posa en el teu camí, ben al contrari, t'ajuda a crear l'aplicació i a optimitzar-la. Quan fas una migració com aquesta entens ben bé la frase del que el bastiment Django és per a "perfeccionistes amb dates d'entrega". Encara ens hi estarem un grapat de setmanes a la migració, en acabar esper poder posar-ho com a exemple de cas d'èxit, o si més no d'aprendre'n de l'experiència.
El cap de setmana passat vaig estar llegint "Maldito Karma" de David Safier, una lectura divertida amb cert tocs d'humor àcid que és llegeix molt bé, com un vi blanc jove fresquet a una tarda calorosa d'estiu.
No sé si serà cosa de la influència del llibre o no, però això de poder fer feia amb el que m'agrada, amb companys i clients com aquests, segur que em fa venir bon karma.
Traducciones/Translations by apertium
0 comentaris, 0 trackbacks (URL) , Tags: Llibres i revistes Python Django
Actualitzant coses
Escrit per Aaloy a 30 de January , 2011 a les 11:23 a.m.
Aquesta setmana he estat força entretingut, m'he estat mirant un grapat de llibreries i utilitats, algunes d'elles noves versions que han anat evolucionant i millorant al llarg del temps. També vaig tenir l'oportunitat d'assistir a la xerrada de Maria Antònia Mas Pichaco damunt la guia PMBOK®, gràcies a l'empenta de Paco que em va recordar que es donava la xerrada. A part de la conferència en sí, també va estar molt bé el cafetó i la xerrada amb Paco, això de contar batalletes i compartir opinions no s'ha de perdre Paco! :)
Xerrada de PMBOK®
Si anam per ordre el primer seria parlar de la xerrada de Maria Antònia. A diferència d'altres assistents que l'havien tinguda com a professora, era la primera vegada que l'escoltava. Em va agradar, la exposició va ser clara i entretinguda. M'hauré de llegir la guia per poder-ne fer una avaluació en profunditat, però pel que vaig entendre es tracta d'un conjunt de recomanacions i millors pràctiques que cobreixen el cicle de la gestió de projectes, però que en cap cas ens marca un camí a seguir o una metodologia. Supòs que la utilitat vindrà quan ja hagis elegit com gestionar un projecte.
En la part anecdòtica me va fer gràcia quan un dels punts de la gestió del projecte consistia en "triar l'equip de gent". Em va recordar a aquelles pel·lícules on l'heroi va cercant l'equip en els llocs més inversemblants, sempre els millors en el que fan, per conformar un equip invencible. Bé, la realitat és que poques vegades tens ocasió de fer això, però quan pots és molt engrescador. El més normal és que l'equip et vengui donat i llavors el que s'ha de fer és tractar d'esbrinar quines mancances i quines aptituds té cada membre de l'equip per arribar a dur a terme el projecte. Seguint amb el símil fílmic, és mes semblant a les pel·lícules on l'entrenador agafa un equip que no coneix i el duu cap a la fita proposada (sempre amb final feliç).
Em va dur molts records una altra frase "un projecte ha de tenir una data de finalització", ja que és una discussió que havia tingut en algunes ocasions amb certa gent. Per mi un projecte comença, es desenvolupa i acaba, tal com també va dir Maria Antònia, tot el que no té dada de finalització entra dins la part d'operacions.
LibreOffice
Al portàtil de casa he substituït ja l'OpenOffice per LibreOffice, la migració va ser molt senzilla i per ara no he detectat cap problema. Pareix més ràpid i no dóna problemes amb el Firefox. No sé si me passa a mi sols, però amb l'OpenOffice i el Firefox en marxa les operacions de tallar i aferrar es feien eternes, ara això no passa. També he trobat millores curioses, com la possibilitat de tenir colors a les pipelles de les fulles de càlcul, opció que ja tenia el Quattro Pro de Borland i que el pas en massa de les empreses cap a Excel va deixar a l'oblit. Com a altra avantatja la millora a la pantalla de càrrega que li dóna un aire un poc més fresc i la millora en la navegació.
VirtualBox 4
He actualitzat el programa VirtualBox a la darrera versió, l'actualització des de la 3.2 és trivial i no s'han perdut màquines virtuals. El temps d'arrencada de l'aplicació i de les màquines virtuals ha millorat molt i també ho ha fet la interfície gràfica d'administració. A més ara té la possibilitat de connectar-se en remot a una màquina virtual mitjançant el protocol RDP, bé en sessió única o en multi sessió. Així dins una Ubuntu 10.10 tenia dues màquines virtuals, una Ubuntu 10.04 i un Windows XP (l'original que va venir amb el portàtil), aquest XP estava connectat via RDP a la màquina virtual Ubuntu i s'hi podi interactuar veient l'arrancada i tot. Des de l'altra ordinador, el PPC, també vaig connectar via remote desktop a la màquina virtual Ubuntu. Divertit i amb moltes possibilitats.
Sorl thumbnail
Sorl es una llibreria per a la gestió d'imatges per Django. És una reescriptura d'una versió anterior pràcticament des de zero. Incorpora totes les possibilitats de la versió anterior però ara de manera molt més transparent i entenidora. Han millorat com es fa caché de les imatges incorporant Redis com a opció de magatzem de claus, opció per distingir el tipus d'orientació de la imatges i un control per determinar si la imatges existeix o no (per exemple en remot quan dóna un 404) i actuar en conseqüència.
A les properes setmanes esper poder parlar de la sortida de Django 1.3 i de Celery. No sé què em fa més patxoca. Per una part Django 1.3 significarà que ja s'avança cap al 1.4, versió que està previst que incorporarà moltes més novetats que la 1.3 que és una versió més de correcció de petits errors i en general petites funcionalitats. Celery, la llibreries de cues per Python i Django també treurà aviat una nova versió. N'estic molt pendent ja que incorpora compressió de missatges, autoescalat, un nou protocol més eficient, ... Per ara m'he llegit la documentació i he de començar a preparar algunes de les nostres aplicacions que fan servir la llibreria migrant a l'actual Release Candidate i veure'n el camí de migració. Ja en xerrarem!
Traducciones/Translations by apertium
0 comentaris, 0 trackbacks (URL) , Tags: Gestió de projectes Python Django
Primer apunt del 2011
Escrit per Aaloy a 23 de January , 2011 a les 12:25 p.m.
Primer apunt del 2011
Primer apunt d'aquests any. Se diu aviat quan gairebé ja ha passat un mes del 2011. Aquesta aturada per festes ha suposat també una aturada en els pots d'aquest blog. No vull dir que escriure sigui una rutina més, però sí que requereix de moments de tranquil·litat davant de l'ordinador, de reorganitzar idees, i amb les festes i amb tot el que queda pendent a l'inici d'any per mor de les festes, doncs tot s'acumula i els moments propicis per escriure al blog són molt limitats.
Així que aquest primer apunt implica d'alguna manera tornar a la normalitat després de festes, posar en ordre els projectes i també fer un petit resum de l'any passat, que com a nota històrica doncs tampoc queda tan malament. Als soferts lectors ja us aviso que aquest apunt va de batalles, buabulància, projectes, ... Un sac on s'hi pot trobar de tot, bé si fa no fa com als resums anuals que fan per les teles, però sense els cops i les caigudes.
Com molts sabeu el 2010 va ser l'any en que vaig deixar una feina estable a Tui España (després Hotelbes) per dedicar-me junt amb altre(s) socis a fer feina a temps complet per a la nostra pròpia empresa). Vist amb un any de perspectiva he de dir que no tot són flors i violes, això de tenir un sou fix cada més està força bé, però també es veritat que poder fer feina en projectes amb la tecnologia que t'agrada i de la manera que t'agrada crec que ho compensa de sobres.
També he pogut veure que moltes de les coses que comenta la gent autònoma o les petites empreses és totalment cert. Crec que es creen micro-empreses i autònoms a pesar de l'administració i no gràcies a ella. Tenir que avançar l'IVA de factures que encara no has cobrat (i que potser no cobraràs) és l'exemple més clar, però també ens n'hem trobat més: disposicions vers la comunicació electrònica de l'administració que sols funcionen amb segons quins sistemes operatius, concursos públics que demanen un programa específic d'un proveïdor concret (no fa falta dir quin, veritat?), subvencions que se'n van a empreses que no ho necessiten, plans avanza que impliquen que siguis una gran empresa per presentar-hi i ja pensats per a que les empreses se n'aprofitin i que juguen al gat i al ratolí amb les normes. Pens que tot hauria de ser més fàcil, en Varsavsky a alguns apunts del seu blog quan parla de la crisi i de com està organitzada la creació d'empreses a Espanya dóna bastant al clau.
També potser totes aquests qüestions burocràtiques tenen una raó de ser, que la gent té per hobby defraudar tot el que pot a Hisenda i a l'Estat (els recents i nombrosos casos de corrupció que s'han destapat a les nostres Illes aquest darrer any així pareix que ho demostren), però també crec que molta paperassa que es fa té per objectiu justificar la pròpia burocràcia i la seva ineficiència. Amb la quantitat d'informació que té l'Estat damunt nosaltres no té sentit que cada organisme torni a demanar la mateixa paperassa, i que se suposi que tothom és un potencial defraudador i es legisli en conseqüència, és a dir, no amb la intenció de fer més fàcil la creació d'empreses i la seva gestió, sinó amb la intenció de fer més fàcil la feina d'inspecció i control, passant la càrrega de feina (i sovint de la prova) cap a l'empresa.
Per exemple, una de les normes suposa que per una micro-pyme com nosaltres hem de fer que els 85% de beneficis vagin com a factures dels socis, la conseqüència és que a principis d'any l'empresa es queda pràcticament descapitalitzada i sense marge de maniobra per escometre inversions (ja siguin de renovació de mobiliari o de projectes propis). Entenc que és una mesura per evitar el frau, però que fa molt difícil la vida a les micro-empreses que començam.
Però no tot és negatiu, la veritat és que la Incubadora del Parc Bit és una gran ajuda per gent com nosaltres i el personal d'allà està fent molt bona feina. També a poc a poc vas notant alguns canvis positius: que l'Ibit organitzi un curs de qualitat d'OpenERP va ser una notícia prou bona pels que creim que el programari lliure és una opció de futur pel nostre teixit empresarial. Les empreses ja no els estranya que els diguis que fas feina exclusivament amb programari lliure i que els desenvoluparàs una aplicació web que correrà damunt Linux. Potser és la crisi, potser és un canvi de mentalitat o tot plegat, però al manco es veu que alguna cosa està canviant de manera lenta però inexorable.
Els projectes del 2010 han estat força entretinguts, hem tingut un gran projecte: la migració i renovació de la web del grup Hoteler Fiesta. Tot i que algunes vegades hem tingut que adaptar-nos al que ens deixava fer el CMS "legacy" més que al que nosaltres ens hagués agradat, el treball va ser molt engrescador i va suposar un repte tècnic important.
Hem pogut fer feina amb Python i Django amb molts projectes. Des de webs presencials com la de Clima Insular a projectes b2c com són els projectes de Globalbooking i Clickote. Hem fet feina amb Python i Django, i afinat les configuracions de sistemes per fer que les aplicacions tenguin un rendiment semblant a webs que es gastes varis ordres de magnitud més en el seus desenvolupaments. Personalment m'ho he passat d'allò més bé en veure com una aplicació com Celery i RabbitMQ pot donar un joc tan gran en el desenvolupament web. En poques paraules, m'he divertit, ens hem divertit amb la feina.
El 2011 es presenta també molt interessant. Tenim en cartera projectes per fer webs de venda per alguns hotels que crec que poden quedar molt bé. No ens les donam de gurús i no tenim el marketing d'altres, però mira som així i no hi podem fer res. L'altra dia a un twitt un conegut gurú del marketing hoteler deia que eren els millors perquè el seu cms podia fer feina amb múltiples idiomes, com si l'Unicode l'hagués descobert ell. Fa ja prop de tres anys nosaltres amb Python i Django posàrem la web d'UltramarExpress en producció en xinès i ens va parèixer la cosa més normal del món, crec que no ens sabem vendre bé. També és veritat que potser també tenim més coneixements tècnics per a saber què és l'Unicode, l'UTF-8 i per distingir la traducció de la localització. Em sap greu, però aquests tipus de venda de fum m'indignen!
Em fa moltes ganes seguir fent feina amb l'arquitectura d'Amazon, amb els preus a que s'han posat les micro-instàncies, fer experiments o posar serveis dedicats és molt barat. Un dels primers que crec que posarem serà un servidor dedicat per Git. Estam investigant si hi ha alguna cosa opensource que ens faciliti la tasca d'administració, però el que és segur que tenir un servidor propi per control de versions damunt Amazon per a una empresa té un cost realment ridícul. En la nostra feina diària feim servir fonamentalment el subversion, però Git (o Mercurial) tenen molts avantatges com per a no intentar fer el canvi. Representa però una manera de fer feina que requereix una adaptació i aquesta ha de ser el menys traumàtica possible. Feim servir Trac per a gestionar projectes i hem d'avaluar el suport de Git o pensar en migrar a Redmine que pareix que el té molt més madur. Com dic, també estaria molt bé tenir una eina web potent d'aministració dels repositoris i dels usuaris, més que res com a una opció de futur.
El 2011 també es presenta molt interessant en el món Python. Supòs que les distribucions més populars ja vindram amb el Python 2.7 i això facilitarà la migració cap a la versió 3.x de Python. La setmana passada pareix que se va arreglar l'entrebanc més important que hi havia amb el mòdul wsgi per la versió 3 i això vol dir que els principals frameworks Python podran utilitzar Python 3 i desplegar-se sense problemes. Seria molt agoserat dir que el 2011 serà l'any en que Python 3 serà l'estàndard per al desenvolupament web, però sí que crec que es portara a Python 3 les principals llibreries i que el 2012 (si no s'acaba el món) un ja tindrà pocs dubtes de si programa amb Python 2 o 3, directament ho farà amb el tres.
El 2010 Python ha guanyat molt acceptació dins la comunitat de programadors. L'índex TIOBE i el premi al llenguatge de l'any són una bona mostra i el 2011 crec que anirà pel mateix camí. Cada cop veus més utilitats, aplicacions web i web fetes amb Python i algun framework (normalment Django). Les utilitats de desenvolupament web per Python cada cop són més potents i a la vegada fàcils de fer anar, amb línea amb la filosofia del llenguatge. He passat d'utilitats de testeig com pyUnit a fer feina amb nose, que fa que crear tests unitaris per testejar aplicacions no sigui molt més complexe que escriure els petits scripts que tanmateix ja feia. Nose permet reutilitzar la feina que ja feim i formalitzar les proves que hauríem fet de totes maneres. South per Django és una altra d'aquestes petites joies que ha arribat a la maduresa. L'actualització del model de dades per Django és trivial amb South i cobreix pràcticament el 99% dels casos. A l'autor li han proposat sovint que South formi part del Core de Django però ell diu que encara no està totalment satisfet i que per això vol mantenir-ho com a projecte separat. Pareix que vol arribar a la perfecció absoluta!
A més de per les ganes que me feia poder fer feina a la meva pròpia empresa, una de les raons que me dugueren a deixar Hotelbeds, no ho negaré, va ser que volia poder desenvolupar projectes amb Python i Django com fins aleshores ho havíem fet a TUI España. Crec que és un dels grans problemes de Python, que una vegada que hi fas feina tornar a altres llenguatges és molt difícil. Crec que va ser i és encara una decisió arriscada, però fer feina amb el que te grada sovint ho és.
La versió 1.3 de Django s'espera d'aquí uns mesos. Serà una versió de transició, orientada a corregir bugs i afegir petites millores de funcionalitat que quedaren com a tickets pendents d'integrar a la versió 1.2. Tot i això hi haurà millores com la millora en el maneig de contingut estàtic, que ha passat de ser una aplicació independent a estar integrada en el core.
Fa pocs dies al twitter i a la llista de desevolupament s'anunciava que es passava a Transifex per a les tasques de traducció. Això és una molt bona notícia. Transifex és d'aquests projectes que poc a poc van calant dins els món del desenvolupament per la seva senzillesa i eficàcia. Una altre bon exemple seria també el d'Sphinx per a la documentació d'aplicacions, o fins i tot una aplicació web molt recent, el readthedocs que s'està convertint en una web ideal on deixar la documentació dels nostres projectes online.
Al 2011 també hi haurà la possibilitat d'anar a la Django Conference que enguany es fa a Holanda, un destí relativament proper i econòmicament assequible. Si l'economia m'ho permet me fa moltes ganes anar-hi, serà una bona oportunitat de conèixer gent a la que segueixo per les llistes de Django i/o Twitter i veure com afronten ells la realitat empresarial.
També crec que pot ser un bon any per implantar OpenERP a l'empresa. El curset de l'Ibit ens va servir per adonar-nos realment de tot el seu potencial. Ja ha sortit la versió 6.0 i encara que no està completament localitzada trob que està prou madura per poder-hi començar a fer feina internament i potser d'aquí algunes setmanes/mesos començar a fer alguns projectes per tercers. OperERP està fet en Python i per tant moltes de les eines i tècniques que ja feim servir són aplicables al desenvolupament d'aplicacions de negoci damunt OpenERP.
Realment no sé si el 2011 representarà el final de la crisi, si tots els projectes que hi ha en expectativa es tancaran finalment i tindrem un any bo. Com que me conec sé segur que no deixaré de passar pena: per la feina, per les factures, pel local, pels projectes,... però és el meu tarannà. La diferència és que abans me creava molta ansietat el saber que les coses es podrien fer d'una manera millor i no tenir l'oportunitat de canviar les coses, ara aquesta ansietat no hi és, perquè al manco tenc l'oportunitat d'intentar-ho.
Traducciones/Translations by apertium
5 comentaris, 0 trackbacks (URL) , Tags: Informàtica Linux Python Django APSL
Celery: mini tutorial
Escrit per Aaloy a 28 de November , 2010 a les 6:55 p.m.
Configuració del servidor rabbitmq
El servidor Rabbitmq és qui rebrà les ordre d'execució de les tasques i les enviarà als workers que han de fer la feina i rebrà el resultats de les operacions de manera que el procés que ha donat l'ordre d'execució de la tasca el pugui fer servir.
És important destacar que a l'hora de donar l'ordre d'execució podem decidir si el resultat s'ha de guardar o bé s'ha de descartar. Si el resultat no és necessari (pensem per exemple en l'enviament d'un e-mail) llavors convé marcar la tasca per a que el resultat es descarti.
sudo aptitude install rabbitmq-server
Els paquets nous següents s'instal·laran:
erlang-os-mon{a} erlang-snmp{a} rabbitmq-server
0 paquets a actualitzar, 3 a instal·lar, 0 a suprimir i 5 a no actualitzar.
Es necessita obtenir 1398kB d'arxius. Després del desempaquetat s'utilitzaran 3187kB.
Esteu segur de voler continuar? [Y/n/?] y
Obté:1 http://es.archive.ubuntu.com/ubuntu/ maverick/main erlang-snmp i386 1:13.b.3-dfsg-2ubuntu3 [581kB]
Obté:2 http://es.archive.ubuntu.com/ubuntu/ maverick/main erlang-os-mon i386 1:13.b.3-dfsg-2ubuntu3 [77,4kB]
Obté:3 http://es.archive.ubuntu.com/ubuntu/ maverick/main rabbitmq-server all 1.8.0-1ubuntu2 [739kB]
S'han obtingut 1398kB en 2s (480kB/s)
S'estan preconfigurant els paquets...
S'està seleccionant el paquet erlang-snmp prèviament no seleccionat.
(S'està llegint la base de dades … hi ha 118931 fitxers i directoris instal·lats actualment.)
S'està desempaquetant erlang-snmp (de …/erlang-snmp_1%3a13.b.3-dfsg-2ubuntu3_i386.deb) …
S'està seleccionant el paquet erlang-os-mon prèviament no seleccionat.
S'està desempaquetant erlang-os-mon (de …/erlang-os-mon_1%3a13.b.3-dfsg-2ubuntu3_i386.deb) …
S'està seleccionant el paquet rabbitmq-server prèviament no seleccionat.
S'està desempaquetant rabbitmq-server (de …/rabbitmq-server_1.8.0-1ubuntu2_all.deb) …
S'estan processant els activadors per a man-db …
S'estan processant els activadors per a ureadahead …
S'està configurant erlang-snmp (1:13.b.3-dfsg-2ubuntu3) …
S'està configurant erlang-os-mon (1:13.b.3-dfsg-2ubuntu3) …
S'està configurant rabbitmq-server (1.8.0-1ubuntu2) …
Adding group `rabbitmq' (GID 123) ...
Fet.
Adding system user `rabbitmq' (UID 115) ...
Adding new user `rabbitmq' (UID 115) with group `rabbitmq' ...
Not creating home directory `/var/lib/rabbitmq'.
Starting rabbitmq-server: SUCCESS
rabbitmq-server.Amb això hem aconsegui instal·lar i posar en marxa el RabbitMq en un sistema Ubuntu 10.10.
Ara ens queda configurar, afegirem un usuari i un vhost per a que accepti els treballs,
a més hem de donar permisos a aquest usuari per a que que pugui enviar treballs
al vhost creat.
Afegim l'usuari:
$ sudo su $ rabbitmqctl add_user django password Creating user "django" ... ...done.
Cream l'vhost
$ rabbitmqctl add_vhost test Creating vhost "test" ... ...done.
Assignam els permisos a l'usuari per a quest vhost
$rabbitmqctl set_permissions -p test django ".*" ".*" ".*" Setting permissions for user "django" in vhost "test" ... ...done.
Ja tenim la part més complexa llesta. El servidor RabbitMQ pot estar en la mateixa màquina o en una totalment distinta de la que llançarà la tasca o de la màquina de l'executarà.
Preparant l'entorn
La gràcia del Celery és que ens permet llançar les tasques i executarles mantinguent la cohesió de la nostra aplicació. És a dir, si estam treballant amb Django Celery ens permet manetenir el codi de cridada i d'execució de la tasca des de la mateixa aplicació o des del mateix projecte.
Per començar convé que instal·lem Celery. Ho farem des de un entorn virtual,
per això convé tenir instal·lats els paquets virtualenv i virtualenvwrapper.
sudo pip install virtualenv virtualenvwrapper
i configurar el virtualenvwrapper.
Crear l'entorn, de nom per exemple celery i instal·larem el paquet celery
mkvirtualenv celery workon celery pip install celery
Que sigui un projecte Django o Python pur té molt poques diferències. En el cas de Django hem d'instal·lar el paquet django-celery i configurar el projecte. Així a les nostres aplicacions afegirem "djcelery"
INSTALLED_APPS += ("djcelery", )I també és important afegir les següents línies al settings.py
import djcelery djcelery.setup_loader()
La resta es comú tant a un projecte Python que a un projecte Django, sols que
les configuracions en el primer cas aniran a un arxiu anomenat celeryconfig.py
i en el cas de django la configuració pot anar dins el settings.py
Per la nostra primera tasca crearem un project Python simple i l'arxiu
de configuració celeryconfig.py contindrà
#-*- coding: UTF-8 -*-
BROKER_HOST = "192.168.1.10"
BROKER_PORT = 5672
BROKER_USER = "django"
BROKER_PASSWORD = "password"
BROKER_VHOST = "test"
CELERY_RESULT_BACKEND = "amqp"
CELERY_IMPORTS = ("tasks", )
CELERY_SEND_EVENTS=TrueEl prefixe BROKER fa referència al servidor RabbitMq que acabam d'instal·lar. Celery pot fer servir difernts tipus de Brookers pero RabbitMq és el més recomanat.
Fitxau-vos que el configuram amb els paràmetres amb els quals hem creat el vhost, i amb l'usuari i password creats a RabbitMq.
CELERY_RESULT_BACKEND serveix per a indicar a Celery on s'han de deixar els resultats. Podem tenir distints backends, des de Memcached, a una base de dades o una base de dades NoSQL.
CELERY_IMPORTS indica a l'aplicació que tasks.py conté les tasques que
s'han d'executar. Bé, de fet el que fa és indicar al daemon de celery
quines són les tasques a exeuctar quan es posi en marxa, però també pot
fer-se servir per altres tipus d'inicialitzacions.
CELERY_SEND_EVENTS li diu a Celery que volem monitoritzar els events que es produeixin. Si no posam aquesta ordre i després volem saber què esta passant ho tendrem molt més complicat.
Creant la nostra primera tasca
Per cerar la nostra primea tasca crearem una arxiu anomenat tasks.py
#-*- coding: UTF-8 -*-
from celery.decorators import task
@task(name='tasks.add')
def add(x,y, **kwargs):
logger=add.get_logger(**kwargs)
logger.info('Entrant a la tasca ...')
return x+yFitxem-nos que és codi Python del més normalet, sols que hem afegit un
decorador tasks per marcar la funció com a tasca i posar-li el nom. Aquest
nom és opcional, però convé posar-li ja que així evitam que celery li posi
un nom per defecte que podria fer menys portable la nostra aplicació.
També podem veure como podem enviar informació a logger des de la tasca.
Executant el worker
Ara ja podem anar al nostre projecte, si tot ha anat bé tindrem dos arxius:
tasks.py i celeryconfig.py
Executam: celeryd -l info
[2010-11-28 18:44:20,949: WARNING/MainProcess] celery@D820 v2.1.3 is starting.
[2010-11-28 18:44:20,950: WARNING/MainProcess]
Configuration ->
. broker -> amqp://django@192.168.1.10:5672/test
. queues ->
. celery -> exchange:celery (direct) binding:celery
. concurrency -> 2
. loader -> celery.loaders.default.Loader
. logfile -> [stderr]@INFO
. events -> ON
. beat -> OFF
. tasks ->
. tasks.add
[2010-11-28 18:44:20,960: INFO/PoolWorker-1] child process calling self.run()
[2010-11-28 18:44:20,962: INFO/PoolWorker-2] child process calling self.run()
[2010-11-28 18:44:20,963: WARNING/MainProcess] celery@D820 has started.amb això hem creat el nostre primer worker preparat per a executar la tasca que
li diguem (i que estarà dins tasks.py). Per aquest article hem deixat el
worker dins al consola, però es pot deixar com a dimoni. Consulteu el vostre
administrador de sistemes de capçalera per a una configuració acurada. El paràmetre
que li passa indica que volem que generi log en mode info. Fixem-nos que també
ens informa de les tasques disponibles i del nivell de concurrència que tenim, 2
en el meu cas, que és el nombre de processadors del portàtil.
I executam la tasca
Des d'una altra consola executarem l'interpret de Python
>>>from tasks import add >>>result = add.delay(2,3)
Si ara observam la consola del worker veurem alguna cosa semblant a això:
[2010-11-28 18:46:57,783: INFO/MainProcess] Got task from broker: tasks.add[c379421f-fa6a-4917-b45a-4833a9e30ef3] [2010-11-28 18:46:57,830: INFO/PoolWorker-2] [tasks.add(c379421f-fa6a-4917-b45a-4833a9e30ef3)] Entrant a la tasca ... [2010-11-28 18:46:57,879: INFO/MainProcess] Task tasks.add[c379421f-fa6a-4917-b45a-4833a9e30ef3] succeeded in 0.0486330986023s: 5
El brooker (RambbitMQ) ha enviat l'ordre d'execució de la tasca add que el worker té registrada i aquest es disposa a executar-la.
Des de la consola hem dit a la coa que volíem que la tasca s'executàs en modoe asíncron, d'aquí l'ús de delay.
Si ara volem obtenir el resultat farem
>>> print result.get()
5Com es pot veure l'exemple és molt senzill, potser massa per adonar-nos de la potència de Celery. Pensem que en lloc d'una suma podem fer la generació d'un pdf, l'enviament d'un e-mail, la càrrega en batch d'un xml, etc. etc. L'important és que el procés principal ja no té que realitzar la tasca i pot ser una altra màquina l'encarregada de fer-ho. Pensem en una màquina plena de targes gràfiques dedicades al processament numèric, en un sistema encarregat del data-mining dels logs, les possibilitats són infinites.
També és important notar l'escalabilitat que aconseguim amb Celery. No estam parlant ja d'escalabilitat amb nombre de processadors, sinó que parlam d'escalabilitat amb nombre de màquines. Mentre les nostres tasques puguin ser paralelitzades podrem fer servir Celery per a distribuir la càrrega de feina entre les màquines disponibles.
I això és sols el principi: celery pot substituir al Cron, permet la monitorització,
la creació de tasques molt més complexes que les que ens permet una funció i un
llarg etcètera d'opcions. La documentació és força completa, però llevat de
casos molt especial, el grau de dificultat que trobarem és el que hi ha a aquest post.
Traducciones/Translations by apertium
0 comentaris, 0 trackbacks (URL) , Tags: Python Django
Introducció a Celery
Escrit per Aaloy a 13 de November , 2010 a les 12:18 p.m.
Introducció a Celery
Celery es una aplicació que ens permet crear tasques de feina asíncrones gestionades per un gestor de cues que està basada en l'enviament de missatges de manera distribuïda. Es focalitza en operacions en temps real però també suporta la calendarització de tasques.
Les unitats d'execució, anomenades tasques, s'executen de manera concurrent en un o més nodes de treball. Aquestes tasques poden executar-se de manera asíncrona bé de manera síncrona (esperant fins que la tasca està llesta).
El sistema de missatgeria recomanat per celery és RabbitMQ encara que és bastant agnòstic en el tema i pot fer servir com a substitut Redis, MongoDB o una base de dades sql.
Encara que el programa va sorgir lligat a Django, actualment és una llibreria que es pot utilitzar de manera independent. S'han creat dos projectes a partir de l'original, Celery manté l'estructura bàsica de la llibreria i django-celery manté la integració amb Django.
Bé, fins aquí la parrafada d'introducció traduïda amb més o menys fortuna de la documentació de Celery, el que estam dien és que celery ens permet executar tasques de manera distribuïda, on el director d'orquestra (si seguim les recomanacions) és una aplicació anomenada RabbitMQ, escrita en Erlang, per cert, que se n'encarrega de rebre les ordres de les tasques que s'han d'executar i les distribueix entre els distints nodes que estan registrats per a executar dita tasca. Aquest director és el que se n'encarrega de saber com està cada tasca i d'obtenir-ne el resultat quan aquesta finalitza, de manera que el programa que ha encomanat la tasca.
Així dons hem de distingir tres actors en tot això, l'aplicació que comana la tasca, l'aplicació que executa la tasca (el worker) i l'aplicació que se n'encarrega de la comunicació entre qui comana la tasca i qui l'execute. La gràcia de tot això és que podem tenir més d'un worker a distintes màquines o a la mateixa màquina, de tal manera que el gestor se n'encarrega de repartir les tasques en funció de qui està lliure per executar-les.
Pensem un noment amb el que això significa: podem passar de tenir una aplicació que ho fa tot, a un conjunt d'aplicacions que poden estar en distintes màquines que col·laboren entre sí. Resumint: ens permet l'escalabilitat horitzontal.
Per què fer servir Celery?
Abans de Celery la meva experiència havia estat amb Beanstalkd, un sistema semblant, molt simple i ràpid. Ara us contaré una batalleta, estant de cap de projecte per Tui España un dels projectes que duia era el de la creació d'un servei web per a la venda on-line de transfers i excursions. La informació estava (i supòs que encara hi és) a una base de dades Oracle i amb una estructura pensada per a introduir-hi informació, de manera que fer una consulta un poc complexa com "vull totes les excursions que hi ha tal dia que es poden fer des de tal punt" era extremadament lent. La solució que trobàrem va ser desnormalitzar la informació passant-la cap a una base de dades Postgresql, un parell mallorquí de milions de registres que s'actualitzaven diàriament.
La primera versió del programa de càrrega estava fet amb Java, va ser complexe de crear, gairebé 3 mesos de feina, mal de mantenir i executant-ho tot amb fils estava gairebé 2 hores en tenir-ho tot carregat.
La segona versió la férem amb Python, una setmana i mitja, un 10% del codi, més funcionalitat i utilitzarem Beanstalk per a distribuir les tasques, ja que la càrrega en sí es podia particionar molt bé. Això ens permeté aprofitar la potència de servidors que estaven ociosos i aprofitar també els temps morts entre la consulta a Oracle, el parseig de la resposta i la introducció a Postgres. A una de les proves posarem en marxa processos a 4 servidors diferents, amb un total d'uns 20 workers i un temps de càrrega d'uns 3 minuts. Amb 2 servidors el temps era d'uns 5 minuts, una millora molt significativa respecte a l'aplicació Java.
Així doncs una de les utilitat que tenim és la de poder distribuir tasques molt pesades entre moltes màquines. Celery el que té molt millor que Beanstalk és una documentació molt acurada, sistemes de monitorització i gestió i en el cas de fer servir Django, la possibilitat de mantenir les tasques dins el mateix projecte.
El segon ús important de Celery és la web. En aplicacions web el que volem és donar la millor experiència d'usuari possible, i aquesta experiència es degrada si l'usuari ha d'esperar molt. El que ens interessa és donar la resposta a l'usuari de que la seva comanda s'està està executant i deixar el servidor web lliure per poder atendre altres peticions. Imaginem-nos per exemple una aplicació de comerç electrònic on l'usuari demana una factura i aquesta li arriba en pdf. En sistemes de molta càrrega la generació del pdf pot degradar la resposta global de la web, el que és interessant és poder enviar la petició de factura a un sistema extern a la web i que sigui aquest l'encarregat de la generació i l'enviament. Aquest sistema no té perquè estar ni tant sols a la mateixa màquina on hi ha l'aplicació web o poden aprofitar-se hores de poca càrrega per a fer-ne la generació i l'enviament.
Anar cap a la complexitat d'arquitectura que representa afegir un sistema de cues asíncron a la nostra aplicació implica voler anar cap a un aprofitament dels recursos i sobretot entendre com funciona la nostra aplicació, què s'ha de fer de manera immediata i què es pot fer de manera asíncrona, saber quines són les tasques més feixugues que ens afecten i com podem paralelitzar-les i distribuir-les. L'altra solució és posar més màquina amb n-mil cores i passar de tot, però a més de ser una opció econòmicament més cara i tècnicament discutible, és una opció a curt termini, ja que si la nostra aplicació té molt èxit pot arribar un moment on ja ni hi hagi màquines més grans o el cost de les mateixes es mengi tot els nostres beneficis. Podem acabar fent feina pel fabricant de hardware i per pagar-ne les llicències associades.
Convençuts? Esper que sí. Un sistema així no és per totes les aplicacions, la complexitat d'instal·lació i manteniment és més gran que tenir tot el sistema en una màquina i sense distribuir, però si teniu necessitats de fer càlculs intensius de manera puntual o tasques per les quals no voleu fer esperar l'usuari Celery és una molt bona solució. A més pensau amb sistemes com el núvol d'Amazon, puntualment podem aixecar-ne tantes instàncies com calgui i executar-ne workers, o imaginem un Datacenter propi on la majoria de servidors estan ociosos, podem posar workers a les màquiens amb mensy càrrega per a que ajudin en els processos pesats. Se'ns obre un ventall de possibilitat pràcticament il·limitat.
En el proper post, instal·lació de Celery, la creació d'una tasca i un exemple d'integració amb Django, així com problemes que ens podem trobar i com a evitar-los.
Traducciones/Translations by apertium
6 comentaris, 0 trackbacks (URL) , Tags: Python Django
Gràcies a tots per venir al creant bits: eines
Escrit per Aaloy a 30 de October , 2010 a les 12:14 a.m.
Actualització: Pujats els documents al dropbox
Una vegada més, i ja van quatre el creantbits ens ha servit per carregar piles, per trobar-nos amb amics que feia temps que no veiem. Cares conegudes d'altres trobades (des d'aquí moltes gràcies per el detall de les galletes) i un quòrum impressionant. Se nota que en Pau té tirada :)
Feia estona que volíem fer una altra trobada d'aquestes. Personalment crec que és molt enriquidora pel que representa d'important per nosaltres veure que hi ha molta més gent que s'interessa pel mateix tipus de coses que nosaltres. Com ja he dit altres vegades això me fa tornar la fe amb la professió.
M'hagués agradat poder fer streaming de vídeo, l'hem intentant, però ens ha fallat la connexió. Ja sé que estam a un Parc tecnològic, però ja sabeu com van aquestes coses. A APSL necessitàrem prop de tres mesos en tenir una ADSL i és sols de les normaletes (i cares), res de connexions de 50 Mb que hi ha pels pobles. A veure si la propera vegada hi ha més sort!
Com a telonero de Pau he presentat un grapat d'eines que trob d'allò més interessants per fer feina amb Django, Python i potser fins i tot per complementar altres llenguatges de programació. En Pau per la seva banda ens ha fet una introducció molt clarificadora del que representa fer feina amb Git. M'ha agradat molt la frase de que Git representa una solució tècnica a un problema social, el de la comunicació. En pau m'ha enviat la presentació i la penjaré junt amb la meva tot d'una que pugui.
Moltes gràcies per l'assistència i el suport. En el nostre ànim està el seguir fent més trobades com aquestes. El suport del Parc Bit (Incubit) cedint-nos la sala val a dir que és una de les coses que fan possible que aquest esdeveniments siguin possibles. Ens queda un any i mig d'incubació encara, així que amenaçam amb tornar-hi :)
Traducciones/Translations by apertium
6 comentaris, 0 trackbacks (URL) , Tags: Informàtica Python Django APSL
Vídeos de la Djangocon inspiradors
Escrit per Aaloy a 03 de October , 2010 a les 11:29 a.m.
Aquests darrers dies he estat mirant els vídeos de la darrera Djangocon, com a tota conferència hi ha gent que vol una cosa més tècnica i gent que la troba massa tècnica, però particularment trob que al la quantitat de conferències i lightning talks que hi ha qui no ha trobat el que volia segurament és perquè no ha cercat prou.
Jo he trobat molt interessants dues conferències, Scaling the World's Largest Django Application de la gent de DisqUs i Switching addons.mozilla.org from CakePHP to Django de la gent de Mozilla add-ons. M'interessen molt perquè mostre tant els problemes com la capacitat de Django per escalar per aplicacions molt grans, molt més grans del que estam acostumats per les nostres contrades. En el cas de Mozilla les xifres que donen el rendiment de programació estan en la línia del les meves pròpies recerques: una aplicació Django necessita de l'ordre de 3-5 vegades més línies de codi que per fer-la en PHP un framework com Cake.
Aquests projectes tan grans fan que s'hagin de cercar solucions a problemes comuns d'escalabilitat. L'interessant d'aquests conferències és que et diuen tant els problemes que s'han trobat com les solucions. Pel Twitter he postejat algunes vegades referències al projecte Celery el gestor de cues que s'està convertint en l'estàndard de facto per Python i Django i que fan servir aquests projectes. Però també ens ha permet veure com utilitzen Sentry una eina per la gestió unificada de logs.
Sentry és una aplicació integrable amb Django que ens permet unificar els nostres logs en un únic servidor i integrar les notificacions d'error i incidències al nivell que vulguem i com vulguen. Ho podem posar integrat dins la nostra aplicació o tenir un servidor independent i fer que els n servidors que composen la nostra aplicació enviïn els missatges per http o per una coa Celery. La solució és força elegant i potent, de fet força més potent del que es pot trobar per Java/J2EE i és una solució a un dels problemes més comuns quan es tenen múltiples servidors: la necessitat de tenir logs unificats per saber què està passant i a on està passant.
Encara em queden força conferència a veure, però tampoc vull endur-me'n un empatx, i a més s'ha de tenir temps per avaluar altres projectes més propers. Per exemple, en aquests moments estic avaluant si per les aplicacions que feim és millor disposar d'un blog separat amb WordPress o bé d'un blog, potser visualment menys atractiu, però que s'integri totalment amb les aplicacions Django. Encara no tenc cap conclusió en ferm, però ara per ara la flexibilitat que ens dóna la integració.
Traducciones/Translations by apertium
0 comentaris, 0 trackbacks (URL) , Tags: Python Django
Entorns virtuals
Escrit per Aaloy a 12 de September , 2010 a les 12:18 p.m.
Quan un es dedica a la programació seriosa amb Python (ja sigui amb Django o amb qualsevol altre framework) hi ha un parell de coses que s'han de tenir en compte: saber amb quina versió de Python programarem i si aquesta versió estarà suportada per la distribució del servidor de producció és una d'elles, potser la més simple. Una altra un poc més complexa és la de plantejar-nos des del principi com durem el mantenimient de l'aplicació.
Amb els servidors dedicats cada cop a més bon preu, no és genys extrany plantejar-se tenir un servidor dedicat per les nostres aplicacions web en Python per exemple. És també molt habitual que les aplicacions tenguin un cicle de vida molt més llarg que les llibreries que hem utilitzat per a crear-les. Ara podem fer servir la versió 1.2.3 de Django, però d'aquí tres o quatre mesos tindrem la 1.3, amb altres llibreries pot passar una cosa semblant.
Quan tenim una aplicació en producció la màxima sempre ha de ser la de si no està trencat no ho arreglis. Si la nostra aplicació funciona amb Django 1.1 i hem anat aplicant els pegats de seguretat, llavors plantejar-nos migrar a una versió major és quelcom que s'ha de plantejar amb cura i sempre tinguent en compte la relació risc benefici que comportarà l'actualització. Així doncs ens podem plantejar no actualitzar una aplicació, però no per això les seguents aplicacions que desenvolupem tenen que fer-se forçosament amb llibreries antigues.
Com manteneir doncs entorns separats? Si instal·lam les llibreries a nivell de la nostra distribució de Linux preferida l'actualització de llibreries i fins i tot del mateix Python es farà a nivell de tot el sistema. Necessitam quelcom que ens aïlli tot allò que utilitzam a la nostra aplicació. Aquesta eina té un nom: virtualenv creada per l'omnipresent Ian Bicking.
Aquesta utilitat ens crea un entorn separat de Python amb les llibreries que nosaltres hi instal·lem. Si no li deim res utilitzarà també les llibreries del sistema posant-les al PYTHONPATH, però també podem fer que la separació sigui del tot completa fent servir l'opció de '--no-site-packages'. Aquesta opció fa que l'entorn virtual no faci servir cap llibreria del path del sistema i ens permet instal·lar-ho to. És l'opció que a mi m'agrada més, encara que signfifiqui perdre més temps amb la instal·lació, em dona la seguretat de que sé exactament tot allò que necessit per la meva aplicació.
El manual del virtualenv està prou explicat, però tot i això jo no recomanaria fer-lo servir a pèl, sinó amb l'ajud de una altra utilitat anomenada virtualenvwrapper de Dough Hellman.
Aquesta eina ens permet gestionar de manera molt senzilla els nostres entorns virtuals, posar-los en un únic punt del sistema i ens proporciona una gran quantitat de hooks que ens permeten definir que volem fer cada cop que es crei un entorn virtual, o que s'elimini, o que s'activi o desactivi.
Així, per exemple, podem fer que cada cop que activem un entorn virtual s'actualitzi el repositori del control de versions, o que ens situi al punt exacte on tenim el codi. Si sempre feim servir Django als nostre projectes podem fer que cada cop que es crei un entorn virtual se'ns instal·lin també les llibreries que més utilitzam.
Per acabar-ho de rematar la utilitat pip instal·lador avançat de codi Python del que ja he parlat en altres apunts, s'acobla molt bé als entorns virtuals, fins i tot li podem dir que no instal·li res si no estam a un d'aquests entorns.
Si feim servir un entorn virtual i pip podem utilitzar llavors la comanda pip freeze quen es dirà exactament què hi ha a l'entorn virtual: paquets i nombre de versió o revisió quan s'ha instal·lant des de codi font. Així podem crear fàcilment un fitxer de requermients pip i asseguar-nos que quan posem l'aplicació a producció les llibreries que es facin servir siguin exactament les mateixes que hem fet servir a desenvolupament.
Si us plantejau fer servir Python en els vostres desenvolupaments un dels millors consells que us puc donar és que dediqueu unes quantes hores a estudiar virtualenv, virtualenvwrapper i pip, la vostra vida a partir d'aquest moment ja no serà la mateixa.
Traducciones/Translations by apertium
3 comentaris, 0 trackbacks (URL) , Tags: Python Django
Quina calor!
Escrit per Aaloy a 28 de August , 2010 a les 12:24 p.m.
Aquestes darreres setmanes han estat d'allò més interessants, caloroses però interessants.
La calor ha vingut fonamentalment d'uns dies que hem passat a Menorca. Ja hi havia anat una vegada de vacances i me va encantar, aquesta vegada no ha estat diferent, encara que hi havia molta més gent que la darrera vegada.
Però no vull donar enveja, així que també toca parlar de programació. Aquests dies hem estat i encara estarem durant unes quantes setmanes més, fent feina amb un projecte que implica fer feina amb un cms anomenat ezPublish.
L'ezPublish és un CMS prou potent, orientat a treure continguts sigui com sigui. Tu escrius el que sigui a la part de l'administrador i l'eZ intentarà renderitzar-ho com pugui. Supòs que si el que importa és el contingut en sí això és fantàstic, però quan es tracta d'afegir-hi regles de negoci per damunt eZPublish és un malson comparat amb frameworks com Django.
Està clar que no podem fer una comparació justa ja que ezPublish no té la flexibilitat d'un framework però crec que l'experiència serveix per reafirmar-me amb l'apreciació de que si el teu negoci no és generar continguts sinó que vols controlar el que passa a la web i per exemple vendre el teu producte, construir l'aplicació directament damunt un CMS és un malson.
Moltes aplicacions necessiten d'un CMS, bàsicament per independitzar la creació de planes del programador que ha fet l'aplicació, però això no vol dir que el CMS tengui que ser l'aplicació. El CMS no ha de ser intrussiu, hauria de limitar-se a una backoffice potent i a ser possible integrable i una API per poder accedir als continguts de la manera que nosaltres vulguem.
El CMS ens permet començar molt ràpid a afegir texts, però com dic, si la nostra web no és estrictament de continguts aviat el CMS es converteix en un problema, ja que te condiciona la manera de fer les coses. Personalment preferesc sol.lucions com Django Page CMS que funcionen a mena de plugin per a les nostres aplicacions. Sóm nosaltres que decidim quin contingut es presenta i com. Potser la interfície no és tan virguera com altres CMS dedicats, però al manco el CMS no es posa en el nostre camí i no ens condiciona.
Un altre CMS per Django que darrerament m'està agradant molt és el projecte Django cms. Si bé és pot considerar ja un CMS en sentit habitual, segueix essent poc intrussiu, podem afegir tant plugins propis del CMS com seguir amb les aplicacions Django que vulguem i l'administrador seguirà funcionant.
Una altra aproximació per Django és lfcms però en aquest cas més que "per Django" hauríem de dir fet amb Django. Aquí ja parlar d'un CMS complet amb el seu propi administrador que és extensible mitjançant plugins fets amb Python. La interfície està molt cuidada i és un CMS a tenir molt en compte si la nostra aplicació és bàsicament de continguts. L'emperò més gran és que necessitam accedir a dues interfícies per fer algunes coses. Per exemple si tenim contingut estructurat i contingut textual, el contingut estructurat l'haurem de gestionar per l'administrador de Django (o fer-ne un nosaltres) i el textual pel backoffice de lfcms. He d'investigar-ho un poc més potser hi ha una manera d'aprofitar tota la fona feina que han fet la gent de lfcm a la interfície.
Un altre CMS prou interessant és un nouvingut anomenat Merengue presenta unes característiques prou interessants com la senzillesa amb que gestiona els temes o la edició col·laborativa (no l'he provada per això), però no té ben lligat el concepte de planes i contingut. No he vist cap vista al backoffice que ens permeti veure clarament la jerarquia de planes. El contingut s'estructura mitjançant una llista plana i perds la referència de l'estructura de la web. Potser s'ha duit a l'extrem la separació dels continguts de la presentació, però al meu entendre tenir una visió esquemàtica dins l'administrador del que hi ha a la web facilita molt la vida a la persona que ha de posar els continguts. Això però, no és un impediment no no s'han de crear moltes planes o el lloc web té una estructura molt fixa, ja que com altres opcions que he presenta aquí, el CMS permet gestionar el contingut directament des de la part de visualització. Merengue apunta maneres i convidrà fer-hi un seguiment de ben a prop.
Però un CMS no és res sense un bon disseny. Crec que l'equip ideal de desenvolupament està format per un dissenyador/maquetador, un parell de programadors i un tècnic de sistemes. En un grup així es maximitza la productivitat, però sempre se pot aspirar a més.
Per això quan tenc una estona estic jugant també amb un micro-framework anomenat bottle amb la idea de facilitar la feina de maquetació amb html pur. És veritat que es podria fer amb Django però la idea és comptar amb una via de visualitzar planes web i trocejar-les amb seccions que no requereixi pràcticament instal·lació i permeti fer maquetes ràpides i tener un prototip funcional que sigui molt bo de modificar. Una vegada el prototip està funcionant passar-ho a plantilles Django és trivial.
Com trivial és gestionar una empresa amb OpenERP. Un complet sistema ERP desenvolupat amb Python i que particularment amb té el cor robat. Ja tenim vàries instàncies funcionant i com que no ha de ser allò que diuen de "en casa del herrero...", una de les primeres gestions que tindrem serà la d'APSL. Si us estau plantejant deixar el Contaplus o semblant o voleu saber en temps real l'estat dels vostres comptes, fer factures i gestionar la cartera de clients, OpenERP és ideal, ja que pots externalitzar tota la part de comptabilitat i fiscal i dur el dia a dia del negoci gràcies a la potent interfície web que té. És un d'aquests projectes que fa anys que segueixo, des de que era TinyERP i el darrer any i mig ha arribat al volum crític que l'ha permès créixer exponencialment en funcionalitat i potència.
El que deia, molta calor, però no sé si és el sol o la quantitat de 'hot software' que he ha al voltant del l'ecosistema Python.
Traducciones/Translations by apertium
3 comentaris, 0 trackbacks (URL) , Tags: Python Django
Haanga vs Django vs Tornado
Escrit per Aaloy a 15 de August , 2010 a les 5:42 p.m.
L'altra dia es va alliberar un llenguatge de plantilles inspirant amb Django anomenat Haanga patrocinat per Meneame.net.
Crec que això és una bono notícia, tenc la mala sort que quan he de tocar codi PHP sempre m'he trobat en que la separació de codi i lògica de presentació directament no hi és. Supòs que perquè els llenguatges de plantilles que hi ha són massa feixucs com per a que un programador de PHP se'ls plantegi en el seu dia a dia.
La sintaxi de plantilles de Django és lleugera per al programador/maquetador: és bona d'aprendre i molt poc intrussiva. Així que Haanga al meu entendre és un gran avanç, ja que té la senzillesa de Django i lleva l'excusa de la velicitat que sovint et donen alguns progrmadors de PHP per posar-ho tot junt, ja que compila les plantilles a PHP, amb la qual cosa tenim tots els beneficis i molt pocs emperòs.
Ricardo al seu blog fa una comparació de velocitat però s'ha de dir que no és ben bé justa, ja que no estam comparant el mateix, no en el cas de Django i Haanga al manco, ja que la comparació s'està fent en peticions per segon i contra el framework i no directament contra la part de plantilles. El framework de Django és quelcom més que les plantilles, aquestes són una part important però anecdòtica en termes de codi, ja que és força senzill canviar de motor de plantilles cap a qualsevol altra. El realment important d'un framework com Django és la facilitat que et dona per escalar tant en màquines com en programadors. L'estructuració del codi i el model MVT fa que sigui molt més senzill reaprofitar codi i mantenir codi existent.
Així doncs una comparació més justa seria la de generar les plantilles sense tenir en compte la part del framework. Per a ser justs també hem de modificar un poc el codi, Haanga fa ús de la compilació, però Django pot fer quelcom semblant, és a dir, podem cachejar les plantilles i reutilitzar-les, això, que ja era pràctica habitual en versions anteriors s'ha estandaritzat en la 1.2.
Si volem més velocitat llavors hem de començar a sacrificar funcionalitat, potser haurem d'anar cap a llenguatges de plantilles que facin el mateix que Haanga, és a dir, compilar el codi, en aquest cas a Python. Un bon exemple d'aquesta mena de llenguatges és el llenguatge de plantilles de Tornado o el de Mako. El primer és força interessant, ja que és d'un nivell de senzillesa i extensibilitat comparable Haanga.
He fet uns petits benchmarks considerant el cas més real, que és el de la generació del header.html. Per a fer la comparació justa he cachejat la compilació de plantilles tant en Django com Tornado, i en el cas de Django he utilitzat sols la part de plantilles. Per exemple el test b2_tpl.py queda:
from django.conf import settings
from django.template import Context
from django.template.loader import get_template
import time
settings.configure(TEMPLATE_DIRS=('../templates',), DEBUG=False,
TEMPLATE_DEBUG=False)
class Info(object):
def __init__(self, i, epoch):
self.i = i
self.epoch = epoch
t = get_template('b2_tpl.html')
x = ( Info(i,time.time()) for i in range(1,100))
for i in range(1,100):
print t.render(Context({'objects':x}))En poques paraules: no hem de programar amb Python/Django como ho faríem en PHP. Per cert pos l'exemple perquè consider que és ideal per veure com es pot fer un script fent us de Django però sols agafant les parts de configuració que ens interessin. Podeu trobar més informació a l'apunt de b-list.
La part de benchmarks de Tornado està inspirada en el treball de Rolando Espinoza sols que he llevat la part de Tornado i he utilitzat sols la part de plantilles.
He fet servir la instrucció time per obtenir els temps i n'he fet la mitja de 5 execucions enviant la sortida a un arxiu.
El resultat és que Haanga ens dóna un temps de 0,1798s, Tornado un temps de 0,099s i Django un temps de 0,3438s. En termes relatius Tornado és 1,8 cops més ràpid que Haanga i 3,5 cops més ràpid que les plantilles de Django. A la seva vegada Haanga és 1,9 vegades més ràpid que les plantilles de Django.
Que Tornado sigui més ràpid no vol dir que tenguem que deixar el llenguatge de plantilles de Django. Hi ha més coses a més de la velocitat pura. Un usuari realment no notarà si la plantilla es genera en 3 o en 1 milisegon però sí que ho notarem a l'hora de mantenir l'aplicació el poder haver fet ús dels avantatges de les plantilles de Django. S'ha d'optimitzar on calgui.
En el cas de PHP i Haanga el consell però seria el de tirar-s'hi de cap. La mantenibilitat que dona tenir el codi separat en plantilles compensa de sobres els pocs milisegons de retard que tindríem el primer cop que es generàs la plantilla en PHP i la gent que tengui que mantenir el codi PHP us ho agrairà.
Enhorabonoa a crodas i a la gent de Menéame per a esponsoritzar un projecte així i obrir-lo al món.
PS. Hi havia un projecte del Google Summer of Code que anava en la línea de Haanga però per Django d'Alex Gaynor, però pel que es veu al final Alex es va decidir cap a un projecte relacionat amb bases de dades no relacionals per Django.
Traducciones/Translations by apertium
0 comentaris, 0 trackbacks (URL) , Tags: Python Django
Integració del TVP CECA
Escrit per Aaloy a 26 de July , 2010 a les 8:20 p.m.
Després de barallar-m'hi un bon grapat de dies he aconseguit integrar el TPV de CECA en una de les aplicacions que estic desenvolupant per APSL. Com en el cas de la integració amb el BBVA he de dir que la qualitat de la documentació és inversament proporcional al suport que tens dels tècnics, per a que quedi clar: la documentació és pèssima, plena de inconsistències i exemples que no funcionen. El suport dels tècnics de CECA molt bo. Poc temps per a contestat (llevat de si demanes en divendres, clar) i respostes clares i concises. Un deu! Amb la gent del BBVA el mateix, se coneix que els ha tocat bregar amb la documentació.
No acab d'entendre què costa mantenir la documentació actualitzada. Si ja no es fa servir un programa per a generar la firma, llavors es refà la documentació i se'n lleva la referència. Si s'incorpora un tag obligatori nou que s'ha d'enviar, llavors s'actualitzen els exemples.
Per si a algú més li serveix faig cinc cèntims del que m'he trobat i del que funciona a l'hora d'escriure aquest apunt.
Generació de la firma
La firma és diferent per l'enviament i per la resposta. En ambdós casos es fa servir el xifrat sha1
Per l'enviament hem de fer una concatenació formada per:
clau_encriptació
merchantid
adquirerbin
terminalid
num_operacion
importe
tipomoneda
exponente
referencia
SHA1
url_ok
url_nokEl que és cada cosa està a la documentació, aquí el que s'ha de saber és: SHA1 és una cadena de caràcters i és necessari posar-la. La referència apareix a la documetació, però NO s'ha de posar, millor dit, és una cadena buida. El número d'operació és el que identificarà la nostra operació i vendrà també a la confirmació així que convé (i és necessari) que sigui un codi numèric únic. L'import és un nombre on les dues posicions representen els decimals, així 12.23 passa a ser 1223. Damunt la concatenació s'ha de fer l'sha1 i passar-ho tot a una cadena hexadecimal i a minúscules. Per exemple:
m = hashlib.sha1() m.update(s) valor = m.hexdigest().lower()
El calcul de la firma per la signatura de la resposta és diferent: clave_encriptacion merchantid adquirerbin terminalid num_operacion importe tipomoneda exponente referencia
Fixau-vos que no hi ha SHA1 aquí i com a cosa important l'import s'ha de posar tal com ve de la resposta, veureu que ve completat a zeros per l'esquerra. En aquest cas la referència sí que ve i està generada per la pasarel·la de pagament.
Enviament de les dades
<form id="pago_form" action="{{url_ceca}}" method="post" accept-charset="utf-8" enctype="application/x-www-form-urlencoded">
<input id="MerchantID" name="MerchantID" type="hidden" value="{{merchant_id}}"/>
<input id="AcquirerBIN" name="AcquirerBIN" type="hidden" value="{{acquirer_bin}}"/>
<input id="TerminalID" name="TerminalID" type="hidden" value="{{terminal_id}}"/>
<input id="firma" name="Firma" type="hidden" value="{{firma}}"/>
<input id="importe" name="Importe" type="hidden" value="{{importe}}"/>
<input name="TipoMoneda" type="hidden" value="978"/>
<input name="Exponente" type="hidden" value="2"/>
<input id="referencia" name="Referencia" type="hidden" value=""/>
<input name="URL_OK" type="hidden" value="{{url_ok}}"/>
<input name="URL_NOK" type="hidden" value="{{url_nok}}"/>
<input name="Pago_soportado" type="hidden" value="SSL"/>
<input name="Cifrado" type="hidden" value="SHA1"/>
<input name="Idioma" type="hidden" value="1"/>
<input name="Descripcion" type="{{descripcion}}"/>
<input type="submit" value="Comprar" />
</form>Si mirau la documentació veureu que qualsevol parescut amb els exemples és pura coincidència. Tots els camps hi són però no hi ha cap exemple que funcioni amb els camps que hi ha. L'import ha d'estar en el mateix format que la firma, és a dir com a nombre sencer on les dues darreres xifres representen la part decimal.
És important notar que la referència viatja buida i que s'ha de posar el camp Cifrado. L'exemple està agafat d'una plantilla Django i preparat per a funcionar amb Euros com a moneda i en castellà com a idioma.
Rebre la resposta
CECA us enviarà la resposta en un POST a la url que l'indiqueu. Ja he comentat abans el tema de la firma. Convé comprovar sempre que la firma de l'operació calculada amb els paràmetres que ens ha donat CECA coincideix amb el que rebem, d'altra manera implicaria que algú altra està intentant validar l'operació de pagament i marcar-la com a correcta quan no seria així.
Si feis com nosaltres la integració fent servir Django heu de tenir en compte que la protecció CSRF evita que pugui enviar-se un post com el que necessitam, així que hem de marcar la url com a exempta d'aquesta protecció. No fa gaire gràcia, però com a protecció addicional convindrà configurar el firewall per a que sols accepti peticions cap a la url confirmació des de les IPs de CECA.
Com a consideracions importants heu de tenir en compte que CECA passarà l'import completat a zeros per l'esquerra i que heu de guardar la referència i el camp Num_aut, ja que són els dos camps que CECA us demanaria en cas de reclamació. El camp Num_operacion se correspon amb el nombre d'operació que nosaltres hem enviat i ens servirà per a localitzar i processar la venda.
Esper que la informació sigui d'utilitat.
Traducciones/Translations by apertium
4 comentaris, 0 trackbacks (URL) , Tags: Python APSL Django
Django caché invalidation
Escrit per Aaloy a 04 de July , 2010 a les 4:32 p.m.
Un dels problemes més importants del desenvolupament d'aplicacions web que necessiten suportar una gran quantitat de visités és el de decidir com es farà l'arquitectura de caché i quan i com s'invalida el contingut de la mateixa.
Al respecte he trobat dues presentacions realment excel·lents de la Djangocon:
Les dues són molt bones, però especialment us recoman la segona si voleu passar una estona divertida a més d'aprendre com funciona el tema de les cachés. Jared Kuolt broda una presentació plena d'ocurrències, dobles sentits i acudits freaks al mateix temps que aconsegueix introduir-nos en la problemàtica de les cachés.
La primera presentació té moltíssima informació, Michel Malone de Pownce explica els problemes que s'han trobat a Pownce i com els han anat solucionant a Django.
El problema fonamental de les cachés a Django és el tema de la invalidació. Django oculta bastant el nom de les claus que genera i sense aquestes claus un no pot anar a Memecached per invalidar-les.
La sol·lució passa invariablement per generar les nostres pròpies claus de manera repetible i fer un us dels signals de Django per a realitzar la invalidació de les cachés.
Això vol dir ser conscients de com està construïda la nostra aplicació, saber com es genera cada plana i poder enviar el senyal adeqüat per a que s'invalidi la caché quan es modifica cert contingut.
Per llocs amb una càrrega mitjana, les eines per defecte que ens dóna Django són més que suficients. El problema ens ho trobam quan tenim llocs amb mil·lions de visites o bé, quan tenim un lloc web amb relativament poques visites però on l'usuari no és conscient de la importància que té servir les planes ràpidament i per tant de la importància de les cachés.
És el tipus d'aplicació que necessita que qualsevol tipus de canvi es vegi reflexat de manera immediata a la web. Si el lloc és petit, senzillament es pot invalidar tota la caché, si el lloc comença a tenir moltes visites aquest tipus d'invalidació no és convenient i ens trobam gairebé amb la mateixa necessitat d'invalidació de cachés que poden tenir llocs amb moltes més visites.
Jared diu a la seva presentació que el tema del rendiment d'una aplicació web és soluciona amb ^ca(sh|che)$. Personalment crec que les dues opcions van lligades, ja que encara que no es faci la despesa en hardware, un lloc web que requereixi um molt bon rendiment i a la vegada tengui poca inversió amb ferro, necessitarà d'una gestió molt fina de les cachés i de la seva invalidació.
Traducciones/Translations by apertium
0 comentaris, 0 trackbacks (URL) , Tags: Python Django
Migració a postgres des de sqlite
Escrit per Aaloy a 13 de June , 2010 a les 2 p.m.
Pels qui no ho sabíeu aquest blog corria damunt una base de dades sqlite3. La base de dades és prou ràpida per les necessitats d'un blog com aquest, però té un emperò considerable: consumeix molta memòria comparada amb un mysql o postgresql. Quan el blog duia una parell de setmanes amb visites que consultàven molts apunts, sqlite començava a cachejar i el consum de memòria de l'aplicació del blog es disparava fins als 160 Mb, mass si ho comparam amb altres aplicacions tant o més complexes que executant-se amb Postgresql estàven entre 30 i 50 Mb. El consum de Postgres és una altra cosa, però com que es reparteix millor entre les aplicacions el resultat final és un estalvi de memòria.
El procés per passar d'sqlite3 a Postgres ha estat el següent:
-
Feim un dump de les dades cap a json. Això es pot fer des de Django amb la comanda
dumpdata, per exemple:python manage.py dumpdata contenttypes > dumps/contenttypes.json
He fet dumps de sites, auth per la part d'usuaris, contenttypes, i després de tota la resta d'aplicacions que fa servir el blog.
-
Cream la base de dades i l'usuari a Postgresql que farem servir, donant-li permisos de creació de taules.
-
Anam a l'aplicació i canviam la connexió de base de dades de sqlite3 a postgres. Per aixòs basta canviar el DATABASE_ENGINE cap a
postgresql_psycopg2i establir el nom de la base de dades i el password. -
Executam la comanda
syncdb. Això ens crearà les estructures de dades que necessitam i ara ja poden amar restaurant les dades.
En el meu cas hi ha aplicacions que modifiquen el contenttypes quan es fa el sycndb, de tal manera que abans de restaurar he fet un trunc contentypes cascade a la base de dades.
-
Restauram, per exemple:
python manage.py loaddata ./dumps/auth.json
Anam repetint el procés. Començam per contentypes, després per sites, després per auth i després per les nostres aplicacions segons l'ordre de dependències que tenguin.
Això és tot, comprovam que tot és al seu lloc i recarregam l'aplicació.
A més he aprofitat que és diumenge per provar més coses. Això implica que el blog pot veure's una mica afectat, ja que vull provar errors 404, 500, ping cap a google i altres coses que vull provar en real.
Traducciones/Translations by apertium
3 comentaris, 0 trackbacks (URL) , Tags: Python Django
Darrera setmana de juny
Escrit per Aaloy a 30 de May , 2010 a les 9:36 a.m.
Aquesta setmana he tocat molt poca cosa de Python i Django, el projecte que ens consumeix gran part del temps és una feina gairebé de rellotgeria però no és un projecte Python. Tot i això he fet servir Django per al prototipat d'algunes part de l'aplicació. És molt més senzill i ràpid muntar un prototip i fer-hi feina que tractar amb l'aplicació real, per molt que tenguem varis entorns de proves.
Estic revisant també els vídeos de la Djangocon Europe 2010. He vist el de Django a l'empresa, el dedicat a la testabilitat i el de GUnicorn. Aquest darrer m'ha agradat especialment ja que tenim plans per anar incorporant GUnicorn com a mètode de referència de desplegament d'aplicacions Django.
En aquests moments estam fent servir CherryPy i la veritat és que funciona molt bé. Encara que la màxima és "si funciona no ho canviïs" GUnicorn és un sistema WSGI molt més orientat a la feina que CherryPy, amb possibilitat de gestionar connexions síncrones i asíncrones. Els benchmarks que hi posen Gunicorn un poc pitjor que altres sistemses WSGI, però per notar-ne la diferència hem de gestionar un lloc web amb moooltes visites. En aquests casos el que ens puguin dir els benchmarks serveix de poc, ja que sempre s'ha d'ajustar la tecnologia al nostre cas concret.
El que té bo GUnicorn a més de l'orientació a la feina és que fa molt senzill desplegar una aplicació Django amb WSGI, fins i tot provant-la en local, està molt ben documentat i amb manteniment actiu.
Parlant d'una altra tema, dir que entenc perfectament a Ricardo quan diu que Amazon l'avorreix! El sistema de contingència que ha desenvolupat Bernat funciona molt bé. Això sí, no hi ha por que els administradors de sistemes es quedin fora feina. Es necessita un bon administrador per fer que tot rutlli, el que sí que hi ha és la tranquilitat de saber que no hi ha problemes de hardware. El valor afeget d'un tècnic d'IT és a més d'entendre com funciona Amazon AWS, crear els scripts necessaris per a automatitzar-ho tot, crear les alarmes, els tests, etc. Amazon no llevarà feina als administradors de sistemes, sols canvia el tipus i la qualitat de feina.
L'altra dia vaig posar la primera alfa-alfa del motor de reserves per hotels a l'entorn de test. Encara està molt verd per a poder-ho alliberar com a codi font, i no he tingut temps per polir alguns errors que té la prova. Per ara és un "naked dessign" on es mostren els contractes, la cerca de disponibilitat i permet modificar algunes coses. Falta documentar, no el codi, que hi està força, però sí fer una documentació amb Sphinx que faciliti entendre tot el que comporta. Si algú vol fer una ullada a l'aplicació que m'ho digui i li facilitaré l'accés.
Seguim intentant trobar un finançament mínim per donar major impuls al projecte, però encara no hem aconseguit res, així que la cosa per ara va tira a tira.
L'altra dia a una trobada es parlava de la necessitat que tenia la gent de WP de comptar amb un motor de reserves semblant al que estam desenvolupant. Crec que no és tan imporant que estigui dins WP com que es pugui fer una integració neta mitjançant cridades Rest, XML o XML-RPC.
No tenc clar quin pot ser el model de negoci d'una aplicació així: cobrar pr suport, per tenir una versió estable, per hostejar el servei?. El que sí tenc clar és que ha de ser de codi obert.
Però no tot ha de ser informàtica, he començat a llegir "Si és dolent t'ho recomano" de Steven Johnson, mentres esper que m'arribin d'Amazon UK dos llibres de gestió de projectes. Per ara Johnson m'està agradant força, té un punt de vista molt fresc respecte a com ens influeixen els canvis com els videjocs, els realities, etc. La seva teoria és que aquests canvis no ens fan més estúpids sinó tot el contrari. Quan l'acabi en faré una ressenya més extensa.
Traducciones/Translations by apertium
7 comentaris, 0 trackbacks (URL) , Tags: Informàtica Llibres i revistes Python Django
Actualitzat vimrc
Escrit per Aaloy a 03 de April , 2010 a les 11:59 a.m.
He actualitzat la meva configuració de .vimrc i els pluggins i ressaltat de sintaxi que hi ha a .vim
- El subversion: http://code.google.com/p/appfusedjango/source/browse/#svn/trunk/myvim
- El .vimrc
- El .vim
Novetats
- Substitució de snipEmu per snipmate. SnipMate fa si fa o no fa el mateix però té una sintaxi més senzilla i clara i permet fer nous snippets molt més fàcilment.
- He afegit un nou ressaltat de sintaxi per json.
- El colors per defecte per gvim passa a ser ara wombat i he canvait el tipus de lletra a DejaVu Sans Mono, ja que té una bona distinció entre la vocal O i el zero, entre l'u i la i, bastant millor per programar.
- Activació per defecte dels menús i de la toolbar a gvim
- Neteja de la configuració a .vimrc
- Pos els alias a un fitxer apart dins ~/.vim/abbr, feu
aliasper veure el que hi ha. - Integració de més codi de pycopia especialment dels snippets per Django.
Traducciones/Translations by apertium
0 comentaris, 0 trackbacks (URL) , Tags: Python Django
Testejant Django amb Nose
Escrit per Aaloy a 02 de April , 2010 a les 10:19 a.m.
A poc a poc però sense pausa estic embarcat en la creació d'un motor de reserves orientat cap a hotels i cadenes hoteleres. És a dir, no es tracta de fer un sistema genèric orientat a la integració d'xml com els que poden necessitar agències i TTOO, sinó de tenir quelcom flexible i ràpid de personalitzar orientat a cobrir les necessitats més o manco complexes de la venda directa on line de nits d'hotel.
És a dir, el sistema ha de cobrir el bàsic (gestió del nombre d'habitacions disponibles, tarifes, descomptes per ocupació, aturada de vendes, etc) però també ha de permetre cobrir necessitat que en aquests moments no coneixem. Per tant, tenir una bona bateria de tests que ens assegurin que afegint noves característiques no ens estam carregant les que ja hi ha és fonamental.
La idea del Test Driven Development és que s'han d'escriure els tests abans d'escriure el codi. Jo no sóc tan purista i els tests els escric quan els necessit, unes vegades abans i unes vegades després d'escriure el codi. La raó és molt senzilla, quan estic immers en l'escriptura de codi per a que passi un test, sovint me trob afegint noves característiques per les que no tenc cap test encara. Llavors crec que el millor és seguir a la zona de programació pura i dura i després escriure els tests. Crec que no és tan important el moment en el que s'escriuen els tests unitaris com el fet de tenir-los.
Un motor de reserves com el que descric es pot fer en qualsevol llenguatge, en el meu cas hem triat fer-ho amb Python i amb Django, ja que ens dóna molta flexibilitat posterior a l'hora de fer adaptacions, que és el que cercam. Així doncs la definició del model de dades i l'ORM que s'utilitza és el de Django, la qual cosa fa que sigui important poder testejar-ho amb les eines que ens proporciona aquest bastiment.
Llegint la documentació de Django podem veure que aquest fa servir els units test de tota la vida i que a més per a que els tests siguin realment unitaris el que fa es utilitzar una base de dades nova i neta cada vegada que executam un test, d'aquesta manera ens asseguram que no hi ha dependències entre diferents execucions de casos de prova i per tant que els tests són realment unitaris respecte a les dades.
Tot i que sigui una solució totalment vàlida, consider que eines com nose fan l'escriptura de tests unitaris una tasca molt més divertida, ja que no has de passar pena de com estructura els test, sinó simplement els has d'escriure amb unes convencions de codi (per exemple els tests han de començar o contenir la paraula test). Per mi això significa menys complicacions i poder reaprofitar petits troços de codi que tanmateix hauria necessitat escriure per provar l'aplicació sense tenir que donar-los el formalisme que necessita un unit test. L'ideal per mi és tenir nose integrat dins el sistema de test de Django.
Afortunadament més gent ha tingut aquesta idea i per sort per mi, gent amb un coneixement més profund que jo de nose a nivell intern com per fer-ne una adaptació per Django, us present el django-nose. És una aplicació que s'instal·la amb pip o easy_install i que necessita molt poca configuració, afegirem 'django_nose' al settings.py a la secció INSTALLED_APPS i afegirem TEST_RUNNER = 'django_nose.run_tests' per dir-li a Django que farem servir el nostre motor de tests enlloc dels seus.
La gràcia d'aquest mòdul i de nose és que és compatible amb el que ja hi havia, però a més afegeix molta més funcionalitat i agilitat a l'hora de crear els tests. Basta fer un python manage.py test --help per adonar-nos de tota la quantitat d'opcions que ens afegeix el mòdul a l'hora de testejar. D'aquestes m'agradaria destacar-ne algunes:
-
--with-coverageEns permet utilitzar la utilitat de coverage de Ned Batchelder que ens permetrà veure quines línies de codi no hem testejat encara. Amb opcions addicionals és capaç de generar-nos un html navegable per a que poguem veure exactament el context de les línies testejades i de les que queden sense provar. -
--processesEns permet aprofitar els nuclis del nostre ordinador, els tests s'executen en paral·lel (recordau que els tests unitaris no han de tenir dependències entre ells) accelerant notablement el temps de procés. -
--with-xunitFa que en lloc de tenir la sortida típica dels unit tests tenguen el format de xunit, el mateix que es fa servir als unit test de Java, per exemple, i que ens permetrà integrar els nostres unit tests amb eines d'integració contínua com Hudson.
A l'hora de testejar una aplicació com el motor de reserves és imprescindible partir de dades conegudes i controlades per poder determinar els resultats esperats. Per això la manera que té Django de carregar les dades és simple i elegant. A cada unit test i abans de l'execució de cada cas de prova es crea la base de dades (en el meu cas un sqlite3 en memòria) i es carreguen les dades inicials (o fixtures en la nomenclatura) que es defineixen a cada unit test. Els fitxtures no són més que arxius en format json, yaml o xml que representen els registres que hi ha d'haver dins la base de dades.
Per exemple: [{"pk": 1, "model": "sites.site", "fields": {"domain": "example.com", "name": "example.com"}} ]
Aquests arixus els podem crear nosaltres directament amb un editor com vim o bé aprofitar-nos de l'entorns de Django i crear-los a partir de l'admin. Així podem introduir les dades a la nostra base de dades i fer un
./manage.py dumpdata applicacio.model
per exemple:
./manage.py dumpdata sites.Site --indent=4
ens proporcionarà el codi json per al model Site, l'indent el que fa es proporcionar espaiat addicional per a que sigui més bo de llegir i de modificar.
Si ens va millor tractar amb yaml, és prou senzill canviar el format:
python manage.py dumpdata sites.Site --format=yaml --indent=4
- fields: {domain: example.com, name: example.com}
model: sites.site
pk: 1
Dins un mateix unit test podem carregar tants arixus de fixtures com vulguem, ja que basta definir una llista del que s'ha de carregar, això vol dir no tenir que tractar amb grans arixus de dades de proves, sinó poder-los fer molt més manejables i sobretot reaprofitables.
La combinació de fixtures, unit test de Python, eines de testeix de Django i nose és molt difícil de batre. Per una part tenim que escriure tests amb Python costa una fracció del temps i de codi respecte a escriure'ls en un altre llenguatge, però a més nose fa que la tasca sigui molt més natural i la capacitat de generar els fitxtures des del mananager de Django fa que una tasca avorrida es convertesqui en trivial.
Tenir una bona bateria de test és fonamental per provar l'aplicació, per demostrar que els casos que s'estan considerant funcionen i tenen les sortides que deim que han de tenir. Però la utilitat dels tests unitaris va més enllà. Són una eina imprescindible en la refactorització.
Una vegada l'aplicació ja fa el que volem arriba l'hora de plantejar-se si es pot fer millor, si l'aplicació pot ser més eficient, de refer el codi per a evitar repeticions. La regla fonamental de la refactorització és que no hem d'introduir funcionalitat nova, quan refactoritzam tot ha de funcionar com abans. Els tests unitaris ens ajuden a demostrar que la nostra refactorització és bona, en tant en quant passin exactament els mateixos tets que teníem abans de fer cap modificació.
Faceu Test Driven Development o agafeu una aproximació més pragmàtica, el cert és que tenir bons unit tests és una inversió de futur que costa sols un poc més de temps quan estam desenvolupant, ja que amb nose els tests es poden reaprofitar de les petites rutines per proves que tanmateix hauríem d'escriure. Els beneficis el veurem a mig i llarg plaç: quan refactoritzem, quan canviem codi, quan les proves les llanci un sistema d'integració contínua. És massa bo per a no fer-ho servir.
Traducciones/Translations by apertium
0 comentaris, 0 trackbacks (URL) , Tags: Gestió de projectes Python Django
Pip pip hurra!
Escrit per Aaloy a 13 de March , 2010 a les 12:08 p.m.
Què voleu, estic content de veure que una eina com pip funciona tan bé.
Pip és una eina per a la instal·lació de paquets i dependències per Python, molt més avançada que easy_install ja que permet tot el que feia easy_install però a més, permet mantenir d'una manera fàcil un arxius amb totes les dependències del nostre projecte.
Ens permet instal·lar paquests de Python des dels repositoris de Pypi, des de subversion, git, mercurial a través d'un fitxer de requeriments simple i a la vegada funcional.
Mode batalleta on:
Ahir vespre estava fent feina amb un projecte mascota que tinc, un motor de reserves per hotels i cadenes hoteleres fet amb Python i Django.
El projecte necessita de força llibreries: la darrera versió de django, nose pels tests, sphinx per la documentació, django debug toolbar, django extensions, south per la migració de dades, ipython, ipdb, pep8, coverage, pytlint. Tot això (i algunes més que s'hi afegiran) constitueixen l'entorn de desnvolupament del projecte.
Actualment faig feina amb tres ordinadors: el fix (el PPC del que tant he parlat per aquí), portàtil un Dell D820 i un Dell de 10". Segons on estic i el que he de fer en faig servir un o altra, o duc dins la borsa el portàtil gran o el petitó.
Per això el pip m'ha solucionat tant la vida. Ara quan desenvolup un projecte me basta mantenir al repositori del control de versions un arxiu amb els requeriments del projecte. Cada vegada que afegeixo una nova dependència no la instal manualment sinó que la pos a aquest arixu i execut
pip install -E $VIRTUAL_ENV -r requirements.txt
dins el meu virtualenv a partir d'aqui i quan he de canviar d'ordinador i vull tornar a fer feina en el projecte, basta baixar-me els canvis i tornar i tornar a executar a aquest ordinador l'ordre que he posat un poc més amunt. Amb una bona connexió en pocs segons (potser minuts) tindrem l'entorn de desenvolupament del programa totalment operatiu.
Mode batalleta off
Pel que en tingueu interés deix aquí un parell d'enllaços:
Traducciones/Translations by apertium
0 comentaris, 0 trackbacks (URL) , Tags: Python Django
Vim IDE per Django i Python
Escrit per Aaloy a 17 de February , 2010 a les 8:18 p.m.
Encara que faig servir distints editors i entorns integrats (IDE) per programar en Python i Django hi ha sempre la constant de retornar cap a Vim i gVim.
La cosa està però, en que per al desenvolupament normal no vull renunciar a un parell de coses que fan la vida més fàcil:
- Resaltat de sintaxi amb colors personalitzables i/o una paleta de colors còmoda per fer-hi feina.
- Autocompletat (dins cert límits, que això és un llenguatge dinàmic) i ajuda integrada.
- Plantilles per no haver d'escriure molt. Per exemple els shebangs, o els models de Django.
- Distints tipus de tabulació segons el llenguatge, quatre per Python, però 2 per HTML i Javascript.
- Possibilitat de tenir oberts molts arxius a la vegada i accedir-hi fàcilment
- Navegació pel sistema d'arxius integrada
I poca cosa més. Després quant més potent sigui l'editor millor, i per això Vim n'és de potent!! El problema és que ja m'agradaria poder fer servir amb agilitat un 20% de les seves capacitats.
En la meva recerca de l'editor perfecte he anat modificant el .vimrc i afegint plugins diversos, i configuracions que anat trobant d'aquí i d'allà. Per si a algú li va bé, he posat el meu .vimrc i .vim amb els plugins a l'appusedjango. Ja me contareu!
Eines per a la isntal·lació de plugins
Si feis un apt-get vim-addons obtindreu una petita utlitat que us permetrà veure quins plugins teniu instal·lats al vostres sistema i activar-los pel vostre usuari. En el meu cas tenc:
bufexplorer installed markdown-syntax installed matchit installed python-indent installed python_bike installed supertab installed surround installed taglist installed utl installed winmanager installed xmledit installed
En local (i instal·lats a mà) tenc també:
- ftplugin
- nerdtree_plugin
- snippetsEmu
- taglist
- mathit
- supertab
- vcssvn
Hi ha altres plugins interessants com el nerdcommenter i altres, però encara m'he d'anar acostumant al altres.
Referències
La veritat és que em costa dir d'on ho he tret tot, la configuració és una feina orgànica, he anat agafant coses d'aquí i d'allà, així que pos els darrers consultats.
Disclaimer: NO sóc cap expert amb vim, així que moltes coses van per assaig i error.
Download
-
El subversion: http://code.google.com/p/appfusedjango/source/browse/#svn/trunk/myvim
-
El .vimrc
- El .vim
Al svn trobareu un .vimrc que heu de posar al vostre home i un arxiu comprimit amb .vim que conté plugins, plantilles i demés, descomprimiu-lo també al vostre home.
No he de recordar la impirtància de fer còpies de seguretat de la configuració antiga abans de res, veritat?
Pels debianites i ubuntaires
Per a tenir l'entorn funcional necessitareu instal·lar
- sudo aptitude ctags
- sudo aptitude vim-addons-manager
- sudo aptitude vim-python (segons versions...)
comprovat per bibigeek (gràcies!) pels ubuntaires amb PPC com jo, no hi ha vim-python i convé recompilar vim amb suport per Python.
Esper que us sigui d'utilitat!
Traducciones/Translations by apertium
2 comentaris, 0 trackbacks (URL) , Tags: Python Django
Ressenya de creant Bits, el déjà vu
Escrit per Aaloy a 30 de January , 2010 a les 10:41 a.m.
El segon creant bits dedicat a la introducció de Python i Django d'ahir va a tornar aplegar un bon nombre de gent interessada per la programació i per Python i Django.
Cares conegudes i gent que vaig poder desvirtualitzar. Em va fer especial il·lusió poder desvirtualitzar Guillem, ja que per un motiu o altre mai ens havíem pogut trobar personalment.
Aquesta vegegada preferírem no allargar molt la jornada i no es donà la xerrada damunt la posada en producció d'aplicacions Django. La passada jornada En Bernat va tenir molt poc temps i acabarem molt tard, així que ho hem deixat per a una millor ocasió.
Aquest pic el consum de gominolas per part dels assistents va ser menor, lluny del rècord de kilo i busques de l'altra vegada. :-P La idea és que si algú s'avorreix al manco se n'endurà un sabor dolç de boca.
Entre l'anecdotari comentar el mal cos que se'ns quedà a tots quan un tassó d'aigua va vessar damunt un portàtil Macbook Pro nou de trinca. Després de netejar-lo va tornar a la vida i esper que segueixi així. Hi va haver un segon intent de tragèdia, quan el que es va vessar va ser cafè i no aigua, afortunadament aquest cop no va tocar el portàtil.
Sols me queda agraïr l'assistència de tots i especialment dels companys que donàren l'assistència tècnica i ajuda. Esper que tothom se n'anàs amb una millor idea de la que tenia a l'entrada damunt el que és Python, el que es pot fer i de la potència de Django per al desenvolupament web.
Ara a encalentir motors per a la xerrada de Ricardo al proper creant bits.
Traducciones/Translations by apertium
7 comentaris, 0 trackbacks (URL) , Tags: Python Django
FireLogger per Python
Escrit per Aaloy a 15 de January , 2010 a les 3:42 p.m.
Quan hom fa feina amb Django una de les primeres coses que aprèn és a mirar la consola del servidor de desenvolupament. Al la consola hi apareixen els missatges d'error i els logs bé en forma de prints o com a logs de Python.
Convé evitar fer prints i fer servir els logs. Aprofitarem el funcionament del logger per tal de discrimitar els tipus de log i distingir entre els missatges que volem que es mostrin sols en depuració (DEBUG), errors o informatious.
Una configuració molt bàsica del logs és la que propòs a projecte base d'appfusedjango, molt ràpidament:
Al properties.py o al settings configuram el sistema de log
1 2 3 4 5 | import logging logging.basicConfig( format="%(asctime)s-%(levelname)s-%(name)s-%(lineno)s-%(message)s", level = logging.DEBUG, ) |
i a cada arxiu on el volguem fer servir
1 2 | import logging log = logging.getLogger(__name__) |
Això ens permte configurar a un sols lloc el nivell de log que volguem i a més saber des d'on s'estan generant els missatges.
Com a retruc, a més ens servirà per poder mostrar els logs a la consola del Firebug gràcies a l'aplicació FireLogger.
Aquesta aplicació té una instal·lació en dues parts, ja que hem d'instal·lar el plugin de Firefox que hi trobareu a la plana (la versió 0.7 en el moment d'escriure això) i després instal·lar les llibreries Python necessàries.
La plana recomana fer servir easy_install però en aquests moments el paquet que hi ha al PyPi està desactualitzat així que millor instal·lar-ho a mà:
sudo easy_install paver sudo easy_install jsonpickle git clone git://github.com/darwin/firepython.git cd firepython sudo python setup.py install
Amb això a una Ubuntu basta. Una vegada insta·lat anirem a la nostra palicació Django i a la secció del MIDDLEWARE_CLASSES afegirem
firepython.middleware.FirePythonDjango
Podem obrir ara la consola de Firebug i trobarem una nova secció anomenada Logger. Aquí apareixeran els missatges de Log de la nostra aplicació. Podem filtrar per la criticitat del log i a la mateixa vegada tenim tota la potència del Firebug per a la depuració de l'aplicació.
Una bona eina per tenir al caixó de les de desenvolupament i depuració.
Traducciones/Translations by apertium
0 comentaris, 0 trackbacks (URL) , Tags: Python Django
Creant Bits, el déjà vu
Escrit per Aaloy a 07 de January , 2010 a les 9:24 p.m.
Em complau anunciar una segona edició de Creant Bits destinada a tots aquells i aquelles que no poguéreu assistir a la primera.
Els contingut seran bàsicament els mateixos, en tot cas mirarem de resoldre algunes mancances de la primera presentació, però en un 99% serà tot el mateix:
- Introducció bàsica al llenguatge Python, amb exercicis.
- Introducció a Django: arquitectura i possibilitats
- Instal·lació d'una aplicació Django a Apache.
La sala serà la mateixa que a la presentació anterior.
Creant Bits, el déjà vu
29 de gener de les 16:00 a les 21:00
Sala de Formació - Parc Bit
Pensau a dur el portàtil carregat amb el Python instal·lat. Hi ha connexió inhalàmbrica a la sala i el Parc Bit ens deixa un projector.
La sala té una capacitat per a 20 persones màxim. Per apuntar-vos deixau un comentari a aquest apunt.
Per cert, aquesta vegada tampoc hi ha catering! :)
Traducciones/Translations by apertium
23 comentaris, 0 trackbacks (URL) , Tags: Python APSL Django
Si House fos programador ...
Escrit per Aaloy a 04 de January , 2010 a les 7:35 p.m.
Ahir estava mirant la presentació de James Bennet a la DjangoCon anomenada "UR DOIN IT WRONG" i me'n vaig adonar que feia referència a una sèrie de màximes tipus:
#11919 No. You must believe the ERROR MESSAGE. You MUST believe the error message.
La conferència és molt bona, us la recoman. El cas, però, és que em va picar la curiositat i vaig seguir l'enllaç fins a arribar a una entrada de comp.lang.perl.misc del març del 2002 on Mark Jason Dominus feia una relació de consells que ell tenia a un arxiu anomenat File of Good Advice.
Les màximes, encara que plenes de sentit comú, tenen una mala llet considerable, i m'han recordat al nostre metge de la tele favorit. Supòs que no desapareixeran de la xarxa, però per si un cas les torn a escriure aquí. Ha passat temps, però la majoria són perfectament aplicables! Esper que les disfruteu tant com jo ho he fet.
#11900 You cannot just paste code with no understanding of what is going on and expect it to work. #11901 You can't just make shit up and expect the computer to know what you mean, Retardo! #11902 You said it didn't work, but you didn't say what it would have done if it *had* worked. #11903 What are you really trying to accomplish here? #11904 Who the fuck cares which one is faster? #11905 Now is the time in our program where you look at the manual. #11906 Look at the error message! Look at the error message! #11907 Looking for a compiler bug is the strategy of LAST resort. LAST resort. #11908 Premature optimization is the root of all evil. #11909 Bad programmer! No cookie! #11910 I see you omitted $! from the error message. It won't tell you what went wrong if you don't ask it to. #11911 You wrote the same thing twice here. The cardinal rule of programming is that you never ever write the same thing twice. #11912 Evidently it's important to you to get the wrong answer as quickly as possible. #11913 Gee, I don't know. I wonder what the manual says about that? #11914 Well, no duh. That's because you ignored the error message, dimwit. #11915 Only Sherlock Holmes can debug the program by pure deduction from the output. You are not Sherlock Holmes. Run the fucking debugger already. #11916 Always ignore the second error message unless the meaning is obvious. #11917 Read. Learn. Evolve. #11918 Well, then get one that *does* do auto-indent. You can't do good work with bad tools. #11919 No. You must believe the ERROR MESSAGE. You MUST believe the error message. #11920 The error message is the Truth. The error message is God. #11921 It could be anything. Too bad you didn't bother to diagnose the error, huh? #11922 You don't suppress error messages, you dumbass, you PAY ATTENTION and try to understand them. #11923 Never catch a signal except as a last resort. #11924 Well, if you don't know what it does, why did you put it in your program? #11925 Gosh, that wasn't very bright, was it? #11926 That's like taking a crap on someone's doorstep and then ringing the doorbell to ask for toilet paper. #11927 A good approach to that problem would be to hire a computer programmer. #11928 First get a book on programming. Then read it. Then write the program. #11929 First ask yourself `How would I do this without a computer?' Then have the computer do it the same way. #11930 Would you like to see my rate card? #11931 I think you are asking the wrong question here. #11932 Holy cow. #11933 Because it's a syntax error. #11934 Because this is Perl, not C. #11935 Because this is Perl, not Lisp. #11936 Because that's the way it is. #11937 Because. #11938 If you have `some weird error', the problem is probably with your frobnitzer. #11939 Because the computer cannot read your mind. Guess what? I cannot read your mind *either*. #11940 You said `It doesn't work'. The next violation will be punished by death. #11941 Of course it doesn't work! That's because you don't know what you are doing! #11942 Sure, but you have to have some understanding also. #11943 Ah yes, and you are the first person to have noticed this bug since 1987. Sure. #11944 Yes, that's what it's supposed to do when you say that. #11945 Well, what did you expect? #11946 Perhaps you have forgotten that this is an engineering discipline, not some sort of black magic. #11947 You know, this sort of thing is amenable to experimental observation. #11948 Perhaps your veeblefitzer is clogged. #11949 What happens when you try? #11950 Now you are just being superstitious. #11951 Your question has exceeded the system limit for pronouns in a single sentence. Please dereference and try again. #11952 In my experience that is a bad strategy, because the people who ask such questions are the ones who paste the answer into their program without understanding it and then complain that it `does not work'. #11953 Of course, this is a heuristic, which is a fancy way of saying that it doesn't work. #11954 If your function is written correctly, it will handle an empty array the same way as a nonempty array. #11955 When in doubt, use brute force. #11956 Well, it might be more intuitive that way, but it would also be useless. #11957 Show the code. #11958 The bug is in you, not in Perl. #11959 Cargo-cult. #11960 So you threw in some random punctuation for no particular reason, and then you didn't get the result you expected. Hmmmm. #11961 How should I know what is wrong when I haven't even seen the code? I am not clairvoyant. #11962 How should I know how to do what you want when you didn't say what you wanted to do? #11963 It's easy to get the *wrong* answer in O(1) time. #11964 I guess this just goes to show that you can lead a horse to water, but you can't make him drink it. #11999 You are a stupid asshole. Shut the fuck up.
Traducciones/Translations by apertium
0 comentaris, 0 trackbacks (URL) , Tags: Python Django Conyes marineres
Connectant el blog amb Twitter
Escrit per Aaloy a 30 de December , 2009 a les 5:06 p.m.
Feia estona que volia connectar el blog amb Twitter, de manera que cada vegada que escrigui un post s'envii directament a Twitter sense tenir que fer-ho jo. No és gran cosa, però és la vagueria informàtica: si es pot automatitzar perquè fer-ho a mà? :)
De llibreries de connexió cap a Twitter des de Python n'hi ha un bon grapat, la majoria el que fan és crear un envolcall a l'API de Twitter per tal que sigui més manejable des de Python i evitar repeticions de codi.
La llibreria que jo he fet servir s'anomena twython i té el mèrit de ser la primera que he provat. Potser n'hi ha de millors o més completes, però pel que he de fer ja basta.
El que vull doncs és que cada vegada que escrigui un nou post, s'envii un nou Twitt. Per fer-ho hi havia dues opcions clares: sobrescriure el mètode save del model o bé utilitzar senyals.
He triat fer-ho amb senyals ja que m'estim més no fer massa complexe el mètode save, i a més la senyal post_save ja ens informa de si s'ha gravat un registre nou o un registre antic. El codi de fet és tan senzill com això:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 | # Signals def twitt_post(sender, **kwargs): is_new = kwargs.get('created') if settings.SEND_TWITT_ON_POST and is_new: instance = kwargs.get('instance') import twython try: twitter = twython.core.setup(username = settings.TWITTER_USER, password = settings.TWITTER_PASSWORD) twitter.updateStatus(_(u"Nou post al Blog de Trespams: %s %s") % (instance.headline, instance.get_absolute_url(), ) ) except AuthError, e: logging.error("Error in twitter account") logging.error(e.message) except Exception, e: loggin.error('Error in twitter post %s' % e.message) # Connect the signal with the callback post_save.connect(twitt_post, sender=Entry) |
twitt_post és el callback és a dir, la funció que es cridarà quan es gestioni la senyal, com a arguments passa la classe, la instància i si el registre es nou o no.
Obtenim primer si el post es nou o no, no vull empipar a la gent cada vegada que faci una correcció ortogràfica o tipogràfica, així que sols enviaré els twitts quan el post sigui nou. També he fet que sols s'envii si la variable de configuració SEND_TWITT_ON_POST així ho diu.
Després ja és cosa de utilitzar la llibreria (login i enviar, així de fàcil) i capturar les possibles excepcions.
Finalment el que feim és connectar l'event amb el model i la funció callback.
Si tot va bé aquest post ja s'hauria d'enviar automàticament.
Traducciones/Translations by apertium
0 comentaris, 0 trackbacks (URL) , Tags: Python Django
I trobaré gent que sàpiga Python?
Escrit per Aaloy a 30 de December , 2009 a les 1:06 p.m.
Sovint quan plantejam que un desenvolupament ho farem amb Python i Django, la gent de les primeres coses que demana és a veure si trobarà gent que sàpiga Python i Django per dur el manteniment posterior, o si nosaltres deixam el negoci, o si ha de contractar algú, ...
Qui està un poc aficat en el tema de la programació web sap que és relativament senzill trobar gent de coneix PHP o Java, però encara el tema Python no està tan arrelat al subconscient com per a que soni com a llenguatge de referència.
Idependentment del que al final s'opti per un llenguatge o un altre, el "trobar gent" serà tant o més complicat en PHP o Java como ho pot ser en Python, ja que conèixer el llenguatge no implica necessàriament poder-se fer càrrec de l'aplicació o que el manteniment de la mateixa sigui senzill. És més, m'atreviria a dir que molts programes web que hi ha en PHP o Java són molt complexes de mantenir per algú aliè al desenvolupament inicial.
Aprendre un llenguatge és relativament senzill, conèixer-ne la sintaxi i poder llegir el codi pot dur pocs dies, setmanes si m'apurau. Després la tasca és saber com està fet el programa, entendre què fa i començar a trobar on s'ha de modificar cada cosa, i és aquí on el llenguatge importa menys que com s'ha desenvolupat el programa.
El model Python i Django fa molt més senzill fer programes bons de seguir, la sintaxi de Python es legible per pròpia construcció del llenguatge, i un programa que utilitzi Django com a bastiment i que segueixi les convencions de codi de Django mateix i de Python serà molt menys propens a les "sorpreses" que un típic programa PHP que segueix sols les convencions del seu programador, i segurament que a un programa Java-J2EE on vet a saber quines llibreries o tecnologia s'haurà utilitzat.
Amb això no vull dir que no es puguin fer programes mantenibles en PHP o Java, sinó que si el que volem en un futur és prendre el control del desenvolupament d'un programa que hem comanat a un tercer, el més important no és tant el llenguatge de programació sinó com estigui estructurat el programa, la legibilitat del codi, el comentaris i la documentació.
Quan un desenvolupament està fet en Python i Django augmentant les nostres possibilitats de que sigui mantenible posteriorment. Potser necessitarem un temps previ de formació en el llenguatge (o formar als nostres programadors interns), però aquest temps es veurà de sobres compensat per la facilitat de manteniment posterior. Per mi és una inversió de futur.
Traducciones/Translations by apertium
3 comentaris, 0 trackbacks (URL) , Tags: Python Django
Aquest 2009 s'acaba
Escrit per Aaloy a 20 de December , 2009 a les 1:38 p.m.
El 2009 ja gairebé s'ha acabat, així que com és habitual convé fer un poc de recapitulació del que ha estat l'any 2009 i definir el que esper del 2010.
Trespams
Sense haver acabat l'any aquest és l'apunt que fa 77, això vol dir que ha estat un any prou constant en el que és la publicació d'apunts en el blog, . M'agrada escriure, em relaxa i em permet posar idees en clar. Si a més aquests posts serveixen a algú més, encara que sols sigui per passar l'estona, crec que s'ho paga el petit esforç de posar-se davant l'ordinador i teclejar.
En aquest moments Trespams té uns 900 visitants més o manco habituals. D'aquest n'he pogut desvirtualtitzar un tant per cent molt petit, potser un 10%, alguns han deixat comentaris i hem pogut mantenir converses virtuals tant pel blog, correu o Twitter. Per mi és una de les experiències més gratificants del blog: poder compartir idees i pensaments amb comunicació bidireccional.
Des del principi el blog ha estat per mi essencial a l'hora de posar en ordre les meves idees i dèries. La gestió de projectes, la gestió d'equips de programació, l'estimació de projectes de programari, Python i Django. Tot sempre amb un fil conductor comú: el programari lliure.
El blog vol ser també part de la meva petita aportació al moviment del programari lliure. Programari lliure per mi significa no sols compartir codi, sinó compartir idees de com podem crear i gestionar aquest codi, eines, idees, ... El coneixement ha de fluir per a que tots com a societat ens en puguem beneficiar.
Python i Django
Per Python i Django també ha estat un bon any. Ha sortit l'esperada versió 3 de Python i Django ha assolit una velocitat de creuer que el consolida com un dels bastiments de referència en la programació web moderna. La llista de Django té uns 15.000 subscriptors, al repositori de projectes de Python, PyPi hi ha una cinquantena de projectes i actualitzacions de projectes diàriament.
Esper que el 2010 torni a ser un any Python, projectes com PyPy i Unladen Swallow poden donar encara més empenta a aquest fantàstic llenguatge. Esperem que l'onada Python arribi també a les empreses per a que tots ens puguem gaudir d'una programació més clara, mantenible i sobretot divertida, on el llenguatge no sigui un condicionant sinó un vehicle per a la creació de programes i la generació de valor per al negoci i en definitiva per a la societat.
Pel 2010 l'objectiu és anar creant més exemples a Appfusedjango, millorar-ne la documentació amb Sphinx. Voldria també millorar el codi d'aquest blog, fer-lo més accessible als dispositius mòbils. M'agradaria poder fer aportacions al projecte Basie, un projecte amb el qual he pogut coneixer noves formes de col·laboració, de control del codi, d'eines, nova gent.
Tot això farcit d'apunts en aquest blog, com a manera de presentar el que m'agrada, d'animar a la gent a participar, i com ja he dit, com a manera d'ordenar les meves pròpies idees. Es presenta doncs un any 2010 força interessant.
Creant Bits
Creant bits és la demostració del que es pot fer quan les idees es converteixen en accions. Un petit comentari al Twitter i la col·laboració de molta gent va fer possible que una vintena de persones ens trobàssim al Parc Bit per parlar de tecnologia, de Python i de Django.
La meva intenció és repetir-ho al llarg del 2010 i més si tenim disponibilitat de Sala. En aquests moments i baix el paraigües d'APSL tenim accés a les sales de formació del Parc Bit i convé aprofitar-ho. Quan no hi tenguem accés ja veurem que feim, però m'agradaria que fos quelcom que anàs perdurant en el temps fins que el cos aguanti i la gent no es cansi.
L'altra dia per un comentari que vaig fer al Twitter d'un curs de Python se'm va demanar si Creant Bits seguiria essent gratuït. La resposta es sí, Creant Bits és una aportació al moviment del programari lliure, com ho poden ser alguns dels apunts del blog, o altres projectes en els particip. No crec que es pugin considerar cursos en el sentit que l'objectiu del Creants Bits no és que la gent surti amb un coneixement profund de la tecnologia, sinó el de presentar el que es pot fer, parlar, reunir-nos i animar a la gent a provar coses, donant-los el primer impuls.
Els cursos sí que els cobr. Quan una empres em demana un curs de Python i Django els objectius és que la gent que participi surti amb un domini del temari que els faci ser productius una vegada acabat el curs. Són moltes hores de curs i moltes hores de preparació per la meva part. L'horari del curs, la localització i els assistents són responsabilitat de l'empresa que em contracta i és aquesta qui fitxa els objectius.
A Creant Bits ens reunim amics i coneguts, gent que ja ens coneixem de manera física i virtual o que tenim ganes de conèixer-nos, amb ganes d'aprendre coses i relacionar-nos. Potser al llarg del temps i amb diferents trobades la gent que participa es veurà amb un coneixements semblants als que tindria amb un curs formal, però això serà tant per l'impuls de la xerrada com per la seva iniciativa personal, i això crec que és la diferència fonamental amb un curs. A un curs vols que la gent surti preparada amb tants coneixements com sigui possible en un temps raonable, a una trobada com Creant Bit a mi el que m'agradaria és que la gent sortís amb motivació per poder començar, amb una petita llum a la foscor, que permeti, amb el seu esforça personal, avançar en el món del programari.
M'estic extenent molt amb aquest tros, però és que veure tanta gent reunida perquè sí per mi ha estat molt important, ja que ha representat passar del món de les idees al món de l'acció, del món de les intencions als fets. L'injecció de moral per mi (i esper que per als participants) ha estat grandiosa i tenc ganes de repetir l'experiència al 2010.
APSL
Al 2009 hem consolidat APSL, l'empresa de la que són CEO i soci. És una empresa atípica, feta a la nostra manera d'entendre els projectes, amb l'ètica al davant, sense voler tenir clients captius sinó essent-ne col·laboradors. Volem fer partíceps a les empreses del que significa el programari lliure, de com les coses es poden fer d'una altra manera fins i tot amb els pressuposts.
Estam intentant rompre amb la idea de pressuposts tancats per a projectes de programari. Creïem amb la idea de que el pressupost inicial ha de ser orientatiu, que després el que importa és que el programa que s'entregui representi el que necessiti l'empresa, que no és necessàriament el que l'empresa vol a l'inici del projecte.
No és una tasca senzilla, representa canviar un poc les regles del joc. Actualment les negociacions d'un projecte sempre estan encaminades a que el risc del projecte ho assumeixi una de les parts. El client intenta que sigui l'empesa desenvolupadora la que assumesqui el risc intentant tancar el mínim possible. L'empresa de programació intentant minimitzar el risc mirant de tancar-ho tot i de protegir-se en el pressupost. És una situació un tant perversa, en la qual tothom hi perd en un moment o l'altra, com en una ruleta russa.
Tot projecte té un risc i aquest hauria de ser compartit i minimitzat. Creim amb la idea d'Scrum com a metodologia de desenvolupament i com a manera de facturar a un projecte. El client assumeix un cert risc: el pagament anticipat d'una quantitat i l'empresa n'assumeix un altre: que l'empresa en qualsevol fita del projecte pugui tancar-lo, dient que el que té ja és el que volia o donar-lo a un altre proveïdor.
El 2010 m'agradaria que fos un any de creixement per APSL perquè voldria dir que aquesta filosofia de treball i gestió ha estat entesa, que una altra manera d'entendre la relació empresa-client és possible.
Gestió de projectes
A 2010 m'agradaria aprofundir en el tema de l'estimació de projectes des d'un punt de vista col·laboratiu. Una de les mancances que tenim com a col·lectiu és que estam massa encaixonats dins la nostra manera de veure les coses, potser sense tenir massa idea del mercat. Són les nostres estimacions raonables? Són les nostres maneres de pressupostar adients? Som competitius? Podem fer alguna cosa per millorar les nostres estimacions?
Crec que es un punt amb la cooperació hi té molt a dir. On podem col·laborar explicant projectes, explicant el perquè de les valoracions i el temps total, per a que serveixin de referència. També es podrien organitzar sessions d'estimació àgil, amb Planning poker estimation. És una idea a la que estic donant voltes però que encara no sé molt bé com organitzar.
També m'agradaria posar en marxa algun tipus de projecte col·laboratiu local, potser lligat al Creant Bits, que servesqui no sols per aprendre sinó també per tenir un producte que pugui beneficiar-nos a tots.
Moltes idees i molts projectes que m'agradaria fer. Tot això s'ha d'acompassar necessàriament amb la dedicació a la família, amb els projectes alimenticis i amb altres projectes que no estan lligats a la informàtica que m'agradaria assolir. Per exemple, no tenc cap coneixement de llenguatge musical, i és una cosa que des de fa anys m'agradaria aprendre. Potser el 2010 serà l'any...
L'any de la crisi
El 2009 passarà per ser l'any de la crisi, però tot i això crec que hem viscut temps interessants. El problema amb la famosa crisi és que tot s'ha enlentit, és com si haguéssim perdut un any, estant a l'expectativa, a veure-les venir. Per mi aquesta expectativa ha estat doble, ja que en hem trobat amb la fusió de TUIE i Hotelbeds, amb la qual cosa molts projectes s'ha paralitzat a l'espera del que passaria.
Com es diu "qui espera desespera", però crec que no ha estat el cas. La sensació de pèrdua de temps és intensa, però tot i això projectes com APSL, el Creant Bits, els passejos amb els nins amb bicicleta i els amics del Twitter no em queda la sensació d'any perdut, sinó d'any de reflexió, de tenir temps de descobrir coses noves i punts de vista diferents.
Encara que molts (la majoria) de pressuposts presentats encara estan al caixó d'algú, poder parlar amb els clients crec que m'ha enriquit com a persona, no des del punt de vista econòmic, però si des del punt de vista espiritual i tot suma!.
Com sempre un espera que l'any que començarà serà millor que l'anterior. Sigui com sigui serà també un any interessant de viure.
Traducciones/Translations by apertium
5 comentaris, 0 trackbacks (URL) , Tags: Informàtica Python Django Gestió de projectes Codi lliure APSL
Creant bits amb Django i Python
Escrit per Aaloy a 04 de December , 2009 a les 11:46 p.m.
Hem arribat a la sala als voltants de les 14:30, allà el responsable tècnic del Parc Bit (Gillem) ens ha explicat com estava tot, endollat el projector i ajudat amb les cadires. Un 10 per la gent del Parc Bit i de l'Incubit, tant per deixar-nos la sala com per l'ajuda.
Hem començat a preparar-ho tot. Respiram un poc més tranquils quan hem comprovat que la connexió a Internet funcionava. Després de 2 mesos i busques sense ADSL començava a pensar que teníem gafe.
Als projectors no els sol agradar el portàtil amb resolució de 1920px, així que m'ha toca configurar-ho un poc. El projector una passada, arriba a 1600x1200 px i des del fons de la sala es veu bé.
La sala de formació té capacitat per unes vint i tantes persones assegudes a la taula. No hi havia cadires abastament i la gent del Parc Bit ens ha ajudat a torbar-ne més.
Després d'això, quan al part tècnica ja funcionava hem anat a cercar la poca intendència que hi ha preparada: aigua i gominolas. Si tot falla, pens, al manco la gent que vengui se'n podrà anar amb un gust dolç.
Primera sorpresa del dia: Xus s'entrega amb una cafetera Nesspresso, cafè i galletes per tothom. No tenc paraules!
Son les 15:30, ja ho tenim tot llest i anam a fer una mossegada ràpida. Allà trobam gent coneguda que també assistiran a la trobada.
En Juan s'adelanta per anar rebent la gent, seguidament baixam, són les quatre i comença a omplir-se la sala.
Molta gent coneguda, amics virtuals que es desvirtualitzen. Em sap greu poder dedicar més temps a presentar-nos com toca, esper que hi haurà més ocasions.
A les 16:30 aproximadament començam: primer amb la part Python i després amb Django. L'objectiu és evitar la por a començar, donar la primera empenta. Qued admirat pel nivell que hi ha a la sala. Veure tants informàtics junts, interessats per aprendre em fa tornar l'esperança en la professió. Sols per això ja s'ho paga els nirvis de muntar una cosa d'aquestes.
No sé estar assegut, m'agrada més explicar i comentar passejant, les gominolas al manco ajuden a recuperar energies. El michelín ho notarà segur :)
Hem acabat amb més de mitja hora de retràs de la part Django, En Bernat tendrà menys temps. És una llàstima, perquè una de les gràcies de Django és la flexibilitat que té per a deplegar-se allà on més ens convengui a nosaltres. Ha de resumir molt la xarl·la i es limita a mostrar la configuració amb mod_python. A veure si a la propera ens dona temps de que mostri wsgi i la potència del balanceig.
Són les nou passades. Acabam. Anam saludant a la gent que marxa i començam a recollir. Encara queda temps per anar parlant amb la gent que queda de batalletes i tecnologia.
En Pau i na Sílvia han fet fotos, quan les tengui les penjaré junt amb les presentacions. Els twitts estan com creant_bits.
Fi de la crònica!
Gràcies a tots per assistir, perdonau els que no heu pogut venir per falta d'espai. La gent que ha vingut de ben segur que us contarà que la sala tampoc no donava per massa més. Però no serà la darrera!
Moltes gràcies a Juan (morenosan), Xus (m'has deixat sense paraules) i Pau per les labors d'assistència tècnica. Gràcies especialment per Bernat, que s'ha currat una presentació. A Paco per la idea del nom, i per damunt de tot a tota la gent que un divendres de pont s'ha desplaçat al Parc Bit i ha estat més de cinc hores compartint la nostra passió per la informàtica.
Si algú em diu que a partir d'una twittejada es podia muntar un sarau com aquest no m'ho crec. La màgia de la comunicació!
Com dic, esper que no sigui la darrera. Personalment he gaudit del contacte amb els assistents, de desvirtualitzar gent, de veure que la visió que tenc de la informàtica és compartida, de pensar que les coses es poden fer d'una altra manera.
Avui no dormiré! :)
--
He pujat les presentacions a SlideShare. Les fotografies són a Flicker, gràcies a Silvia i Pau.
Traducciones/Translations by apertium
8 comentaris, 0 trackbacks (URL) , Tags: Python Django
Desplegament de Django
Escrit per Aaloy a 29 de November , 2009 a les 1:53 a.m.
Una de les característiques que més m'agraden de Django per desenvolupar és que ja ve amb el seu propi servidor integrat. És un servidor no apte per entorns de producció, de fet a les planes de Django es recomana repetides vegades que NO es faci servir a producció, però que va molt bé al desenvolupament.
El que fa tenir aquest servidor és per una banda acursar el temps necessari per començar a desenvolupar. Una vegada creat el projecte i la primera aplicació, posam el servidor en marxa amb un python manage.py runserver i ja tenim accés a un complet servidor web.
A més ens permet depurar des del principi les nostres aplicacions. La consola del servidor que acabam d'executar mostrarà els missatges del servidor i els logs que li enviem com un servidor web qualsevol, però a més ens servirà de consola de depuració de Python.
Encara que hi ha entorns com Eclipse+Pydev que ens permeten posar punts de ruptura directament al codi, un dels mètodes més simples consisteix en escriure import pdb;pdb.set_trace() allà on vulguem que s'aturi el programa. Llavors, en arribar-hi a la consola on hem executat el servidor ens apareixerà l'entorn de depuració de Python (el pdb). Si volem fer un poc més de feina podem instal·lar l'ipdb i tendrem una consola de depuració amb autocompletat i resaltat de sintaxi.
A Python diuen allò de "batteries included" per indicar que amb Python hi ha tot el necessari per arrancar, Django segueix la mateixa filosofia, de manera que no necessitam configurar cap servidor web per començar a fer-hi feina i ens proporciona a més eines de depuració prou potents: la possibilitat de depurar amb pdb/ipdb o semblants, la depuració mitjançant els missatges d'error de les plantilles o la possiblitat de depurar evitantn la recàrrega amb l'opció de --no-reload del servidor de desenvolupament.
Una vegada ja tenim l'aplicació creada i depurada passam al desplegament. Pels experts de sistemes no ha de ser cap problema seguir les instruccions de les instruccions de desplegament de Django. Es pot desplegar en Apache (mod_python o mod_wsgi), damunt ngnix, lighthttp, Cherokee, ... En general no es tant cosa del servidor com de triar una tecnologia considerant el nostre entorn d'execució.
Perquè pensau que Django està pensat per ser escalable per amunt i per avall. Segons les restriccions del nostre entorn de producció ens convindrà elegir una tecnologia o una altra. Decidir si ens convé tenir un sols servidor http o separam l'execució de l'aplicació del servidor de continguts, quin tipus de caché farem servir, etc. etc.
Tot és molt flexible, però aquesta flexibilitat fa que ens tenguem que fer preguntes, Django no decideix per nosaltres com s'ha d'instal·lar. Fa recomanacions però en general s'adapta al que hi ha.
Si un disposa d'uns tècnics de sistemes com els que jo tenc la sort de fer feina, aquesta flexibilitat és fantàstica, segons l'aplicació decideixen la tecnologia que es fa servir, la caché, les instàncies que s'aixecaran o com es serveix el contingut estàtic. Si un s'ho ha de fer tot i no disposa d'un servidor propi (o al manco d'un servidor virtual) el desplegament vindrà marcat pel que ens deixi el nostre ISP.
Posar una configuració? Doncs no, dependrà de cada aplicació i de cada entorn. Sols un parell de recomanacions:
- wsgi funciona molt bé
- Si podeu separau el servidor d'aplicacions Django del servidor de contingut estàtic.
- Amb un poc més de pressupost es pot tenir un servidor dedicat o virtual configurat i podrem treure tot el suc a la nostra aplicació.
Traducciones/Translations by apertium
0 comentaris, 0 trackbacks (URL) , Tags: Python Django
Una ullada a Sphinx
Escrit per Aaloy a 27 de November , 2009 a les 11:36 p.m.
Si heu fet servir la documentació de Python, Django o potser alguna de les aplicacions més importants o darrerament li heu fet una ullada al Building Skills with Python us n'adonareu que comparteixen un estil comú. Django ha personalitzat les plantilles, però com podem veure a la part on explica com es crea i es contribueix a la documentació, podem feure que Django fa servir Sphinx.
Sphinx és l'eina amb la qual s'estan creant les documentacions dels principals projectes basats en Python (encara que ens permet escriure qualsevol tipus de documentació) i poc a poc esdevé la killer appplication de la documentació tècnica.
Començar amb Sphinx és prou senzill, sols necessitam tenir Python instal·lat i
$ easy_install Sphinx $ sphinx-quickstart
i anar contestant les preguntes. Si heu compilat alguna vegada algun programa al món Unix veureu com quan acabin les preguntes s''haurà creat un directori amb un arxiu anomenat Makefile (make.bat per altres). Executant-lo ens crearà un directori build amb les bases del que pot ser la nostra documentació.
I és que Sphinx és bàsicament un compilador orientat a la creació de documentació. Agafa documents escrits en Restructured Text i crea un lloc web en HTML, genera un pdf o latex. Això vol dir que per poder treure'n el màxim partit s'ha de conèixer Restructured Text i fer-li una ullada a les comandes que Sphinx incorpora per crear les taules de contingut, fer el resaltat de sintaxi, incorporar imatges o fórmules matemàtiques. Una vegada passat el període d'aprenentatge (que ja us dic que és curt) veureu que el resultats que s'obtenen són espectaculars.
Ja he dit sovint pel blog que m'agrada escriure la documentació (i escriure en general) en text pla, utilitzant LaTeX, Markdown o ResTructured Text. Sphinx ajunta el fet de poder escriure la documentació com m'agrada amb un resultats realment professionals. És a la documentació hipertextual el que LaTeX és per a la documentació científica.
Potser ara us estareu demanant perquè fer la documentació amb aquesta eina i no limitar-nos a un document amb OpenOffice (o fins i tot en Word!!!), se m'acudeixen un parell mallorquí de raons:
- El text pla es pot posar damunt un control de versions.
- Permet que molta gent participi en la creació de la documentació.
- Sphinx ens dóna unes plantilles que fan que la documentació llueixi.
- El ressaltat de sintaxi Python ve de sèrie i costa ben poc fer el ressaltat per altres llenguatges.
- El cercador està inclòs a la documentació
Però sobretot, perquè amb Sphinx escriure documentació és divertit. Pots distribuir-te la feina com vols, no has de passar ànsia per la maquetació final, ni per que les imatges se t'han descol·locat i no saps on han anat a parar (si heu fet documents de més de 20 planes amb un processador de text sabreu què vull dir). I que escriure documentació no sigui feixuc segurament farà que ens faci menys peresa documentar i tothom ens estarà eternament agraït. Jo ja he començat amb la documentació d'appfusedjango que no se digui que no predic amb l'exemple.
Personalment crec que Django ha marcat un abans i un després en el món de la documentació de projectes, de tal manera que cada dia més projectes i aplicacions emulen la manera de documentar de Django i Django (com Python) fa servir Sphinx. Demostrant que per a que un projecte tengui èxit no basta que sigui bo sinó que ha d'estar ben documentat.
No cerquem en Sphinx la bala de plata. Fer bona documentació no és senzill, Sphinx sols fa que sigui menys pesat i ens dóna un resultat final molt més amigable als nostres lectors. Un dels creadors de Django,Jacob Kaplan-Moss, ha escrit un conjunt de posts damunt com s'ha d'escriure bona documentació: Writing great documentation, de lectura imprescindible. Però tot i que no sigui fàcil s'ha de començar, ens hem d'avesar (i jo el primer) a que Sphinx sigui part de les eines de projecte, a escriure documentació i a millorar el nostre estil, tanmateix diuen que la pràctica fa el mestre, no?
Traducciones/Translations by apertium
0 comentaris, 0 trackbacks (URL) , Tags: Python Django
Ulipad 4
Escrit per Aaloy a 24 de November , 2009 a les 11:53 p.m.
Acab de devallar-me la darrera versió (la quatre) de l'editor Ulipad. És un editor fet en Python damunt les llibreries wxPython.
La primera impressió que m'ha donat l'editor es pot resumir en una paraula: velocitat. És un editor molt ràpid, carrega en pots segons i l'autocompletat de codi, a més d'estar a un nivell molt bo, és també molt ràpida. Sols per això ja trob una justificació més que raonable per donar-li una oportunitat.
La gestió de projectes és un tant limitada, únicament ens presenta un sistema de fitxers, però és suficient per la majoria de projectes.
El suport de Python és molt bo i és extensible mitjançant plugins. En té un per Django que ens deixa executar el servidor de desenvolupament de Django. Tanmateix tot això és un poc anecdòtic si ho comparam amb la senzillesa i usabilitat d'aquest editor. Per ara (i duc una hora llarga amb ell) m'està agradant molt.
Destacar la bona integració que té amb la shell de Python, basant-se en pyCrust, la integració amb pylint i un parell d'utilitats que ens fan la vida més fàcil: un testejador d'expressions regulars i un gestor de trossos de codi (snippets).
Una altra de les coses que m'ha agradat és el suport que té per a l'edició de documents en RestructuredText, permetent-nos tenir una previsualització en temps reals de com queda el document.
He trobat a faltar un major suport per l'execució de test unitaris (sols té integrats els doctests) i la integració plena d'un depurador. Es recolza en un depurador molt bo, en Winpdb, però en aquesta versió pareix que s'ha deixat un tant desactualitzat. Supòs que obrint un ticket s'arreglarà aviat.
És un editor senzill, útil per que vol un editor gràfic i no pot o vol fer servir IDEs com Netbeans o Eclipse.
Traducciones/Translations by apertium
3 comentaris, 0 trackbacks (URL) , Tags: Python Django
Anunci Python i Django workshow
Escrit per Aaloy a 20 de November , 2009 a les 8:24 p.m.
Actualització
Hem arribat als 20. La resta haureu d'esperar a la propera!
Tothom que s'ha apuntat fins al meu darrer comentari, el 24 entra dins aquest primer workshow. Aniré posant per aquí i pel Twitter les novetat que hi hagi.
Em sap greu pels que els agradaria venir i han quedat fora, però compreneu que la sala és petita, 20 persones són moltes i el format que hem pensat (amb voluntaris ajudant als nouvinguts) no dóna per massa més.
Si la cosa agrada us promet que no serà la darrera! :)
No sé quin nom posar-li, en Paco proposa al twitter "programming python & django before beers" o "def bit():" però a les hores que acabarem (més de les 18:00) ja us dic jo que al Parc Bit no hi ha res obert per anar fer unes cerveses. M'ha agradat molt el Creant Bits, començant amb Python i Django i ja veurem com acabarem...
Així doncs faig l'anunci oficial:
Creant Bits amb Python i Django Divendres 4 de desembre Hora: 16:00 a 20:15 (o més) Lloc: Sala de formació del Parc Bit Organitza: APSL Col·labora: Incubit, que ens deixa la sala.
En quant als continguts, crec que els hauríem d'afinar entre tots. Per començar jo us proposaria el següent:
- Introducció a Python (1.5h)
- Pausa (15 min)
- Introducció a Django (1.5 h)
- Pausa (15 min)
- Posant una aplicació Django a producció (45 min)
La idea seria que la gent li fa ganes saber què és això de Django i Python pugui fer-se'n una idea. Es donarien quatre pinzellades de Python, amb exemples que tothom pugui executar al seu portàtil.
Per la part Django: mostrar com començar, que és, veure com es pot crear una plantilla, l'organització del codi i veure alguns exemples d'aplicacions, etc. Ha de servir com a presa de contacte i que tothom pugui fer-se una idea de les possibilitats que té aquest bastiment.
Com que al final el que volem tots és posar-lo en producció, s'explicaria com n'és de bo de fer posar-ho a un entorn Apache.
Però com us dic sols és una idea. El temps és limitat, però si estau interessats en donar-li una altra orientació estic obert a qualsevol possibilitats.
Requisits
- Tothom amb el seu portàtil, millor amb Linux. Si algú vol dur el seu fix i pantalla serà benvingut, però avís que hi ha una passejada i no sé com està la sala d'endolls.
- Python 2.5 ó 2.6 instal·lat, el 3.x no us funcionarà amb Django.
- Un editor amb ressaltat Python: vim, gvim, notepad++, Netbeans, Eclipse, ...
- La darrera versió estable de Django insta·lada. La 1.1.1 en el moment d'escriure això.
- Navegador Firefox amb el Firebug instal·lat
- iPython instal·lat
Inscripció
Lliure, però teniu en compte que la sala té una capacitat d'unes 20 persones, així que, per favor, deixau un comentari per a que pugui controlar quants serem i saber si hem arribat al límit de la sala. Al comentari podeu posar el tipus d'orientació que us agradaria.
Quan serà la propera?
Dependrà de l'èxit d'aquesta i de que Parc Bit/Incubit tengui lliure la sala i ens la cedesqui (des d'aquí moltes gràcies des de ja!). M'agradaria que això acabàs com a grup d'usuaris/empreses més o manco estable, però això ho hem de decidir entre tots.
Altres
Miraré d'informar-me com està la sala en el tema de connexions d'Internet. Si hi ha connectivitat fins i tot podríem intentar fer un projecte en grup, si no aquesta potser la propera vegada.
M'agradaria poder dir que hi haurà catering, però seria mentida :)
Traducciones/Translations by apertium
33 comentaris, 0 trackbacks (URL) , Tags: Python Django
Planes estàtiques amb Flatpages
Escrit per Aaloy a 19 de November , 2009 a les 6:15 p.m.
Fa estona que volia posar contingut no directament relacionat amb el Blog a Trespams, ja sabeu, quí soc, sobre el blog i coses d'aquests, però no ho arribava a fer. Finalment m'he decidit i aprofitaré per explicar com podem fer aquest tipus de coses amb Django.
El primer de tot és centrar el problema. L'objectiu és tenir lligat a la nostra aplicació (el blog en aquest cas) un conjunt de planes que no estan relacionades amb l'aplicació o aplicacions, que no han de ser editades o ho han de ser molt poc.
El cas típic són les planes de condicions legals, les planes de "sobre mi" dels blogs. Agafau la idea, no?
Com que és un cas molt comú Django té una aplicació al contrib pensada per fer precisament això. Ens proporciona un formulari on podem posar el títol, el contingut en format html i indicar si volem la plantilla que s'utilitzarà, si permetem comentaris o no i si l'usuari ha d'estar registrat al sistema per veure la plana. Aquesta aplicació se'n diu Flatpages.
La documentació de Django està força ben explicada.
-
Afegim 'django.contrib.sites' a INSTALLED_APPS del nostre setting.py si no hi és ja. Si no hi era convé fer un syncdb. Crearem una entrada al Sites amb la nostra url i també posarem el SITE_ID del settings.py el valor de l'id d'aquest registre. Si sols en teniu un serà 1.
-
Afegim 'django.contrib.flatpages' als INSTALLED_APPS.
-
Afegim 'django.contrib.flatpages.middleware.FlatpageFallbackMiddleware' a MIDDLEWARE_CLASSES del settings.
-
Executam
python manage.py syncdbper a generar les taules de Flatpages. -
Cream el directori flatpages dins el nostre directori de templates i cream l'arxiu d'default.html. Aquesta serà la plantilla per defecte que s'utilitzarà. Hem de personalitzar-la com volguem. A aquesta plantilla flatpages li passa la variable
flatpage, a partir de la qual podem obenir el títol i el cos del text amb{{flapage.title}}i{{flatpage.content}}. Podeu fer una ullada al codi de trespams del svn per tenir-ho més clar.
Ara ja sols és cosa d'anar afegint registres amb les urls i continguts que volguem. Django en no trobar una plana abans de mollar un 404 el que farà és anar a flatpages, si la url correspon a una que hagem definit generarà la plana a partir de la plantilla i els continguts del registre corresponent.
Per acabar, dir que no fa falta posar el filtre safe a les variables. Per defecte se suposa que l'html és segur, ja que no està pensat per a que un usuari extern a l'aplicació l'editi.
Traducciones/Translations by apertium
2 comentaris, 0 trackbacks (URL) , Tags: Python Django
Comparant Django i Drupal
Escrit per Aaloy a 16 de November , 2009 a les 5:56 p.m.
Pareix que alguns desenvolupadors de PHP s'estan plantejant anar cap a Python i Django com a una via per a fugir de les complexitats i problemes del PHP.
Des d'aquest blog vull encoratjar-los a al manco provar-ho i després decidir si convé o no fer l'esforç. Personalment pens que sí, pero tot dependrà de l'experiència que tengui cada un i de com vulgui enfocar els projectes.
Per ajudar en la decisió trob que és força interessant visitar dos enllaços:
El primer és una apunt i el segon és la discusió a un fil de la llista d'usuaris de Django. No us perdeu els comentaris, on es discuteix si Django i Drupal són comparables o no.
Traducciones/Translations by apertium
0 comentaris, 0 trackbacks (URL) , Tags: Python Django
Risc o seny
Escrit per Aaloy a 25 de October , 2009 a les 8:56 a.m.
Llegint l'article de Ned Batchelder, "The Scalability of programming languages" me n'adono que es troba en una tesitura com la que jo potser me trobaré d'aquí poc. Ell, un dels creadors de Tablo, comprat fa dos anys i mig per HP, es veu que es troba sota pressions per deixar de desenvolupar l'aplicació en Python per fer-ho en un dels llenguatges "corporatius" d'HP.
Per mi Django i Python representen un avantatge competitiu, ja que permeten una posada en producció, des de la concepció de la idea fins a posar l'aplicació al servidor, molt més curta, un temps de reacció als canvis i incidències difícilment superable, i sobretot, una capacitat de manteniment que fa que es pugui tocar una aplicació feta fa anys sense problemes.
El més curiós,però, és veure com les novetats, tot allò que pot representar una ruptura amb "la manera de fer les coses" presenta un problema fins i tot en les empreses que han fet de la innovació una de les seves raons de ser.
En una era on la tecnologia representa un avantatge competitiu important, limitar-se a allò que fa tot hom vol dir que no s'aprofita aquest avantatge. Això no vol dir tirar-se de cap cap a una nova tecnologia tan aviat com apareix, però una vegada avaluada, si la nova tecnologia representa una millora respecte al que tenim, el risc de ser dels primers en fer-la nostra es pot veure compensat per l'avantatge que ens donarà. Fins i tot si es demostra que la tecnologia no era allò que esperàvem, l'esforç necessari per a implantar-la i el canvi mental que suposa, farà que el nostre equip estigui més obert a les novetats i que llavors sigui molt més senzill anar introduint millores als nostres processos.
És un poc també com la situació en la que una empresa té l'opció de crear un nou programa per a comercialitzar els seus productes o comprar-ne un de ja fet. Desenvolupar-lo implica més risc i cost, però ens dóna un control total damunt el programa i podem fer-hi les modificacions que facin falta per adaptar-lo als nostres processos de negoci. Comprar-ho fet vol dir que qualsevol amb el capital suficient pot fer el mateix que nosaltres comprant el mateix programa que nosaltres. El que es dóna a més, és que nosaltres podem haver pagat per unes modificacions al programa que han costat temps i esforç de desenvolupar, i la nostra competència les tendrà des del primer dia a cost zero. Adéu a l'avantatge competitiu!
En aquests moments la tecnologia web i els llenguatges que li donen suport estan en un moment d'evolució important: llenguatges d'script per desenvolupar webs, pas cap a models de base de dades no relacionals, utilització de bastiments javascripts purs, serveis REST... Tot això està molt lluny dels llenguatges i tecnologies "corporatives", però són les tecnologies que formen la base de les noves aplicacions d'Internet, de la innovació.
Les empreses hauran d'elegir si prefereixen quedar-se en un sector madur i anar fent el mateix que tothom, o començar a apostar per la innovació i jugar-se-la de tant en tant amb projectes i tecnologies no tant consolidades però amb molta projecció.
Els riscs hi són, però ja se sap, qui vol peixos ...
Traducciones/Translations by apertium
0 comentaris, 0 trackbacks (URL) , Tags: Python Django
Consultes dinàmiques a Django
Escrit per Aaloy a 15 de October , 2009 a les 9 p.m.
Estic mirant de fer un poc més fàcil la integració de jqGrid amb Django, integrar la paginació ha estat força senzill ja que jqGrid i Django fan servir un conjunt de variables semblant: plana actual, total de registres i llista de registres.
jqGrid passa aquests paràmetres per GET quan feim un camvi de plana, per tant podem aprofitar l'objecte paginator de Django per fer la paginació com si fos una aplicació HTML clàssica, sols que en lloc d'HTML retornarem JSON (o XML).
La cosa es complica un poc més quan es tracta de cercar. jqGrid fa servir quatre paràmetres: _search, que es posa a true quan s'ha activat la cerca, searchField que conté el camp pel qual es cerca. SearchField retorna el contingut de la variable index de colModels si existeix o bé el nom. Llavors tenim també searchOper que ens informa del tipus de cerca que es vol fer. Per exemple, passa eq per indicar la coincidència exacta, ew per indicar acaba amb i cn correspon a la sentència IN d'SQL. Per acabar searchString conté el valor del que estam cercant.
Així doncs es tracta de montar un filtre de manera dinàmica amb Django a partir dels valors que ens envia el jqGrid. El primer que hem de fer és fer la taula d'equivalències que mapejarà cada operació de jqGrid amb una operació equivalent de Django.
Per exemple:
1 2 3 4 | django_equivalences = { 'eq': '%s__iexact', 'lt': '%s__lt', 'le': '%s__lte'} |
Això éns permet convertir fàcilment una expressió d'igualtat per a un camp amb l'equivalent Django
1 2 | field = self.request.GET.get('searchField') op = request.GET.get('searchOper') % field |
Amb això ens trobam amb dos problemes: què feim amb les condicions del tipus "no és igual a" o "no comença amb" i què feim amb les expressions "in". I si m'apurau amb un problema més, com passam aquestes condicions que constriuim (o construirem) dinàmicament a Django. Perquè Django espera construccions del tipus Entry.objects.filter(id__gt=4) i nosaltres el que farem és construir-les.
Si ens fixam en la documentació veurem que el problema dels inclosos i dels exclosos està resolt per filter i excludes. Amb la mateixa construcció i generant la consulta amb excludes en lloc de filter ja ho tenim. Sols necessitam doncs que el mapeig a més de la plantilla ens retorni el tipus a que correspon filter o bé excludes.
1 2 3 4 | django_equivalences = { 'eq': ('filter', '%s__iexact'), 'ne': ('exclude','%s__iexact'), 'lt': ('filter', '%s__lt')} |
Suposant que la nostra classe es diu Entry podríem fer quelcom semblant a això:
1 2 3 4 5 6 7 | searchField = request.GET.get('searchField') op = self.request.GET.get('searchOper') value = request.GET.get('searchString') tipo, filtro = django_equivalences[op] query = {filtro%searchField:value} record_list = Entry.objects.filter(**query) if tipo == 'filter' \ else Entry.objects.exclude(**query) |
Fitxem-nos com hem fet la construcció dinàmica. En lloc de crear el codi el que feim és crear un diccionary query que té com a clau l'operació de filtratge damunt el camp que jqGrid ens passa i com a valor, el que volem cercar.
Ens queda encar un petit detall, què passa amb l'in, a Django mapejaria com icontains i espera una llista de valors com a paràmetre. Està clar que ho podríem tractar com un cas particular, però anem-ho a fer divertit, el que farem serà afegir una funció com a valor del mapeig que ens dirà el que hem de fer amb el valor, llevat de in i ni (includes i not includes) no s'ha de fer res, millor dit la funció ha de retornar el mateix valor que li passam (us sona la funció identitat?) i en cas contrari convertirem el valor que ens passi a una llista de valors mitjançant el mètode split(',').
Així doncs el nostre mapeix seria:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 | identity = lambda x:x django_equivalences = { 'eq': ('filter', '%s__iexact', identity), 'ne': ('exclude','%s__iexact',identity), 'lt': ('filter', '%s__lt',identity), 'le': ('filter', '%s__lte',identity), 'gt': ('filter', '%s__gt',identity), 'ge': ('filter', '%s__gte',identity), 'bw': ('filter', '%s__istartswith',identity), 'bn': ('exclude','%s__istartswith',identity), 'in': ('filter', '%s__in',lambda x: x.split(',')), 'ni': ('exclude','%s__in',lambda x: x.split(',')), 'ew': ('filter', '%s__iendswith',identity), 'en': ('exclude','%s__iendswith', identity), 'cn': ('filter', '%s__icontains', identity), 'nc': ('exclude','%s__icontains', identity) } field = request.GET.get('searchField') op = self.request.GET.get('searchOper') value = request.GET.get('searchString') try: tipo, filtro, f = django_equivalences[op] except KeyError: tipo, filtro, f = django_equivalences['eq'] filtro = str(filtro % field) query = {filtro:f(value)} record_list = Entry.objects.filter(**query) if tipo == 'filter' \ else Entry.objects.exclude(**query) |
Com veis un exercici interessant perquè hi ha un poc de tot: funció lambda, us dels diccionaris, ús de les funcions com a objectes i pas de paràmetres a una funció mitjançant un diccionari.
Traducciones/Translations by apertium
1 comentari, 0 trackbacks (URL) , Tags: Python Django
Prova de concepte: APSLHOTELS
Escrit per Aaloy a 11 de October , 2009 a les 10:15 a.m.
Ahir al vespre vàrem obrir al públic la nostra prova de concepte del que pot ser una plana de reserves d'hotels: http://hotelestest.apsl.net/. I quan dic prova de concepte en refereixo a poder mostrar el que es pot fer amb la tecnologia Python+Django.
A la nostra comunitat un tant per cent elevat de la informàtica gira al voltant del negoci turístic. Quan vas a parlar amb algú del sector i li dius que programaràs la seva aplicació amb Python i Django sovint ho troben un poc estrany, estan acostumats al PHP i com a molt al Java o .Net, no veuen clar que es pugui fer.
Aquesta prova de concepte vol mostrar el senzill que és i algunes de les possibilitats que té la tecnologia actualment. Per fer la demostració volíem que la plana es connectàs a un XML/SOAP d'un tercer ja que d'aquesta manera es cobreix l'ús més habitual: fer una aplicació web on el motor i la capa de presentació estan separades per un servei XML. Dels proveïdors amb que contactàrem vàrem tenir molt bona acollida i suport de la gent de Valadis/Versys, ens varen donar moltes facilitats des del començament i el seu XML és molt senzill de mapejar. Està clar que les idees que es presenten es poden desenvolupar amb gairebé qualsevol proveïdor XML d'hotels, però vàrem tenir la sort de poder topar amb aquesta gent i que ens donàs accés al seu sistema de proves sense cap problema. Des d'aquí moltes gràcies.
Així doncs, tenim una aplicació B2C que permet reserva hotels i que connecta al motor XML de test d'un proveïdor extern. Com a bon entorn de test he de dir que es troba en contínua evolució i que les dades que es mostren són sols un subconjunt molt limitat de les que es tendrien en un entorn de producció. No us fixeu massa en les dades, sinó en el bessó del que es mostra. Per cert, podeu provar tot el que volgeu, tanmateix no es fa cap tipus de pagament ni control a la tarja que poseu.
Feta aquesta introducció anem a veure un poc la bèstia:
El desenvolupament
Com he dit l'aplicació està desenvolupada fent servir Python+Django, pel control de versions s'ha fet servir Subversion i pel control del projecte s'ha fet servir Trac.
Per crear l'aplicació hem mapejat l'XML a objectes Python amb la llibreria lxml, una llibreria que envolcalla les llibreries C libxml2 i libxslt. La velocitat del C i l'expressivitat de Python.
Per la depuració i programació hem fet servir: django-extensions, debug_toolbar, ipdb i ipython. La primera llibreria ens proporciona tot un conjunt de funcions còmodes per l'administració de l'aplicació, debug_toolbar ens diu quina plantilla es fa servir en cada pantalla, les seves herències, les sql que es generen, etc. ipdb és un depurador de línia de comandes, com el pdb però amb autocompletat i ressaltat de sintaxi. ipython és una consola Python, Django l'aprofita si està instal·lada, proporciona autocompletat, resaltat de sintaxis i un gran nombre de comandes extra.
Els editors més habituals han estat Netbeans, Eclipse, Vim, Kate i Notepad++ (per Windows). L'editor importa poc, tots estan configurats per fer feina amb UTF-8 i els tabuladors configurats a 4 espais. Amb això en tenim prou per poder fer servir en qualsevol moment l'editor que més ens agradi. Particularment vaig d'un a l'altra segons la màquina que faig servir.
Per les planes de contingut estàtic hem fet servir django-page-cms, la idea és mostrar com un gestor de continguts es pot integrar dins l'aplicació.
Els css i js es comprimeixen abans de servir-se gràcies a django-compress. Això vol dir que podem fer cachés mesos. Quan el css o el js canvii sols hem de tornar a generar els arixus comprimits i la llibreria els posa un nou nom.
La configuració de sistemes
L'aplicació s'executa en un chroot propi dins un servidor propi que duu moltes més aplicacions.
vendor_id : GenuineIntel cpu family : 6 model : 15 model name : Intel(R) Pentium(R) Dual CPU E2180 @ 2.00GHz stepping : 13 cpu MHz : 1994.999 cache size : 1024 KB top - 08:52:55 up 729 days, 13:56, 0 users, load average: 0.07, 0.02, 0.00 Tasks: 126 total, 1 running, 125 sleeping, 0 stopped, 0 zombie Cpu(s): 0.0%us, 0.2%sy, 0.0%ni, 99.8%id, 0.0%wa, 0.0%hi, 0.0%si, 0.0%st Mem: 1015232k total, 984036k used, 31196k free, 154472k buffers Swap: 2097144k total, 2948k used, 2094196k free, 90536k cached
L'entorn d'execució s'independitza de la màquina amb aquest chroot i a més es va crear un entorn virtual separat per l'aplicació amb virtualenv, això permet tenir un control total damunt les llibreries i el Python Path.
Seguint les millors pràctiques, hem separat els dominis que serveixen el contingut estàtic del contingut dinàmic. Això és prou senzill amb Django i sols has de recordar escriure les url amb {{MEDIA_URL}}.
Donat que és una màquina amb altres aplicacions volíem que el consum de memòria fos mínim. Per això hem optat per que el servidor http no fos un Apache sinó nginx que tenim configurat tant per servir el contingut estàtic com per a fer de proxy invers cap al motor WSGI que executa l'aplicació Django.
Pel motor WSGI triarem CherryPy (d'aquí no res posarem Tornado). Amb això tenim una relació de consum de recursos de màquina i rendiment molt òptima i una escalabilitat horitzontal fabulosa. Posant-hi un balancejador podem anar copiant i aferrant chroots i dins cada chroot podem tenir tantes instàncies de l'aplicació con suporti la màquina.
És veritat que no calia complicar-se tant per a una prova de concepte, però la idea no és tan sols mostrar el que es pot fer en programació, sinó com la tecnologia i el frameworks s'adapten a les necessitats actuals i futures de rendiment. És a dir, que s'escala cap amunt i cap avall.
L'aplicació
A la primera plana hi trobam el cercador, el típic cercador afegiria. Aquí utilitzam jquery-ui pels widgets de calendari. Hi ha la demostració de l'autocompletat als apartats de destinos i nombre de hotel.
El errors es mostren a la plana de manera poc intrusiva. Per això s'utilitzen plugins de jquery i les capacitats de serialització json de Python i Django, junt amb la validació de formularis de Django. És molt més senzill del que sona.
També es pot veure la utilització que es fa del CMS en les planes de Aviso Legal per exemple i també hi podem trobar dos tipus d'ofertes.
Aquestes es mantenen dins la part d'administració. No són gaire sofisticades, però serveixen per mostrar el dinamisme que es pot aconseguir i el concepete d'url semàntica.
Picant damunt una oferta per exemple, anirem a la plana de resultats. Recordem que anam contra un entorn de proves i que les dades són les que són. La idea és poder mostrar com les dades de l'hotel es carreguen dinàmicament mitjançant una cridada AJAX i com l'aplicació manté en sessió les dades de la selecció.
Hem posat també un filtre js. Pitjant damunt el filtre de categoria desapareixen els hotels amb aquesta categoria. Es pot utilitzar el mateix concepte per a filtrar els resultats per altres camps (preu, tipus d'habitació, ...)
A partir d'aquí ja anam a les pantalles que obtenen la informació de compra i a la de gràcies. Res destacable en aquest punt que no hagi sortit abans.
Pas a producció
Hi ha gent que ens ha demanat pel pas a producció d'aquesta aplicació. Recordau que és una prova de concepte, no hi haurà pas a producció, serveix per mostrar idees de programació, conceptes tecnològics i d'optimització.
El que si es pot fer és desenvolupar una solució a mida amb aquestes tecnologies, però serà sempre per a un tercer i si es fes hi hauria molta feina que en la prova de concepte s'ha obviada:
- Continguts estàtics: hem posat lorem ipsum gairebé per tot.
- Filtres
- Paginació de resultats
- Connexió amb una passarel·la de pagament
- Generació del bono
- Enviament del bono al client
- Backoffice de control
L'aplicació tal com està i en les seves possibles evolucions té per objectiu que gent com nosaltres que ens dedicam al desenvolupament Python i Django poguem mostrar al món turístic el que es pot fer. Consideram l'aplicació com un Projecte Mascota: hi anam dedicant hores quan ens fa ganes.
Traducciones/Translations by apertium
2 comentaris, 0 trackbacks (URL) , Tags: Python Django
Simple Web services
Escrit per Aaloy a 23 de September , 2009 a les 8:33 p.m.
L'altra dia ens van donar un "curset" d'introducció a TIBCO i ho pos entre cometes perquè curset potser no és la paraula adient, ja que més aviat va ser una presentació comercial. Tot i això i entre capada i capada d'avorriment vaig tenir l'ocasió de parlar amb el formador (o deformador) dels serveis dins les possibilitat que ofereix TIBCO. Deia que era molt senzill agrupar serveis i generar el WSDL corresponent per a ser consumit fàcilment per altres aplicacions.
No dubt que això sigui així però les meves reticències fonamental venen donades pel fet de que quan vols que el teu servei sigui consumit externament, el que has de fer és facilitar al màxim que la gent ho pugui fer.
El SOAP és un protocol que va néixer per ser simple i que ha acabat essent complexe fins al punt de fer-se immanejable, sobre tot gràcies a les extensions que es varen introduir per a facilitar-ne la creació i consum per llenguatges concrets. L'article de Peter Lacey del 2006, anomenat The S Stands for Simple en fa una discussió emprant el mètode socràtic que s'ho paga llegir.
Quan per raons de negoci (el cap ho ha exigit, per dir-ho més clar) hem tingut que fer els web services amb SOAP el que hem procurat sempre és controlar molt bé el resultat final del WSDL de manera que fos fàcilment consumible tant pel qui l'ha creat (Java en el nostre cas) com per altres llenguatges (Python per exemple). Aquest resultat difícilment s'obté si qui genera el SOAP suposa que és el mateix que l'ha de consumir i per tant no veu la complexitat des del punt de vista d'un tercer, sinó que genera el WSDL de manera que la transformació inversa li sigui favorable.
Per una altra banda afegir la capa SOAP i consumirla té un cost (el famós payload) en termes de capacitat de procés i ampla de banda. Si alguan cosa té és que entre namespaces, definicions i subdeficinions, un missatge que podria de ser de pocs bytes multiplica el seu pes per 100 o per 1000.
Per una altra banda, la complexitat del WSDL fa que sovint no basti el WSDL com a documentació (un dels objectius del SOAP) sinó que s'ha d'adjuntar una documentació addicional explicant cada missatge, quins són els paràmetres, etc. Així que argumentar que el WSDL s'autodocumenta és pecar un poc d'ingenuo i optimista.
El SOAP és bo si es mantén simple, el problema és que fer-ho simple i consumible fàcilment duu molta feina i se suposava que això ens ho hauria d'evitar.
Ara mateix l'alternativa és tornar a la simplicitat. Evitar la sobrecàrrega de feina de màquina i gent que suposa el SOAP i anar cap a protocols més senzills:
-
XML+HTTP. Amb una eina d'extracció de documentació podem fer la web de documentació i proves al mateix temps que escrivim el servei. Activant la compressió del gzip del servidor ens queda tot d'allò més compacte.
-
XML-RPC. Anam un poc més enllà. Podem consumir l'XML com si d'una llibreria es tractàs. Igual que abans la documentació dins el codi ens pot permetre estalviar molta feina. David Fisher ha fet un exemple molt instructiu amb Django d'aquest concepte.
-
Json-RPC o Json+HTTP. El Json s'ha convertit en un format d'intercanvi potent i senzill. Perquè no utilitzar-ho? Es pot consumir gairebé des de qualsevol llenguatge modern i la transformació a objectes nadius és trivial.
-
REST. Utilitzam les URL si l'HTTP per al nostre intercanvi d'informació. És el que mou la web. Se li ha donat un nom i un conjunt de criteris per a formalitzar el mecanismes d'accés als serveis.
El gran avantatge de tot això és que la generació del servei no requereix de llenguatges "empresarials", sinó que ho podem fer fins i tot amb qualsevol microframework (web.py, Tornado, ...) amb Django o amb qualsevol cosa que ens permeti respondre a una petició http i tractar-ne les capçaleres.
Per Django per exemple tenim el projecte Django-Piston que ens permet crear una API REST per als nostres projectes d'una manera molt poc intrusiva.
Fa una bona temporada que el SOAP i els WSDL que hem fet sols estan en manteniment. Si els tengués que fer ara i depengués de mi o serien serveis REST o bé XML purs i segurament tampoc estarien fets en Java.
Traducciones/Translations by apertium
3 comentaris, 0 trackbacks (URL) , Tags: Informàtica Python Django
Comenta, que serveix
Escrit per Aaloy a 21 de September , 2009 a les 9:02 p.m.
Quan un fa programes per aprovar l'assignatura sap que ha de posar comentaris perquè si no no és considera un codi professional, així que els posam per obligació, perquè hi han de ser, però potser sense adonar-nos de la seva utilitat real.
Els programes que feim per aprovar rarament són molt complexos, són exemples que es poden fer en poques setmanes i que rarament s'hauran de mantenir. La vida real és força diferent, els programes tenen un cicle de vida llarg, d'anys o dècades i s'han de fer millores correctives i adaptatives.
Aquell programa que ahir teníem molt clar al cap d'uns anys potser ja no ho tindrem tant i els comentaris que posàrem faran que tot ens resulti molt més fàcil d'entendre. I ja no diguem res si no som nosaltres el que hem de mantenir el codi!
Els bons comentaris ens haurien de dir el perquè del codi, què fa, què espera a l'entrada i què a la sortida. El codi ens dirà com ho fa. Això vol dir que el codi també ha de ser llegible, teclejar una mica més no costa tant i podem posar noms a les variables, a les classes, a les funcions, que tenguin significat.
Python per exemple té dues castes de comentaris, els que es fan servir per documentar el per què, i que sovint es presenten en forma de cometes triples i que es lliguen al doc (i a l'ajuda) de l'objecte i els comentaris fets amb la parrilla (#) que s'han de fer servir per comentar aspectes del codi que no necessitin. Java té quelcom semblant.
Però podem anar una mica més enllà. L'altra dia al twitter algu deia que li quedava fer la documentació de la pràctica. També molt habitual :) Posar bons comentaris ens pot estalviar una bona feina i no tan sols això, sinó que serà text de qualitat ja que s'ha fet amb menys presses i amb la ment més fresca en relació al que se està fent.
I encara una utilitat més: el pseudocodi. Particularment quan he de fer una cosa complexa començ per escriure'n les passes i les pos en forma de comentari. D'aquesta manera tenim més clar el que s'ha de fer i el temps s'aprofita, ja que aquest pseudocodis seran després els comentaris que ajudaran a seguir millor el codi.
No vull acabar sense citar una eina fantàstica per a la documentació: l'Sphinx us podeu adonar d ela bona feina que fa amb la documentació de Python o Django per exemple. Sphinx té una cosa molt bona, ens permet extreure els comentaris del nostre codi Python i afegir-los a la documentació, de manera que du el reaprofitament i la màxima DRY al món dels comentaris i la documentació. Per poc que us agradi el RestructuredText no deixeu de fer-li una ullada a aquesta eina, fins i tot si no programau en Python.
Per cert, en el seu dia vaig deixar un petit tutorial de RestructuredText a Bulma.
Traducciones/Translations by apertium
2 comentaris, 0 trackbacks (URL) , Tags: Python Django
Django CherryPy vs Tornado
Escrit per Aaloy a 14 de September , 2009 a les 7:28 p.m.
Fa pocs dies s'ha alliberat un motor web anomenat Tornado part de la tecnologia que mou FriendFeed.
És un servidor no bloquejant que promet molt en quan a velocitat, pensat per moure FriendFeed i escalar la càrrega que faci falta.
Independentment de que es pugui desenvolupar amb ell tot una aplicació, sols la part de servidor http ja és prou interessant com per fer-li una ullada. En un post a la llista de Django Bret Taylor, un dels autors de Tornado, postejava com connectar el motor amb Django mitjançant WSGI.
Actualment una de les configuracions que més m'agraden per desplegar aplicacions és la combinació nginx+CherryPy WSGI+Django (amb balancejadors, memcached i tota la pesca gràcies a la bona feina de Bernat i Pere), així que estava força intrigat per veure com respondria Tornado.
La base estava feta: una mini-aplicació "hello world" que vaig escriure per comparar aquesta tecnologia/arquitectura amb diferents frameworks PHP, així que ha está un no res actualitzar la darrera versió de CherryPy WSGI i crear una nova aplicació semblant per Tornado.
El codi font és a:
I ara els resultats:
La màquina és un PPC 1 Gb de RAM, 2 CPU de 2 GHz
Amb Tornado WSGI:
ab -c 10 -t 60 http://localhost:8888/
This is ApacheBench, Version 2.3 <$Revision: 655654 $>
Copyright 1996 Adam Twiss, Zeus Technology Ltd, http://www.zeustech.net/
Licensed to The Apache Software Foundation, http://www.apache.org/
Benchmarking localhost (be patient)
Completed 5000 requests
Completed 10000 requests
Completed 15000 requests
Completed 20000 requests
Completed 25000 requests
Finished 25022 requests
Server Software: TornadoServer/0.1
Server Hostname: localhost
Server Port: 8888
Document Path: /
Document Length: 266 bytes
Concurrency Level: 10
Time taken for tests: 60.019 seconds
Complete requests: 25022
Failed requests: 0
Write errors: 0
Total transferred: 9333206 bytes
HTML transferred: 6655852 bytes
Requests per second: 416.90 [#/sec] (mean)
Time per request: 23.987 [ms] (mean)
Time per request: 2.399 [ms] (mean, across all concurrent requests)
Transfer rate: 151.86 [Kbytes/sec] received
Connection Times (ms)
min mean[+/-sd] median max
Connect: 0 0 0.1 0 6
Processing: 3 24 1.9 24 68
Waiting: 0 24 1.9 23 68
Total: 7 24 1.9 24 68
Percentage of the requests served within a certain time (ms)
50% 24
66% 24
75% 24
80% 24
90% 25
95% 26
98% 27
99% 28
100% 68 (longest request)
Usant CherrPy amb 3 threads (la millor configuració per la meva màquina segons les meves proves)
ab -c 10 -t 60 http://localhost:8088/
This is ApacheBench, Version 2.3 <$Revision: 655654 $>
Copyright 1996 Adam Twiss, Zeus Technology Ltd, http://www.zeustech.net/
Licensed to The Apache Software Foundation, http://www.apache.org/
Benchmarking localhost (be patient)
Completed 5000 requests
Completed 10000 requests
Completed 15000 requests
Completed 20000 requests
Finished 21500 requests
Server Software: CherryPy/3.0.3
Server Hostname: localhost
Server Port: 8088
Document Path: /
Document Length: 266 bytes
Concurrency Level: 10
Time taken for tests: 60.001 seconds
Complete requests: 21500
Failed requests: 0
Write errors: 0
Total transferred: 8299000 bytes
HTML transferred: 5719000 bytes
Requests per second: 358.33 [#/sec] (mean)
Time per request: 27.907 [ms] (mean)
Time per request: 2.791 [ms] (mean, across all concurrent requests)
Transfer rate: 135.07 [Kbytes/sec] received
Connection Times (ms)
min mean[+/-sd] median max
Connect: 0 0 20.5 0 2999
Processing: 3 28 10.7 26 413
Waiting: 3 26 10.3 24 412
Total: 3 28 23.1 26 3031
Percentage of the requests served within a certain time (ms)
50% 26
66% 28
75% 30
80% 31
90% 34
95% 38
98% 43
99% 48
100% 3031 (longest request)
En resum:
416.90 req/s for Tornado WSGI 358.33 req/s for CherryPy
Tornado fa que una CPU es posi al 100% gairebé des del començament de l'execució de la comanda ab, CherryPy també posa la CPU al 100% però ho fa més tard i potser això explica la diferència, potser algún gurú de CherryPy em podrà dir una configuració millor que la que tinc.
58 peticions representen un 16% més de peticions que pot aguantar Tornado respecte de CherrPy, i si us hi fixau el codi que es necessita per executar-hi Django és pràcticament calcat gràcies al WSGI.
Seguesc fent proves, però per ara la cosa pinta molt bé.
Traducciones/Translations by apertium
2 comentaris, 0 trackbacks (URL) , Tags: Python Django
Decoradors a Python
Escrit per Aaloy a 30 de August , 2009 a les 7:22 p.m.
Què és un decorador
Un decorador és el nom d'un patró de disseny. Els decoradors alteren de manera dinàmica la funcionalitat d'una funció, mètode a classe sense tenir-ne que fer subclasses o canviar el codi font de la classe decorada. En el sentit de Python un decorador és quelcom més, inclou el patró de disseny, però van més allà, Bruce Eckel els assimila a les macros de Lisp.
Els decoradors i la seva manera d'utilitzar-se ens ajuden a fer el nostre codi més net, a autodocumentar-lo i a diferència d'altres llenguatges de programació no requereixen que ens aprenguem un altre llenguatge de programació (com passa amb les anotacions de Java per exemple). En la seva utilització podem atracar-nos a la programació orientada a aspectes (AOP) o utilitzar-los per a afegir sistemes de control a les nostres funcions, de log, caché, ... Les possibilitats són infinites. El decoradors formen part de Python des de la versió 2.4 i com diu Michele Simionato ens aporten el següent:
- Redueixen el codi comú i repetitiu (l'anomenat codi boilerplate).
- Afavoreixen la separació de responsabilitats del codi
- Augmenten la legibilitat i la mantenibilitat
- Els decorador són explícits.
Aquesta potència té un preu: en rendiment (que s'haurà d'avaluar per a cada aplicació) i en complexitat a l'hora de desenvolupar-los. Un decorador típic veurem que és molt bo d'escriure, però la cosa es complica un poc quan volem passar paràmetres o mantenir la signatura del mètode. Aquesta complexitat no és tant pel codi que s'ha d'escriure sinó perquè hem de recordar com s'ha d'escriure el decorador per a cada cas.
Afortunadament veurem que gent com Michele Simionato han desenvolupat paquets que ens simplifiquen molt la vida. Tot i això i abans de fer servir aquestes utilitats convé saber què són i desenvolupar-los sense ajuda. És un poc com aprendre's les taules de multiplicar i després ja utilitzar la calculadora.
Classificació dels decoradors
Podem dividir els decoradors en grups:
- Segons els paràmetres que admeten:
- No admeten paràmetres
- Sí admeten paràmetres
- Segons si preserven la signatura del mètode al que decoren:
- Decoradors no que preserven la signatura
- Decoradors que si la preserven
Els decoradors més senzill són aquells que no admeten paràmetres i no preserven la signatura
Un decorador que no fa res
Per començar crearem un decorador que el que farà es convertir qualsevol funció en un /dev/null, és a dir, no retornarà res i no farà res amb la funció.
1 2 3 4 5 6 7 8 | def forat_negre(f): def none(): pass return none @forat_negre def di_hola(): return "hola" |
Si executam di_hola() no tendrem cap resultat, millor dit tindrem None
La sintaxi @ del decorador de Python és el que s'anomena syntactic sugar, és a dir, una manera d'escriure les coses que ens simplifica la legibilitat, però fet i fet es podria escriure perfectament com
1 2 | di_hola = forat_negre(di_hola) di_hola() |
i tendríem el mateix que fa el decorador. Recordem que les funcions són objectes i que es poden assignar i passar com a paràmetres a Python.
Tot i la senzillesa de l'exemple ens serveix per veure el següent:
Un decorador no és més que un envolcall cap a una funció i per tant ha de retornar una funció, més concretament un callable, per a entendre'ns, qualsevol cosa que posant-hi un doble parèntesi al costat () no peti.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | def retorna_objecte(f): ....: def obj(): ....: return object() ....: return obj ....: In [17]: def di_hola(): ....: return "Hola" ....: In [18]: di_hola = retorna_objecte(di_hola) In [19]: di_hola() Out[19]: <object object at 0xf7f745e8> |
Al nostre decorador forat_negre li hem passat una funcició sense paràmetres, però si li passam paràmetres ens trobarem una sorpreseta
1 2 3 4 5 6 7 8 | @forat_negre def suma(a,b): return a,b suma(2,3) TypeError Traceback (most recent call last) TypeError: none() takes no arguments (2 given) |
que per una altra banda és del tot normal, hem definit el forat_negre de tal manera que retorna una funció sense paràmetres, així que si li intentam passar els paràmetres que tenia la funció decorada senzillament es queixa i peta.
Anem a definir un poc millor el nostre decorador per a que no ens passi així i poder admetre el mateixos paràmetres que la funció decorada
1 2 3 4 5 6 7 8 9 10 11 12 | def forat_negre(f): "d'aquí no surt res" def none(*args, **kw_args): pass return none @forat_negre def suma(a,b): "suma dos parametres qualsevols si pot" return a+b suma(2,2) |
Ara ja no dona error. Així doncs una altra conclusió: a més de tornar una funció, hem de procurar que la definició de las funció que tornam admeti al manco els mateix nombre de paràmetres que la funció que volem decorar. Si no sabem quants són aquests ens curam en salut amb args i kw_args.
Fixem-nos que no hem mantingut la signatura de la funció i com a experiment intentau fer un help(suma). Tornarem damunt això un poc més endavant. Ara per ara ja sabem com crear decoradors simples a partir d'una funció.
Fent decoradors no intrusius
Si heu fet un help(suma) o un suma.__name__ potser un haureu sorprés en veure que le nom de la funció és none en lloc de l'esperada suma. Si pensau amb el que hem fet tampoc és d'extranyar, fet i fet hem substituït la funció original per
una altra, recordem que el decorador f aplicat damunt la funció g és equivalent a fer g = f(g).
El que és aconsellable és que el decorador sigui capaç de mantenir la documentació i el nom de la funció que decora, ja que d'aquesta manera es simplifica l'ús de la funció i els autocompletadors de codi no es tornen bojos.
Això ho podem fer de dues maneres: la llarga i la curta
La manera llarga
1 2 3 4 5 6 7 | def forat_negre(f): def none(*args, **kw_args): pass none.__doc__= f.__doc__ none.__dict__= f.__dict__ none.__name__= f.__name__ return none |
Amb les tres instruccions adicionals que hem posat tornar a recuperar les metadades de la funció original que passam al decorador. Si hara feim un help veurem que es fa damunt el nom de la funció correcta suma i que l'ajuda també és la seva.
Help on function suma in module __main__:
suma(*args, **kw_args)
Suma dos parametres qualsevols si pot
Fixem-nos en la signatura de la funció no s'ha preservar. Abans admetia dos paràmetres i ara n'admet un nombre qualsevol. Per la majoria de casos això no té més importància, però al final de l'article veurem com es pot resoldre.
La manera curta
Com que el tema de reservar les metadades és força interessant i comú, al mòdul functools hi trobam la funció wraps que és en sí mateixa un decorador i que fa aquesta funció. D'aquesta manera el codi anterior quedaria:
1 2 3 4 5 6 7 | from functools import wraps def forat_negre(f): @wraps(f) def none(*args, **kw_args): pass return none |
Fixau-vos que hem fet servir un decorador per crear un altre decorador. Insistirem en aquest tema més tard.
Un decorador amb arguments
El decorador que hem fet a l'apartat anterior era prou simple, feia ben poca cosa i no tenia paràmetres. Si volem fer decoradors hem de fer primer de tot que siguin útils, i també ens trobarem amb la necessitat de que aquests decoradors admetin paràmetres.
A Django, per exemple, podeu trobar que el decorador de cache admet paràmetres que ens permet dir-li durant quan de temps ha de cachejar els resultats, o el decorador vary_on_headers, que ens permet modificar el contingut de la resposta de les vistes afegint les capçaleres que indiquem.
Anem a veure com ho podem aconseguir nosaltres. També hi ha dues maneres de fer-ho, la clara i la complexa. La manera clara és la que recoman i utilitza una classe per a fer el decorador, la complexa requereix més esforça per a entendre què està fent el decorador, és més curta, però personalment preferesc un codi més legible.
De la mateixa manera els decoradors que hem fet com a funcions es poden crear com a classes, però en aquest cas, crec que la definició en forma de funcions és més bona de seguir, i ens permetrà distingir clarament entre els dos tipus de decoradors: el que no admeten paràmetres que es construeixen preferentment mitjançant funcions i els que admeten paràmetres, que es construeixen preferentment fent servir classes.
Per seguir amb el forat negre, ara el nostre exemple el que farà es mostrar el resultat o no segons li roti. Per això el que farem serà passar-li una funció com a paràmetre que en ser executada determinarà si s'ha de mostrar el resultat de la funció decorada o no
El mètode clar de fer decoradors amb arguments
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 | #!/usr/bin/env python # -*- coding: UTF-8 -*- import random class forat_negre_sonat(object): "Un decorador amb fam" def __init__(self, mostrar): self.mostrar = mostrar def __call__(self, f): def none(*args, **kw_args): if self.mostrar(): return f(*args, **kw_args) else: return "Nop" return none @forat_negre_sonat(mostrar = lambda :random.choice((True, False))) def suma(a, b): "Suma dos elements que li passam com a paràmetre" return a+b if __name__=="__main__": print suma(2,3) print suma(5,6) print suma(9,5) |
Fitxem-nos amb que hem fet:
-
Hem creat una classe Python que al seu constructor (l'init) agafa el paràmetre o paràmetres que vulguem. És un constructor normal, així que admet paràmetres per defecte per exemple.
-
Recordem que el decorador hem dit que ha de ser un objecte cridable (callable), a una classe, la cridabilitat la dóna el mètode call. Aquesta classe la definirem de manera que agafi la funció a decorar com a paràmetre. D'aquesta manera tenim accés tant als paràmetres del decorador, que hem passat al constructor, com a la funció decorada, que hem passat com a paràmetre al call.
Després d'això ja sols en queda encapsular la cridada com ho fèiem al cas anterior, retornant el decorador en lloc de la funció decorada.
A l'exemple el que he fet és mostrar que el paràmetre pot ser el que nosaltres vulguem, en concret he passat una funció anònima, creada amb lambda que és la que s'encarrega d'establir l'aleatoritat del resultat.
Si voleu podem fer aquest decorador una mica més complet, fent que admeti a més de funcions valors i que preservi el nom i documentació de la funció decorada.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 | #!/usr/bin/env python # -*- coding: UTF-8 -*- import random class forat_negre_sonat(object): "Un decorador amb fam" def __init__(self, mostrar=None): self.mostrar = mostrar def __call__(self, f): def none(*args, **kw_args): if callable(self.mostrar): opcion = self.mostrar() else: opcion = self.mostrar if opcion: return f(*args, **kw_args) else: return "Nop" none.__name__ = f.__name__ none.__doc__ = f.__doc__ return none @forat_negre_sonat(mostrar = lambda :random.choice((True, False))) def suma(a, b): "Suma dos elements que li passam com a paràmetre" return a+b @forat_negre_sonat(mostrar=True) def resta(a,b): return a-b if __name__=="__main__": print "Exemple amb %s " % suma.__name__ print suma(2,3) print suma(5,6) print suma(9,5) print "Exemple amb %s " % resta.__name__ print resta(2,3) print resta(5,6) |
El mètode enrevessat de fer decoradors amb arguments
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | def forat_negre_dos(mostrar): def wrap(f): @wraps(f) def wrapped_function(*args, **kw_args): if callable(mostrar): opcion = mostrar() else: opcion = mostrar if opcion: return f(*args, **kw_args) else: return "Nop" return wrapped_function return wrap |
Bé, enrevessat, el que es diu enrevessat no ho és, per una cosa tan simple no té massa història, però fixau-vos que és un poc més mal de seguir.
El primer que hem fet és definir la nostra funció, on hi hem posat els paràmetres que admet. Aquest funció retorna una altra funció que admet un argument, que és la funció decorada, que a la seva vegada admet un nombre indeterminat d'arguments (recordem que això ho estam forçant nosaltres).
Com que la segona funció, wrapped_function està definida dins wrap, té accés al paràmetre del decorador i pot actuar en conseqüència.
Encadenant decoradors
Els decoradors es poden encadenar, és a dir, una funció pot tener tans decoradors com faci falta i necessitem, sols limitats pel nostre sentit comú i la legibilitat del programa. Dos decoradors són habituals, tres no es veuen gaire, quatre o més són per pensar-s'ho.
Per a l'exemple manllevaré un dels decoradors més útils, el memoize, que ens permet cachejar una funció segons els seus paràmetres. Al Python Decorator Library hi ha una implementació del patró memoize prou senzilla de seguir amb el que ara sabem i a més ens servirà per completar la construcció de decoradors sense paràmetres fent servir una classe.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 | class memoized(object): """Decorator that caches a function's return value each time it is called. If called later with the same arguments, the cached value is returned, and not re-evaluated. """ def __init__(self, func): self.func = func self.cache = {} def __call__(self, *args): try: return self.cache[args] except KeyError: self.cache[args] = value = self.func(*args) return value except TypeError: # uncachable -- for instance, passing a list as an argument. # Better to not cache than to blow up entirely. return self.func(*args) def __repr__(self): """Return the function's docstring.""" return self.func.__doc__ |
A diferència de la construcció amb paràmetres, al constructor de la classe memoized s'hi posa com a paràmetre la funció a decorar, i al mètode call hi van els paràmetres de la funció, en lloc de la funció a decorar com es feia a l'altre mètode.
Per què s'ha fet servir aquesta manera si l'altra és més senzilla? Dons perquè necesitam mantenir en memòria la caché i el que fa és mantenir-la en un diccionari dins de la mateixa classe. Si la caché fos externa (amb memcached per exemple), això s'hauria pogut fer perfectament en forma de funció.
A més definirem un decorador que ens servirar per indicar quan entram a la funció i comprovar el decorador memoized.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | def log(f): "Registra l'execució de la funció" def wrap(*args): print "Excutant %s, args: %s" % \\ (f.__name__, ",".join(str(x) for x in args)) return f(*args) return wrap @memoized @log def fibonacci(n): "Return the nth fibonacci number." if n in (0, 1): return n return fibonacci(n-1) + fibonacci(n-2) print fibonacci(12) |
Provau d'executar aquest codi amb i sense la funció memoized. Amb els dos decoradors activus veureu que el cada decorador agafa com a entrada la funció ja decorada que surt del decorador que té més avall. Així el memoized agafa com a entrada la funció fibonacci ja decorada amb el log.
Podeu fer la prova amb un exemple més simple:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 | #!/usr/bin/env python # -*- coding: UTF-8 -*- def uppercase(f): "Dada una función f que devuelve un string lo pasa todo a mayúsculas" def wrap(): return f().upper() return wrap def make_bold(f): "Dada una función f que devuelve un string le añade los tags de bold" def wrap(): return "<strong>%s</strong>" % f() return wrap @make_bold @uppercase def say_hello(): return "Hello world" print say_hello() |
Provau canviant l'ordre dels decoradors i veureu perfectament com es van aplicant els decoradors des de la funció per amunt. A l'exemple primer es converteix el "Hello word" a majúscules i després se li apliquen els tags de negreta.
La signatura pendent
Abans d'acabar ens queda un tema pendent: la signatura. Els decoradors que hem creat poden preservar el nom i la documentació de la funció que decoren, però no preserven la signatura, és a dir, el nombre de paràmetres que li passam.
Michele Simionato ha escrit un mòdul excel·lent anomenat decorator que extén la utilizació dels decoradors, mantén la signatura de la funció, el nom i la documentació, i a més ens dona la possibilitat de crear factories de decoradors. Una eina per a tenir sempre a mà. Amb aquest mòdul podríem escriure el codi de l'exemple anterior com:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 | from decorator import decorator @decorator def uppercase(f, *args): "Donada una funció f que retorna un string ho passa a majúscules" return f(*args).upper() @decorator def make_bold(f, *args): "Afegeix el tag strong a la sortida de la funció" return "<strong>%s</strong>" % f(*args) @uppercase @make_bold def say_hello(nom): "Di hola, home!" return "Hello world %s" % nom if __name__=="__main__": from inspect import getargspec print say_hello('World') print say_hello.func_name print say_hello.__doc__ print getargspec(say_hello) |
Si executau el codi podem veure que no ens ha fet falta recore a wraps o a reasignar nom, la pròpia llibreria de Simionato ho ha fet. A més, si ens fixam en la sortida de l'exemple:
<STRONG>HELLO WORLD WORLD</STRONG> say_hello Di hola, home! ArgSpec(args=['nom'], varargs=None, keywords=None, defaults=None)
La primera línea correspon a la sortida de la funció que hem decorat. La segona és el nom d'aquesta funció. Ens surt el nom de la funció original i no el del decorador. La documentació també s'ha mantingut i per acabar, podem veure que la signatura de la funció és correcta, ens diu que té un argument obligatori anomenat nom.
Conclusió
Esper haver deixat un poc més clar el tema dels decoradors. Crear-los no és difícil, utilitzar-los és simple, sols hem de tenir clar què són i quan fer-los servir. Són una eina potent que ens permet fer el nostre codi més legible i cohesionat. Fora por i a disfrutar amb els decoradors.
Com tot en aquesta vida, usau-los amb coneixement i moderació.
Referències
Per escriure aquest article m'he basat en múltiples fonts, les més importants i útils han estat:
Traducciones/Translations by apertium
0 comentaris, 0 trackbacks (URL) , Tags: Python Django
Qooxdoo
Escrit per Aaloy a 20 de August , 2009 a les 10:31 p.m.
Qooxdoo és un bastiment per a la creació d'aplicacion web amb aparença d'escriptori, és a dir el que es coneix com a RIA (Rich Internet Applicatinons).
Aquests darrers dies he estat donant-li una ullada a aquest bastiment. Feia temps que li estava seguint la pista i finalment m'he decidit a avaluar-lo com toca, és adir, començant a fer-hi alguna cosa productiva.
Quan un va a la web de Qooxdoo el primer que pot reparar és que el bastiment compta amb un bon conjunt d'exemples que ens permeten veure les possibilitats del producte. L'aparença de Qooxdoo no és tan cuidada com la d'Extjs per exemple, però en el seu conjunt presenta tota les funcionalitats necessàries.
A l'hora d'avaluar el bastiment m'he fixat en dos punts que per mi són claus: la mantenibiliat de les aplicacions i les seves capacitats de connexió amb altres sistemes.
Donat que el cicle de vida de les aplicacions és molt més llarg que el temps de desenvolupament, tenir un bastiment que ens permeti crear aplicacions mantenibles és fonamental. El bastiment ens ha de permetre estructurar l'aplicació en mòduls i a més ens ha d'abstreure de les complexitats del javascript en el que fa a les colisions de noms.
Qooxdoo resol aquest tema molt bé. No és tan sols un bastiment gràfic, sinó que ha creat un vertader llenguatge orientat a objectes amb les possibilitats bàsiques que ens dóna el javascript. La gent que ve de Java es sentirà com a casa amb aquest model d'objectes: ens permet tenir variables i mètodes estàtics, crear fàcilment mètodes, parla de constructors, d'herència i fins i tot hi ha alguan cosa semblant als interfícies. Aquest model de programació es pot aplicar per modularitzar el codi, de manera que podem crear objectes que representin una finestra amb el seu contingut o bé fer un objecte per a representar un aspecte concret: un menú per exemple.
A l'hora de posar el sistema en producció Qooxdoo se n'encarrega d'esbrinar quins són els mòduls que es fan servir i empaquetar i comprimir els distints mòduls que hem creat en un únic arxiu (més de 500 K a les meves proves). Com es pot veure està força ben pensat, i és una de les coses que més m'han agradat, ja que amb una aplicació gran és molt fàcil perdre la pista del que es fa i acabar amb mega-arxius javascript que tenen un poc de tot. Qooxdoo ens ajuda a estructurar el nostre codi sense perder l'avantatge de poder fer el desplegament a producció amb un únic arxiu javascript.
La connexió amb altres sistemes també em feia passar un poc de pena. Hem connectat bastiments javascript fent servir json, xml i cridades ajax i quan més complexe és el bastiment més dificultats hi ha amb el json, ja que sempre ho esperen d'una determinada manera.
Qooxdoo permet connectar-se mitjançant Ajax i fent servir Json RPC. He creat una sèrie de serveis jrpc de prova amb Django i he mirat de connectar-los. M'ha costat una mica ja que no comptava amb el problema de la seguretat del javascript a IE i Firefox. No es permeten cridades a dominis diferents (crossdomain), però tampoc a ports diferents. Com que Django a desenvolupament ho tenia a localhost:8000 i el javascript ho tenia com a file:// no hi havia manera de fer la connexió per POST. La solució ha esta utilitzar nginx per a servir el javascript i a més per servir de proxy invers cap a Django, amb això ja he pogut fer la connexió sense problemes i tenir el millor dels dos mons, independitzant la capa de presentació de la capa del web service, creat amb el servidor Django i amb capacitat d'autodocumentació, de manera que el servidor Django és capaç de generar una plana on es publiquen els mètodes del json rpc i una petita utilitat per comprovar-ne el funcionament. Semblant al WSDL però millor i tot.
Encara és prest per saber si Qooxdoo complirà totes les espectatives que hi tenc. Falta comprovar com es porta amb widgets complexos: taules, llistes, mescla de contingut web html amb el javascript generat i veure com es pot tractar l'autenticació i la seguretat. Per ara pareix que generar interfícies d'usuari potents és prou senzill i que la comunicació amb altres sistemes és possible, i ara per ara li veig moltes més possibilitat que opcions com Dojo i Extjs, sobretot perquè li veig una filossofia que m'agrada: la de tenir un sistema pensat per al desenvolupador i per al qui ha de mantenir l'aplicació.
Traducciones/Translations by apertium
1 comentari, 0 trackbacks (URL) , Tags: Python Django qooxdoo
Django 1.1
Escrit per Aaloy a 29 de July , 2009 a les 8:35 p.m.
Des de fa poquetes hores ja tenim la versió 1.1 de Django. Les novetats es poden trobar a l'anunci oficial de Django 1.1.
Per mi el més interessant són els canvis a l'ORM (les agregacions són una característica llargament esperada) i les millores a l'Admin, que es pot personalitzar sense fer tant esforç com abans.
Enhorabona als "core developers" i a tots els que feim feina amb Django. Tenim cada dia un bastiment més productiu i potent, on tot el que s'afegeix no és per mor del marketing o perquè fa "cool", sinó perquè realment serveix per a la feina.
Traducciones/Translations by apertium
1 comentari, 0 trackbacks (URL) , Tags: Python Django
Escalabilitat, multiprocessador i GIL
Escrit per Aaloy a 16 de July , 2009 a les 8:34 p.m.
Una de les dèries que tenim com a informàtics (o que hauríem de tenir) és la d'aprofitar el millor possible els recursos que tenim a la nostra disposició.
Això es tradueix algunes vegades en discussions de que si un llenguatge és millor que un altre en l'aprofitament de la màquina, i quan toca a Python, el tema estrella és el GIL, el mecanisme intern que fa servir Python per poder ser multi-fil. GIL fa que les aplicacions que fan un us intensiu dels fils no siguin tan òptimes com podrien ser (potser sí són més segures i més bones de programar, però això és una altra història) i que puguem pensar no facin tan bon us dels múltiples processadors com es podria suposar. En aquests casos el millor potser és llegir un article molt aclaridor, en lloc de començar a pegar destralades.
Tot i això, pareix que hi ha una confusió entre el que representa l'escalabilitat d'una aplicació i el nombre de processadors d'una màquina. En principi pareix el mateix, però realment no ho és.
La vertadera escalabilitat no la dóna el fet de que l'aplicació aprofiti al 100% els 4 o 8 processadors que pugui tenir el nostre servidor, l'escalabilitat la tenim quan aquesta mateixa aplicació pot executar-se damunt 8 màquines amb els processadors que tenguin. És a dir, que davant un problema que necessiti més potència de màquina, la solució no sigui posar una màquina amb més processadors, sinó posar més màquines.
Aqui ja no parlar d'aplicacions que executin fils, parlam d'aplicacions que executen processos, de comunicacions entre processos, de particionat de la nostra aplicació de manera que pugui distribuir-se.
Una de les maneres més senzilles que he trobat d'obtenir això és mitjançant els sistemes de missatgeria i de les coes de feina. En aquest cas tenim un o varis servidors que actuen de gestors de les coes de feina i una sèrie de processos que envien tasques a la cua per a ser realitzades, processos que realitzen les tasques i processos que consumeixen els resultats. El GIL no és un problema, el problema és pensar en com s'ha de fer l'aplicació, en com distribuir les càrregues entre els servidors, en definitiva, el problema és d'enginyeria de programari pur i dur. Passam de pensar en dues dimensions a pensar en n-dimensions.
Si hi ha una cosa que té bona Python és la poca distància que hi ha entre voler fer una cosa i tenir la capacitat de fer-la. Així doncs fer programes que facin servir les cues és realment trivial: posam un servidor, el beanstalkc per exemple, les llibreries Python que facin de client i ja ho tenim llest. Separar l'aplicació real per a que vagi en capes i establir un sistema integrat de control, això ja és una altra cosa :)
Quan parlam d'aplicacions web un sistema de coes a més pot fer-se servir per a augmentar l'escalabilitat de la nostra aplicació i donar un millor temps de resposta als nostres usuaris.
Imaginem per exemple una situació típica: la nostra web (feta amb Django, per suposat) quan acaba el procés de compra envia un e-mail a l'usuari amb la factura del que acaba de comprar.
Si no ens hem complicat la vida, tendrem que dins la mateixa vista que guarda les dades de la compra hem posat una cridada a la funció que genera el pdf amb la factura i la cridada a la funció que l'envia.
Encara que la factura es generi força ràpid, generar i enviar el pdf pot ser un problema si tenim molta càrrega, consumeix cicles de CPU que estam llevant als visitants de la nostra web.
En aquest cas podem tenir una maquina o vàries destinades a la generació i enviament de les factures. La nostra aplicació web sols envia a la cua (a una altra màquina o màquines) la petició de que s'ha de fer la factura, i els processos que tenim escoltant a la cua de treballs generaran i enviaran la factura.
Com que l'enviament del treball a la cua de treballs és pràcticament instantani, l'usuari té la sensació de que la web ha respost molt ràpidament. Com que tenim màquines dedicades per a la generació de les factures aquestes també es generen força aviat i s'envien. Mentre hem deixat el servidor (o servidors web) descarregats per atendre més peticions. Hem escalat!
És un exemple típic, com veis l'escalabilitat no s'ha aconseguit posant més màquines que fessin de servidors d'aplicacions Django o posant més processadors, sinó separant les tasques en màquines especialitzades. Si parlam de parsejar XML la cosa és encara més divertida, a l'article [High-performance XML parsing in Python with lxml] (http://www.ibm.com/developerworks/xml/library/x-hiperfparse/) de la web d'IBM i gairebé a les acaballes també ens dóna la pista: l'estratègia de dividir i conquerir; una altra vegada més pensar en com feim les coses.
Aquest és un apunt damunt escalabilita, Python i Django, però perfectament podríeu substituir Python pel vostre llenguatge de capçalera i Django pel vostre bastiment web preferit, Python fa fàcil provar totes aquestes coses, però el realment important és adonar-se de que es poden fer.
Traducciones/Translations by apertium
6 comentaris, 0 trackbacks (URL) , Tags: Python Django
Eclipse Galileo vs Netbeans Python (trunk) per Python i Django
Escrit per Aaloy a 05 de July , 2009 a les 11:42 a.m.
La publicació de la nova versió d'Eclipse, Galileo ha servir d'excusa per a tornar (al manco temporalment) a Eclipse com a entorn de desenvolupament per Python.
És la versió Galileo d'Eclipse millor que Netbeans? Doncs depèn, al cap i a la fi del que es tracta és de que l'IDE ens faci més productius, però a partir d'aquí ja és una qüestió de preferències personals.
Anem a veure les meves...
Facilitat d'instal·lació
La facilitat d'instal·lació és important perquè fa que puguis actualitzar de versió o posar un nou programador a fer feina en poc temps. La gent que fa feina amb PL em diu que necessita molt de temps per poder tenir l'entorn de desenvolupament llest, i com tots sabem temps són doblers.
Ambdós entorns són iguals de bons d'instal·lar. Ambdós tenen una arquitectura de plugins, així que la velocitat de posada en marxa depèn fonamentalment del nostre ampla de banda.
Si ens limitam a Python dir que la versió de Netbeans per Python ja duu el pluggin integrat i necessita davallar menys coses. De la mateixa manera no és necessiten pluggins addicionals al Netbeans per fer feina amb javascript, css o html, ja ve tot de sèrie.
Guanya Netbeans però de ben poc, ja que gràcies a projectes com Yoxo podem configurar-nos la nostra pròpia distribució d'Eclipse.
Portabilitat
Faig feina gairebé al 99% damunt Linux, però amb versions i386 i PPC. De tant en tant he de provar alguna cosa damunt Windows i per això vull que el meu entorn de desenvolupament pugui funcionar igual de bé en totes aquestes plataformes.
Aquí hi ha un guanyador clar: Netbeans. Funciona igual de bé a totes les plataformes. Eclipse dóna molts problemes al PPC i de fet aquesta plataforma no està soportada als repositoris oficials.
Suport per Python
La versió Python de Nebeans ja té el pluggin integrat. Eclipse necessita el pluggin PyDev. Ambdós pluggins són molt bons: permeten la utilització del virtualenv, tenen ressaltat de sintaxi, autocompletat, ajudes, plantilles, refactorització i creació de Unit Tests. Empat!
Suport per Django
Cap d'ells proporciona un suport específic per Django, encara que Netbeans ho té com a projecte. Tanmateix, però, el més important és que l'entorn permeti executar el python manage.py runserver --noreload en mode depuració.
Aquí Netbeans falla. El depurador està molt enfora del depurador d'Eclipse i Pydev, que et permet posar punts de ruptura on vulguis i té tot el que un necessita en depuració. Amb Netbeans no hi ha més remei que fer servir import pdb; pdb.set_trace() al codi i utilitzar el depurador de línia de comandes. És un punt de millora molt important per Netbeans, i si no us sentiu còmodes amb la depuració per línea de comandes llavors l'elecció clara és Eclipse.
Suport per CSS
Eclipse necessita el puggin d'Aptana per posar-se a nivell de Netbeans i al meu parer no ho aconsegueix del tot. Ambdós tenen ressaltat de sintaxis, autocompletat i ajudes, però Netbeans a més et pot presentar un exemple de com queden les coses. Útil en algunes ocasions per veure visualment que l'estil que estam modificant queda com volem o que és el que volem modificar. Netbeans guanya per poc.
Suport per Javascript
Una altra vegada més Eclipse necessita d'Aptana per posar-se a nivell de Netbeans. Netbeans més proporciona un mode de depuració de javascript basada amb Firebug que va força bé. Pareix que Eclipse també suporta quelcom semblant però no és tan inmediat com amb Netbeans. No és una opció molt important, però donat que estam parlant d'integració de les eines més habituals a l'IDE s'ha de tenir en compte. Netbeans guanya.
Control de temps i tasques
Ambdós entorns ens permeten gestionar llistes de tasques a fer i controlar el temps dedicat a cada tasca. Associar arxius a tasques i controlar automàticament quan hi estam fent feina i quant no. Això és fonamental pels qui factures a hores o senzillament volen dur un control dels projectes.
Nosaltres feim feina amb Trac i Eclipse gràcies al pluggin Mylyn permet connectar-se a un repositori Trac i gestionar-ne les tasques. Eclipse guanya!
Aprofitament de l'espai
Personalment m'agrada tenir una distribució on tengui a la vista l'arbre d'arxius, l'editor i la consola de tasques i missatges. Quan faig feina amb les dues pantalles de 20" puc posar l'IDE a una pantalla i el navegador a l'altra, però quan faig feina amb sols una pantalla l'aprofitament de l'espai de l'IDE fa que pugui, o no, col·locar-hot tot per a tenir una visualtizació més ràpida i ergonòmica.
Eclipse consumeix molt més espai que Netbeans. Es pot tunejar un poc, però la tendència és a desaprofitar pixels en menús, pipelles i demés. L'estructura de Netbeans per mí és molt més elegant i amb un aprofitament d'espai gairebé òptim. Com a IDE per al portàtil: Netbeans.
Integració amb SCM
La integració amb el sistemes de control de versions és prou bona a ambdós entorns. En la nova versió la integració del pluggin Subversive és excel·lent a Eclipse i l'assistent gràfic en el maneig de branques de Netbeans és simplement genial.
En les operacions del dia a dia Eclipse guanya. Té una opció que et permet veure els canvis entrant i sortint (això també ho fa Netbeans) però a diferència de Netbeans la interfície d'usuari està molt més orientada a la usabilitat i és trivial llevar arxius, marcar-los com a no integrables, veure'n les diferències abans d'integrar, etc.
La usabilitat de Netbeans és força dolenta, de fet és molt més còmode anar per línia de comandes. Guanyador i amb molt avantatge: Eclipse.
Revisió de canvis
Netbeans ens permet mostrar quins canvis hem fet a l'arxiu que estam editant i manté un control local de versions. Eclipse també manté aquest control local, però la usabilitat és inferior.
A l'hora de mostrar diferències Netbeans manté el ressaltat de sintaxi, Eclipse no. Per mi el més útil és el control local i aquí Netbeans és el millor.
Plantilles
Ambdós IDEs ens permeten crear i gestionar les nostres pròpies plantilles de codi i ho fan força bé. Empat!
Execució de tests unitaris
Ambdós sistemes ho permeten, però com passa amb el control de versions, el d'Eclipse és molt més usable. Guanyador per poc: Eclipse.
Refactorització
Els dos entorns suporten la refactorització de códi. Empat!
Eines d'edició
Ambdós entorns ens permeten fer cerques a tot el projecte, fer servir expressions regulars, substitucions, ... Els dos editors són força avançats i més que suficients per a les necessitats d'edició de codi. Empat!
Afegitons
Eclipse des del principi ha fet de la possibilitat d'extendre l'entorn mitjançant pluggins una virtut. Netbeans s'hi ha incorporat més tard i això es nota. La quantitat d'afegitons per Eclipse esborrona, per Netbeans cada cop n'hi ha més i millors però encara Eclipse n'és un clar guanyador.
L'aposta per Python i Django
Eclipse no té una orientació clara cap a Python, però gràcies a PyDev i a la facilitat que té Eclipse per a integrar pluggins s'ha convertit en un entorn molt potent per a la programació.
Netbeans en canvi té un build sols per Python, ple suport i al roadmap hi ha la intenció de suportar Django: creació de projectes, plantilles, etc. L'aposta per Python a ca'n Netbeans pareix molt més clara que a Eclipse, encara que en aquests moments i com a regla general, actualment Eclipse proporciona molta més funcionalitat i integració que Netbeans.
Traducciones/Translations by apertium
0 comentaris, 0 trackbacks (URL) , Tags: Python Django
Enumerate: indexant les llistes en Python
Escrit per Aaloy a 21 de June , 2009 a les 11:21 a.m.
Quan feim feina amb llistes de tant en tant en sorgeix la necessitat de fer feina amb l'index de la llista en lloc (o a més) de l'element de la llista en sí.
Sovint ens podem trobar fent coses com
1 2 3 4 5 | x = ['a','b','c'] i = 0 for item in x: i +=1 print i, item |
Davant aquest tipus de problemes, pensau que el més habitual és que el propi llenguatge ja ho tengui resolt, de la mateixa manera que els problemes més habituals de la programació web estan resolts per Django.
El nostre problema es redueix a fer servir enumerate. Aquesta funció agafa un iterador (una llista per exemple) i ens retorna un nou iterador, els elements del qual són una tupla composta pel comptador (l'índex) i l'element de l'iterador inicial que li passam com a paràmetre.
enumerate pot agafar com a paràmetre un nombre que serà el valor inicial de la seqüència (per defecte zero). La seva sintaxi és doncs enumerate(iterable, start=0)
Amb això podem escriure el codi anterior com
1 2 3 | x = ['a','b','c'] for i, item in enumerate(x): print i, item |
o sí volem comptar a partir d'1
1 2 3 | x = ['a','b','c'] for i, item in enumerate(x, 1): print i, item |
El nom a més és prou descriptiu de la funcionalitat que fa, amb la qual cosa el codi ens queda fins i tot millor documentat.
Traducciones/Translations by apertium
0 comentaris, 0 trackbacks (URL) , Tags: Python Django
Django: Guia d'aprenentatge
Escrit per Aaloy a 07 de June , 2009 a les 4:49 p.m.
La pregunta a la que vull intentar respondre en aquest article, és la que fa força gent que vol canviar la manera en que fa aplicacions web i com que ha sentit parlar molt bé de Django, s'atraca a aquest bastiment amb l'esperança de poder millorar la seva productivitat, és a dir: què he de saber per començar a fer webs amb Django?
Primer de tot em de saber què és Django. Django és un bastiment, és a dir, tot un conjunt de llibreries que interactuen entre sí i que estan orientades a fer i mantenir llocs i aplicacions web.
Primera cosa, doncs, Django no és un llenguatge de programació. Per a fer servir Django es necesita conèixer el llenguatge de programació amb el que està creat i aquest llenguatge no és ni més ni manco que Python.
Segona cosa: Com a bastiment que és Django requereix que les coses es facin d'una determinada manera, és el que anomena modle MVT (Model Vista Template), però al mateix temps ens està dient que per sí sol Django no ens construirà la nostra aplicació. Hi haurem de fer feina, segurament aquesta feina serà més ràpida i productiva, però no hi ha solucions màgiques.
Prerequisits
- Se suposa que sabeu programar en algun llenguatge d'alt nivell.
- Se suposa que teniu coneixements d'HTML i CSS
- Se suposa que teniu coneixements del que és una base de dades relacional i d'SQL
- Se suposa que saber fer anar la línia de comandes
Si no compliu aquests quatre requisits convendria replantejar-se el tema. Segurament trobareu que Django no és l'eina màgica que havíeu suposat.
En aquests temps de crisi costa molt dir que no als clients i una vegada emmerdats anar cercant l'eina màgica que ens tregui del problema. Per molt bona que sigui l'eina sempre hi haurà una sèrie de requisits, si no els compliu, millor cercau una altra cosa (potser una altra feina?) o anau invertint en la vostra formació de base.
Supòs que tots els que llegiu això compliu de sobra aquests requisits i m'estic passant de prudent i tiquis-miquis, però tenc una anècdota molt recent: l'empresa per la que treball habitualment va demanar un pressupost a una empresa externa. A aquesta empresa (no diré noms, però no és de Mallorca) se li va demanar que el pressupòs contemplàs que el llenguatge de programació triat per fer l'aplicació fos Python+Django, Java J2EE (amb Hibernate, Spring i JSP) i que en tot cas podien presentar la proposta per PHP. La proposta Java feia servir CakePHP i Zend i cap referència a cap tecnologia Java. La proposta PHP cap referència a versió de PHP. Imaginau quina impressió dóna el pressupost! Per acabar-ho de rematar la primera cosa que deia és que s'havia de fer l'anàlisi de requeriments. Què s'ha pressupostat doncs? Entenc que amb la crisi tothom està arreplegant tot el que pot, però tot té un límit!
Així doncs, per a començar, suposarem que ja teniu un cert nivell informàtico-programador i que no és la primera vegada que sentiu parlar de l'HTML i heu picat planes web a mà. L'SQL potser inicialment no serà tant necessari, però si realment volem arribar a fer aplicacions web mantenibles és imprescindible saber-ne i conèixer al manco els fonaments de les bases de dades relacionals.
Bé, ja ho tenim clar, i si heu arribat fins aquí ara estareu esperant que us digui cap on heu de tirar, què és el que s'ha d'aprendre i amb quin ordre per fer que la corba d'aprenentatge sigui el més suau possible. Som-hi doncs!
El bàsic de Python
Python és un llenguatge de propòsit general, molt net i estructurat. Es poden fer tant aplicacions de consola, aplicacions web o aplicacions gràfiques, el que vulguem. El que propòs aquí es el mínim que convé saber per poder fer webs quan abans millor i anar aprenent el llenguatge i les seves llibreries associades així com ho necessitem.
Una guia molt senzilla és la que tenim a Python para todos de Raúl González Duque, i que ens servirà com a referència d'estudi i com no el tutorial de Python. Què hem de saber en aquesta etapa?
La consola
- Utilitzar Python en mode consola: Python com a calculadora
- Instal·lació i execució d'iPython
El llenguatge
Hem de perdre la por a fer feina amb una consola de comandaments. La consola ens permetrà experimentar ràpidament amb el llenguatge, ens n'adonarem del que representa fer feina amb un llenguatge interpretat i fer fer-ho am iPython a més ens permetrà veure algunes de les possibilitat d'aquesta consola (que no oblidem la utilitza Django si està instal·lada).
- Creació del "Hola Món". Establim les regles de tabulació (tabulació a 4 espais, UTF-8 i format Unix pels arxius és la meva elecció.)
- Tipus bàsics: experimentació amb el intèrpret i l'editor. Nombres, cadenes, boolenas, llistes, tuples i diccionaris.
- Sentències de control:
if,for,pass,breakicontinue - Dates a Python.
- Creació de funcions bàsiques. Paràmetres per nom, valors per defecte.
- Documentació i comentaris
Amb això ja tindrem les nocions bàsiques del llenguatge. Encara no hem entrat en tota la potència de Python però si hem programat en una altra llenguatge ja tindrem en ment un bon anàlisi comparatiu. Segurament ja estareu pensant com refer alguns programent en Python.
- Orientació a objectes bàsica.
- Les funcions són també objectes. La funció lambda
- Els paquets.
Amb un poc de sort ja sabreu què és la orientació a objectes, si nó és un bon moment per aprendre'n. L'objectiu és adonar-nos de que tot en Python és un objecte i com s'estructura el codi en classes, mòduls i paquets.
- Programació funcional: fonamental veure la comprensió de llistes.
- Ordenació de llistes.
- Creació Manipulació de fitxers, utilització de les llibreries
osysys - Les excepcions.
Amb això ja tindrem gairebé el 80% del que necessitarem per fer una aplicació de consola, però no oblidem que a més nosaltres volem fer aplicacions web i a més amb Django, així que afegirem un parell de coses més:
- Expresions regulars bàsiques amb Python. L'eina Kodos.
- El mòdul Sqlite3 i el plugin de Firefox per a gestionar les bases de dades Sqlite.
- l'
easy_install. Hem de saber què és i com utilitzar-lo per a instalar noves llibreries. - Batteries included. Convé saber què significa aquest concepte i veure, al manco llegir, quines són les principals llibreries que Python inclou i què fan.
Amb això ja ens estam orientant cap a la web i Django. Les expressions regulars les farem servir sovint a Django a l'hore de mapejar urls amb les funcions Python corresponent. El mòdul sqlite ens permetrà veure el que és la API d'accés a base de dades de Python, familiaritzar-nos amb sqlite i si no ho havíem fet ja, començar a instal·lar els primers plugins de Firefox. Perquè tothom té Firefox per a desenvolupar webs, no?
Django
Podem començar a fer aplicacions web una vegada coneguem i ens sentim còmodes amb el llenguatge. El temps dedicat a Python no és per res temps perdut, ben al contrari. Feis exercicis, creau scripts, llegiu codi, l'important és poder llegir Python i que les instruccions surtin de manera natural.
- Instalau Django des del subversion
- Feis el tutorial de Django.
No està pensat per ser un manual de millors pràctiques sinó per mostrar el principal del bastiment i tenir alguna cosa repetible, una base comú.
- Mirau appfusedjango, el subprojecte
project. Està fet per a crear nous projectes pel mètode de copiar i aferrar.
Els models
- Creació de models: l'ORM de Django.
- Utilització de la consola per a introduïr dades als models
- Utilització del manager de Django per afegir dades.
- Execució de consultes fent servir l'ORM
En les aplicacions web passarem força temps manipulant dades, bé des de la web o bé directament amb scripts i la consola. Convé que no hi hagi por.
Les plantilles
- Creació de planes estàtiques
És un primer exercici. Consisteix en agafar un grapat de planes estàtiques i convertir-les en una aplicació Django utilitzant el direct_to_template. Això
ens ha de permetre veure com, sense més complicacions podem fer planes a la manera tradicional (i inefectiva).
- Les plantilles de Django: definició i concepte d'herència.
- Filtres
- Tags
- Refer les planes estàtiques anteriors fent servir l'herència de plantilles.
Aquí començarem a veure les possibilitats que té Django respecte de la manera més tradicional de fer les coses. Veurem que fer webs amb blocs i pensant en diferències és molt productiu.
El view.py i url.py
- Veure com les urls mapegen contra funcions Python
- Utilització de les plantilles
- Les expressions regulars i les urls.
- HttpResponse, render_to_template , redirect
Ens n'hem d'adonar que no hi ha màgia, que tot és codi Python que envolcalla al protocol HTTP.
Formularis
- Creació de formularis.
- Renderització de formularis
- Formularis lligats a dades
- Validació i control d'errors.
- Pujar arxius i imatges.
- L'autenticació
Dedicau el temps que sigui necessari als formularis. De ben segur en fareu molts al llarg del cicle de vida d'una aplicació web. Django és molt elegant en la seva utilització.
Quan es veuen els formularis m'agrada passar també per l'autenticació, ja que en aquest punt ja es té tot el necessari i consider que l'aspecte de la seguretat s'ha de tractar tot d'una que es pot.
Reflexió
No sé ben bé com anomenar aquesta etapa, però convé que dediqueu alguns dies a fer una aplicació web des de zero. Pensau en alguna que es us agradaria fer i vegeu-ne la seva viabilitat. Reduïu-ne l'abast si és necessari, però intentau fer una aplicació web completa.
L'objectiu és adonar-nos del que sabem, del que no sabem i del que no sabíem que no sabíem.
Django i Javascript
- Json i Django
- Exemple de jQuery (o la llibreria que utilitzeu) i com lliga amb les urls y views de Django.
No sols d'HTML viu l'home i cada cop les planes web tenen més javascript. Django és agnòstic en el que fa a la llibreria a utilitzar, però sigui com sigui fa que sigui realment fàcil interactuar amb llibreries javascript com jQuery, extjs o Dojo.
Fent les webs escalables
- Internacionalització
- Els principals middlewars.
- Sistemes de cachés
- Context processors
- Creació de tags pròpies
Aquí ens n'adonarem de les respostes que dóna Django als problemes reals quan la nostra web passa de ser una idea a estar en producció i té prou visites. Hem de veure a més com podem interactuar amb les peticions HTTP i amb la informació que passam a les plantilles.
Reutilització
- Convé adonar-se de com Django permet reutilitzar codi, de l'estructuració del projecte en aplicacions i d'aquestes en mòduls PYthon.
- Repassar els principals settings.
- Repassar les aplicacions que ja venen incorporades al bastiment: e-mail, sessions, paginació, signals, sitemaps i sites.
- Algunes aplicacions interessants: django-photologuer, django-page-cms, django-rosetta, django-command-extensions
Reflexió 2
Tornau a fer l'aplicació anterior incorporant javascript, internacionalització, cachés i tags fets ad-hoc. Mirau com queda el codi i com queda l'aplicació.
Arribats a aquest punt ja estau llets per anar agafant cada vegada més coneixements. No oblideu que aquests han de venir tant des de la part Python com de Django. Quan una cosa no vegeu com es pot fer amb Django cercau en el propil llenguatge. Si és un problema comú segur que està solucionat en el propi bastiment, i si no ho està vol dir que ja té solució a nivell de llenguatge de programació.
Això és tot, esper que us servesqui de guia i us animi a fer feina en aquest bastiment. Recordau, però que no hi ha bales de plata i que tot requereix esforç i dedicació.
Traducciones/Translations by apertium
3 comentaris, 0 trackbacks (URL) , Tags: Python Django
Django i python: orientat a la feina
Escrit per Aaloy a 03 de June , 2009 a les 6:36 p.m.
Avui, mentre explicava a la gent de l'equip web un grapat d'optimitzacions que podem fer per fer que les nostres aplicacions siguin més ràpides i actualitzables, al mateix temps pensava que en la potència que ens està donant Python i Django gràcies a la seva orientació cap a fer les coses com s'han de fer.
La comparació amb Java, l'altre llenguatge que feim servir, no pot deixar d'estar present, i llevat d'excepcions (poques) he de dir que la combinació Python i Django en surt sempre afavorida davant de Java.
Django està molt orientat a la web, però no de qualsevol manera, està orientat a fer webs que s'han de mantenir i que han d'escalar. Per això veus que és gairebé trivial fer coses que en altres plataformes resulten força costoses: qui no recorda el que s'ha de fer per fer un redirect amb Struts, per exemple?, o la complexitat que té validar formularis tant amb Struts com amb Spring?.
Django està orientat a fer aplicacions pel món real, això vol dir: que surtin ràpides, puguin sofrir moltes modificacions al llarg del seu cicle de vida i que es pugin actualitzar també molt ràpidament.
El bastiment segueix de molt aprop la filosofia de Python, la de mantenir les coses legibles i simples. Hi ha d'haver sempre una manera obvia de fer les coses, i quan fas feina amb Django veus que els 90% dels problemes que et planteja una aplicació web tenen resposta directa. La resta poden dur un poc més de feina, que normalment vol dir fer herència d'alguna classe.
Una de les darreres aplicacions en les que he pogut comparar Java i Python ha estat una aplicació que agafa XML, el manipula i el posa dins una base de dades. L'aplicació original, feta amb Java+Spring+Hibernate, va dur setmanes de feina, mesos si contam el manteniment posterior. Fer el mateix amb Python (lxml+sqlalchemy) ha duit menys de 4 dies-home de feina.
Posar l'aplicació Java en producció també va ser un poc malson per mor de les dependències entre les llibreries. Posar-la en Python dins un virtualenv és pràcticament immediat.
Tampoc ha resistit la comparació el manteniment i la correcció d'errors. El temps que passa per localitzar l'error i fer l'actualització en Python és mínim, en Java multiplicant per 10 o per 20 aquest temps.
Ens queda un tema: el rendiment. Com moltes vegades he dit per aquí el rendiment basta que sigui "prou bo", és a dir, per l'aplicació que estam parlant partíem d'un temps de càrrega d'entre 4 hores en les primeres versions Java, a 30 minuts a les darreres versions amb multi-fil. Amb màquines amb 8 Gb de RAM i menjant-se la màquina tant amb memòria com amb processador.
La versió Python també es feta amb multi-fil, on el nombre de fils és parametritzable, amb pool de connexions i demés. Sols que escrit amb un 10% de les línies de codi. El temps? Doncs 6 minuts i mig. Consum de memòria: mínim, ús de la CPU: intensiu però sense deixar torrada la màquina.
Python està orientat a que les coses es facin i es mantenguin. Per algunes tasques concretes potser no serà la millor solució, però cada vegada estic més convençut que és el primer que s'ha de provar. Si no va prou bé, el prototip ens haurà servir per a conèixer millor l'abast del problema sense haver-hi invertit molt de temps, i sempre som a temps d'optimitzar alguna secció del codi amb C, de maneres per utilitzar codi C (o C++) o bé d'escriure codi intermig que es transformi en codi C compilable n'hi ha força. Al wiki de Cpython (una de les eines que ens permet fer això) n'hi ha un bon grapat.
Traducciones/Translations by apertium
5 comentaris, 0 trackbacks (URL) , Tags: Python Django
Django, imatges i Imagekit
Escrit per Aaloy a 23 de May , 2009 a les 4:46 p.m.
Django Imagekit és una llibreria creada per Justin Driscoll que ens permet crear miniatures i/o distints tamanys d'imatges a partir de la imatge original, i que s'integra molt bé amb Django. És una llibreria més senzilla que la de django-photologue ja que no té tota la funcionalitat per a crear gal·leries fotogràfiques.
El tutorial per a fer-la anar està força bé, però aprofitaré la benentesa per a fer cinc cèntims de com podem pujar una imatge al nostre site amb Django i presentar-la de nou.
El codi font de l'exemple complet és a appfusedjango.
El model
Per començar definim el model:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 | from django.db import models from imagekit.models import ImageModel from django.db import models from imagekit.specs import ImageSpec class Photo (ImageModel): image = models.ImageField(upload_to='photos') comments = models.TextField() num_views = models.PositiveIntegerField(editable = False, default = 0) class IKOptions: spec_module = 'sample.specs' cache_dir = 'photos/cache' image_field = 'image' save_count_as = 'num_views' def thumb(self): if self.image: return '<img src="%s">' % self.thumbnail.url else: return "" thumb.allow_tags = True thumb.short_description = 'Foto' |
En aquest cas es tracta d'un model molt senzill, guardam la imatge image al directori photos i posarem dins la base de dades tant la referència del fitxer (això és important, dins la base de dades no és guarda la imatges sinó sols la metadada) i el comentari.
El camp num_views ens pots servir per anar guardar la quantitat de vegades que s'ha vist la imatge i és utilitzat si volem per la llibreria d'Imagekit.
La part interessant és a la classe IKOptions. Aquesta defineix quin tractament se li donarà a la imatge, on s'enmagatzemaran les miniatures i a quin camp es fa referència.
spec_moduleÉs el mòdul d'ImageKit que es farà servir i que defineix els tamanys possibles. D'aquí una estona el veurem amb detall. En el nostre cas el modul es diuspecs.cache_dir: guardarem els distints tamanys generats aphotos/cacheimage_field: Les metadaes son pel campimagedel model.
El mètode thumb ens serviex per poder posar la miniatura dins el llistat de l'admin.
Els tamanys
Per definir els tamanys Imagekit distingeix entre el que és la visualització de la imatge i les manipulacions que s'hi fan. La imatge original necessita passar per uns filtres que Imagekit anomena processors. Una imatge pot generar-se aplicant un o més d'aquests filtres.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 | from imagekit.specs import ImageSpec from imagekit import processors # define the thumnail processor class ResizeThumb(processors.Resize): width = 100 height = 75 crop = True class ResizeDisplay(processors.Resize): width = 600 class ResizeBig(processors.Resize): width = 800 # define your spec class Thumbnail(ImageSpec): pre_cache = True processors = [ResizeThumb,] class Display(ImageSpec): processors = [ResizeDisplay,] class Big(ImageSpec): processors = [ResizeBig,] |
A l'exemple sols faig servir un tipus de processor el de Resize per a redimensonar la imatge als tamanys que farem servir.
Pujam una imatge
Per pujar una imatge necessitam definir un formulari, el mètode que tractarà aquest formulari i les urls que farem servir, així com les plantilles que es mostraran.
Les urls
Aquesta és la part senzilla.
1 2 3 4 5 6 7 8 9 | from django.conf.urls.defaults import * from django.conf import settings from django.contrib import admin admin.autodiscover() urlpatterns = patterns('', url(r'^$','sample.views.index', name="main-page"), url(r'^display/(?P<id>\d+)/$','sample.views.display', name="display-image"), .... |
Definim una url per l'index que identificarem per nom main-page i que serà tractada al mètode index del mòdul views del nostre paquet sample.
I una altra ulr que ens permetrà visualitzar la imatge a partir del seu identificador. Anomenam a aquesta url display-image, original que és un...
El formulari
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 | # -*- coding: UTF-8 -*- from django import forms from PIL import Image class AttachmentForm(forms.Form): """Form for the attachment sample. Added a simple validation to accept only png files checking for 'image/png' in the content_type of the file""" image = forms.FileField(help_text="add a png or jpg file") comments = forms.CharField(widget= forms.Textarea, help_text="describe the image") def clean(self): "Validate the entire form" cleaned = self.cleaned_data try: file = cleaned['image'] except Exception, e: # perhaps this is not a file raise forms.ValidationError("Not valid file: %s" % e) if not file.content_type.lower() in ["image/jpeg", "image/png", "image/jpg"]: raise forms.ValidationError("Just jpg or png files please") im = Image.open(file) if not im.format in ['JPEG','PNG']: raise forms.ValidationError("Just jpg or png files please") return cleaned |
El formulari com es pot veure és d'allò més normalet, definim un camp per la imatge i un camp per als comentaris.
La part "nova" està en la validació. No ens podem fiar del que ens diu la gent que puja, així que le que farem és comprovar que el mime type es correspon amb un format vàlid, i com que fins i tot això es pot manipular, farem una comprovació addicional amb PIL per a comprovar que la imatge és el que diu ser. Aquesta comprovació dependrà del vostre nivell de paranoia.
Tractant la imatge
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 | from models import Photo from django.http import HttpResponseRedirect from django.shortcuts import render_to_response from forms import AttachmentForm def index(request): "Obtains the attachment and saves it to the disk" if request.method == 'POST': form = AttachmentForm(request.POST, request.FILES) if form.is_valid(): f = form.cleaned_data['image'] foto = Photo() foto.image.save(f.name, f) foto.comments = form.cleaned_data['comments'] foto.save() return HttpResponseRedirect('/') else: form = AttachmentForm() fotos = Photo.objects.all() return render_to_response('index.html', {'form':form, 'fotos': fotos}) |
Per a tractar arxius i imatges la part delicada és recordar que hem de passar la informació al formulari afegint el request.FILES i pensar a posar al formulari enctype="multipart/form-data"
A més d'això hem de guardar dues vegades: una per la imatge, que guardarà el contingut dins el sistema de fitxers, i una altra per la resta del model i les metadades de la imatge. Per això tenim un foto.image.save(f.nam, f) guarda la imatge al sistema de fitxers.
Mostrar una imatge és prou senzill
1 2 3 | def display(request, id): foto = Photo.objects.get(pk=id) return render_to_response('image.html', {'foto': foto}) |
Mostrant les imatges
Al template hi passam objectes del tipus Photo, que recordem tenen tota la fontaneria del ImageKit.
Això vol dir que a més de la imatge original, puc fer servir els formats que he definit a specs: Thumbnail, Display, Big.
Per utilitzar-los en la nostra plana sols hi hem de fer referència:
1 2 3 | <li><img src="{{foto.thumbnail.url}}" /></li> <li><img src="{{foto.display.url}}" /></li> <li><img src="{{foto.big.url}}" /></li> |
com podem veure sols és cosa de fer referència al tamny definit i treure'n el que ens interessa, en el nostre cas la url per a mostrar la imatge.
Per darrera ImageKit se n'ha encarregat de fer les transformacions i guardar la imatge a la caché, de manera que la feina pesada de generació dels distints tamnays sols se fa un cop.
A partir d'aquí ens podem comlicar tant com voguem, per exemple:
- Al mètode clean fer que no es puguin pujar imatges de més d'un tamany.
- Reescriure el mètode save del model per a que no es guardi la imatge original sinó una altra imatges ja reduïda.
- Escriure més processors per fer més manipulacions a les imatges.
- etc. etc.
Però el és segur és que amb això que us he contat tingueu el 90% dels casos solucionats.
Nota: Oscar, esper que això et servesqui ;)
Traducciones/Translations by apertium
3 comentaris, 0 trackbacks (URL) , Tags: Python Django
Django vs PHP frameworks
Escrit per Aaloy a 10 de May , 2009 a les 6:36 p.m.
Llegint llegint he anat a parar a una plana que compara els principals bastiments (frameworks) PHP entre sí per demostrar com n'és de ràpid el KumbiaPHP comparat amb els altres.
Com que el codi utilitzat per les proves està disponible, doncs he vist que era un simple hello world, així que he creat un projecte hello_world a appfusedjango per poder comparar amb Django.
Disclaimer: La velocitat d'execució no ho és tot. Segur segur, que fet en x ben optimitzat l'aplicació y és més ràpida per aquesta prova. Això no és per veure qui la té més llarga, sols intent comparar coses més o manco semblants per saber on estam. Disclaimer 2: Qui en sap d'aquest tipus de proves és la gent que es dedica més a sistemes, en Bernat o en Guillem, per exemple :)
Amb què he fet les proves?
- La màquina es un PPC 64 de 2 MHz amb 1 Gb de RAM utilitzat Ubuntu 9.1 i Gnome com a Desktop. És a dir, no he fet servir un servidor i hi ha moltes coses executant-se.
- Python 2.5.2 (r252:60911, Jul 31 2008, 17:33:15) [GCC 4.2.3 (Ubuntu 4.2.3-2ubuntu7)] on linux2
- Com que em feia peresa muntar i configurar l'Apache he fet servir un servidor fet amb Django, el CherryPy, concretament el mòdul WSGI que podeu trobar al Django-Cerise sense executar-ho amb mode daemon per tenir major facilitat de modificar la quantitat de threads.
- La versió de Django és la trunk
- Per a les proves finals he optimitzat l'aplicació llevant tot el codi de depuració i middlewares que no es feien servir com el del la compressió gzip.
L'aplicació
L'aplicació no té cachés (tot a dummy i sense per-site-cache) i he fet que la plana en generar-se passi per la vista per a que es faci tot el recorregut MVT.
Els resultats:
executam ab -c 10 -t 60 http://localhost:8088/ cada vegada:
- Aplicació amb codi extra, servidor amb 10 threads 277 req/s
- Aplicació amb codi extra, servidor amb 3 threads 285 req/s
- Aplicació amb codi extra, servidor amb 5 threads 285 req/s
- Aplicació optimitzada, servidor amb 5 threads 334 req/s
- Aplicació optimitzada, servidor amb 3 threads 331 req/s
KumbiaPHP, el més ràpid de la comparativa PHP treu 34 req/s, casualitat? He fet alguna cosa malament? Potser, però no sóc el primer, hi ha gent que també ha notat un fort augment del rendiment en passar de PHP a Django.
I una de les proves:
ab -c 10 -t 60 http://localhost:8088/
This is ApacheBench, Version 2.3 <$Revision: 655654 $>
Copyright 1996 Adam Twiss, Zeus Technology Ltd, http://www.zeustech.net/
Licensed to The Apache Software Foundation, http://www.apache.org/
Benchmarking localhost (be patient)
Completed 5000 requests
Completed 10000 requests
Completed 15000 requests
Completed 20000 requests
Finished 20009 requests
Server Software: CherryPy/3.0.3
Server Hostname: localhost
Server Port: 8088
Document Path: /
Document Length: 266 bytes
Concurrency Level: 10
Time taken for tests: 60.003 seconds
Complete requests: 20009
Failed requests: 0
Write errors: 0
Total transferred: 7723474 bytes
HTML transferred: 5322394 bytes
Requests per second: 333.47 [#/sec] (mean)
Time per request: 29.988 [ms] (mean)
Time per request: 2.999 [ms] (mean, across all concurrent requests)
Transfer rate: 125.70 [Kbytes/sec] received
Connection Times (ms)
min mean[+/-sd] median max
Connect: 0 2 82.1 0 3001
Processing: 4 27 41.7 24 2295
Waiting: 0 25 41.5 21 2291
Total: 5 30 92.1 24 3038
Percentage of the requests served within a certain time (ms)
50% 24
66% 28
75% 30
80% 32
90% 37
95% 43
98% 50
99% 59
100% 3038 (longest request)
(development)aaloy@G5:/tmp/djangocerise-master/src$ ab -c 10 -t 60 http://localhost:8088/
This is ApacheBench, Version 2.3 <$Revision: 655654 $>
Copyright 1996 Adam Twiss, Zeus Technology Ltd, http://www.zeustech.net/
Licensed to The Apache Software Foundation, http://www.apache.org/
Benchmarking localhost (be patient)
Completed 5000 requests
Completed 10000 requests
Completed 15000 requests
Finished 19863 requests
Server Software: CherryPy/3.0.3
Server Hostname: localhost
Server Port: 8088
Document Path: /
Document Length: 266 bytes
Concurrency Level: 10
Time taken for tests: 60.006 seconds
Complete requests: 19863
Failed requests: 0
Write errors: 0
Total transferred: 7667358 bytes
HTML transferred: 5283558 bytes
Requests per second: 331.02 [#/sec] (mean)
Time per request: 30.210 [ms] (mean)
Time per request: 3.021 [ms] (mean, across all concurrent requests)
Transfer rate: 124.78 [Kbytes/sec] received
Connection Times (ms)
min mean[+/-sd] median max
Connect: 0 0 0.1 0 3
Processing: 12 30 9.0 29 361
Waiting: 10 28 8.7 27 361
Total: 12 30 9.1 29 362
Percentage of the requests served within a certain time (ms)
50% 29
66% 31
75% 33
80% 34
90% 37
95% 40
98% 44
99% 47
100% 362 (longest request)
Traducciones/Translations by apertium
6 comentaris, 0 trackbacks (URL) , Tags: Python Django
Custom management commands
Escrit per Aaloy a 09 de May , 2009 a les 1:03 a.m.
Si heu fet al manco el tutorial de Django haureu fet servir per exemple el típic
python manage.py runserver
per a iniciar el servidor. A partir de la versió 1.0 de Django podem fer servir una sintaxi semblant per crear comandaments lligats a la nostra aplicació i que s'executin també d'aquesta manera.
Podem crear comandaments per fer qualsevol cosa, de fet es que acaba fent és executar un script de Python. Així doncs, ens podríem demanar quina avantatja hi ha en fer-ho així, vàries:
- No necessitam definir cap variable d'entorn per dir quin settings hem de fer servir, l'script manage.py ja se n'encarrega.
- El comandament quedarà lligat a l'aplicació. Per tant podem crear comandaments que depenguin d'una aplicació concreta i que la complementin.
- Podem accedir a l'ajuda del comandament si l'hem definida. Permet a l'usuari saber com ha de fer servir la comanda.
Podem crear tres tipus de comandament a mida, que trobam definits dins django.core.management.base
-
Comandaments que hereten de
AppCommandi que poden prendre com a paràmetre una llista d'aplicacions Django. -
Comandaments que hereten de
LabelCommandque poden prendre com a argument qualsevol cadena de text. -
Comandaments que hereten de
NoArgsCommandi que no prenen paràmetres.
Si anau a la documentació oficial de Django veureu que el que hi ha és més aviat poc. Potser perquè quan ja saps com és fa pareix tan fàcil que potser no mereix l'esforç. Tot i això, crec que és una opció prou útil i que hauria de tenir més importància a la documentació.
Afortunadament hi ha al manco dos articles prou bons que ens ajuden amb exemples a treure partit d'aquesta funcionalitat. Si anam al codi font comprovarem que tot està força documentat, però la realitat és que encara que a partir de la versió 1 fer aquests tipus de coses s'ha convertit en trivial, en versions anteriors no ho era.
Aquest apunt té per objectius fer publicitat d'aquesta funcionalitat, podeu trobar més informació al codi font de Django i als següents apunts:
i exemples tant al codi font de Django com a django-extensions .
Fes-hi una ullada, després de tot, fer un hello world d'aquesta manera és prou senzill:
1 2 3 4 5 6 | from django.core.management.base import NoArgsCommand class Command(NoAgrsCommand): help ="prints hello world" def handle_app(self, app, **options): print "Hello world" |
Per provar-ho creau un paquet python anomenat management dins la vostra aplicació Django, i dins aquest paquet un altre anomenat commands. Dins aquest hi posaríem els nostres scripts. Per exemple si la nostra aplicació es diu uep i volem crear un comandament que es cridi com python manage.py hello hauríem de crear una estructura com:
uep/
__init__.py
models.py
management/
__init__.py
commands/
___init__.py
hello.py
Els noms dels comandament poden col·lisionar, així que o bé tenim noms prou especials o bé convé prefixar-los amb el nom de la nostra aplicació per tal de tenir una oportunitat més.
Traducciones/Translations by apertium
0 comentaris, 0 trackbacks (URL) , Tags: Python Django
Visor de logs amb Django
Escrit per Aaloy a 22 de April , 2009 a les 8:53 p.m.
L'altra dia em vaig trobar amb la necessitat de mostrar els logs de l'aplicació a la plana web que estava desenvolupant.
Per anar bé, els logs han de ser dinàmics, és a dir, la plana ha de refrescar-se segons apareguin noves línies de log (i això vol dir Ajax). La idea és tenir quelcom com el tail -n 100 f ... del Linux. Per la naturalesa de les peticions Http, no podem arribar a tenir la funcionalitat del tail, però podem atracar-nos-hi un poc. Aquest article explica com ho podem fer utilitzant Django, Python i jQuery.
El javascript
La part del navegador ha de refrescar-se automàticament i sols el tros que correspongui a la visualització dels logs. A cada refresc haurà de cridar al servidor, obtenir la informació del log i presentar-la.
Per aconseguir això utilitzarem Jquery i un afegitó anomenat Timers. Aquest plugin ens permet definir de manera una funció que s'executarà cada x milisegons fins que l'aturem o tanquem el navegador.
El temps de refresc dependrà de la nostra aplicació, i el truc està en donar-li un temps un poc menor que la velocitat a la que la nostra aplicació genera les línies de log. No fa falta mirar-s'hi gaire, sols és per donar una major sensació d'actualització en temps real i que es mostri la informació línia a línia. Si li donam un temps major farem menys peticions i la presentació del log potser es faci per blocs de línies.
També necessitarem fer una cridada Ajax al servidor per a que ens doni les línies de log. Això ho podem fer directament amb jQuery, amb la funció jQuery.ajax per exemple. Veurem que a més del text del log necessitam la darrera posició llegida, així que farem servir el getJSon, que ens permetrà obtenir la informació de manera més estructurada.
El servidor
Per simular el tail farem un poc de trampa. La primera vegada que es faci la petició ens situarem al final del fitxer de log i la segona ja enviarem la informació. És a dir, mostram al informació a partir de la segona vegada que es crida la funció. Com que el temps es suposa que és petit no té massa importància i simplifica molt la programació. Hem de tenir en compte que l'obtenció de log ha de ser ràpida.
Per a posicionar-nos farem servir la funció seek i tell ens donarà la posició resultant una vegada llegit el fitxer. Aquesta posició és la que anirem passant a cada petició, de manera que quan tornem a llegir el log, ho farem a partir de la darrera posició llegida. A l'exemple a més he fet servir la funció reverse per tal d'ordenar la llista de línies llegides de més nova a més antiga i simular l'scroll.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 | from django.http import HttpResponse from django.utils import simplejson def _tail(f, actual = 0): """ Returns a tuple with the actual position on the file and the last read lines in html format. """ text ="" if actual == 0: f.seek(0, 2) else: f.seek(actual) log = f.readlines() if log: log.reverse() text = "<br/>".join(log) return f.tell(), text def tail(request): "Tail simulation" f = open('sample.log', 'rU') pos = int(request.GET['pos']) pos, msg = _tail(f, actual= pos ) f.close() data = {'msg': msg, 'pos' : pos } return HttpResponse(simplejson.dumps(data)) |
Fixem-nos que el que tornam és una estructura json, creada a partir d'un diccionar Python mitjançant el simplejson.
A més consumim la posició del fitxer des de la que hem de començar a llegir, que vindrà donada pel paràmetre get. No he posat validacions ni tractament d'excepcions, ho deix com a exercici per al lector (això sempre queda bé dir-ho quan un no té massa ganes de fer feina :-P )
Amb el que ara veim del codi de servidor, el codi javascript ja és entenidor:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 | <script type="text/javascript"> $(function() { var pos = 0; var demos = $("div.wrapper div.demos"); var active = false; $('#log').ajaxStart(function(){$('#loading').show()}); $('#log').ajaxStop(function(){$('#loading').hide()}); $('.controlled-interval', demos). find('.start').css("cursor", "pointer").click(function() { if (!active) { active = !active; $('#log').everyTime(1000, 'controlled', function() { $.getJSON( "/log/tail/?pos="+pos, function(data) { if (data.msg !=''){ $("#log").prepend(data.msg+'<br/>'); } pos = data.pos; } ) }); } }).end().find('.stop').css("cursor", "pointer").click(function() { if (active) { active = !active; $(this).parents("div"). find('#log').stopTime('controlled'); } }); }); </script> |
Qui fa la feina és $('#log').everyTime(1000, 'controlled', function() qui es que manté el timer. Cada vegada que passa un segon (1000 ms) es crida al la funció del servidor amb la posició que acabam de llegir. El primer cop no tenim aquesta posició i la inicialitzam a zero, d'aquí que necessitem dues cridades a la funció per obtenir el primer log.
Notem com és de simple manipular el json a Javascritp. data té el text que hem d'escriure i la darrera posició de l'arxiu. Si el text està en blanc simplement ens limitam a actualitzar la posició; si hi ha contingut el posarem dins del div amb identificador log que tenim definit dins la plana.
S'hi pot fer molta més: posar-hi més disseny, podríem fer que es retornàs ja informació la primera vegada que es crida, tenir un reset per a que cuan continua el log sols venguin les darreres línies i no tot el bloc, però la idea és la mateixa:
- fer servir un timer
- fer servir ajax enviant la darrera posició on hem arribat
- fer servir seek i tell per posicionar-nos dins l'arxiu
Com és habitual l'exemple ho podeu davallar d'appfusedjango, o directament un svn co http://code.google.com/p/appfusedjango/source/browse/#svn/trunk/tail
Hi trobareu també una petita utilitat anomenada generate_log.py executant-la en una consola escriu línies de text que poden ser llegides per l'exemple.
Traducciones/Translations by apertium
0 comentaris, 0 trackbacks (URL) , Tags: Python Django
Carregant imatges
Escrit per Aaloy a 19 de April , 2009 a les 11:35 p.m.
Normalment quan treballam amb imatges amb Django ho feim des del punt de vista de disseny, és a dir, posam les imatges al directori on s'han de servir i per avall. Potser a vegades hem fer servir quelcom com el Django Photologue per a tenir les fotografies més estructurades dins la nostra aplicació.
Què passa però, si el que volem és carregar les fotografies des d'un script a la nostra base de dades, o quan ens trobam que la informació no ve pels canals habituals d'una imatge que es puja mitjançant un formulari.
Aquest és el cas que tractarem en aquest apunt. Tenim una imatge que ens arriba codificada en base64 i el que volem és afegir-la amb un petit comentari a la nostra aplicació.
El model que farem servir és tan senzill com això:
1 2 3 4 5 | from django.db import models class Photo (models.Model): image = models.ImageField(upload_to='photos') comments = models.TextField() |
La imatge ens ha arribat com a una cadena de text. El contingut ho podeu trobar al subversion del projecte appfusedjango.
El primer que hem de fer es decodificar la imatge, després crearem una instància de la classe Photo, li assignarem el contingut, el comentari i guardarem. Fins aquí supòs que tothom d'acord.
El problema, però, és que ImageField suposa que li passarem una objecte del tipus File i nosaltres el que tenim és una cadena de text. Necessitarem per tant simular aquest tipus de dada de manera que puguem tractar amb informació que no vengui directament d'un fitxer de text. Ni més ni manco el ContentFile. Aquesta classe que podem trobar a django.core.files.base el que fa és precisament això.
Una altra de les coses que hem de fer és guardar la imatge i assignar-li el nom. Per fer això haurem de crear el nostre objecte Photo, guardar la imatge i després guardar-ho tot a la base de dades.
Anem a veure com quedaria:
>>>import base64 >>>from django.core.files.base import ContentFile >>>from sample.models import Photo >>># ja tenim tot el necessari >>>raw_data = base64.b64decode(open('test/encoded_logo.b64').read()) >>># acabam de llegir les dades, podrien arribar directament >>>foto = Photo() >>>foto.image.save('the-image-name.gif', ContentFile(raw_data)) >>>foto.comments = "Django & Python rules!" >>>foto.save()
El pas del 'image.save' és necessari perquè Django no guarda les imatges per defecte dins la base de dades sinó al sistema de fitxer (de fet podem guardar-ho allà on ens vengui de gust gràcies a les possibilitats del custom storage) així que necessitam definir tant el nom de la imatge com les dades. A la definició del camp ImageField ja li hem dit que voliem deixar les imatges a la carpeta photos que estarà dins el directori que tinguem definit com el nostre MEDIA_ROOT.
El projecte que he pujat serveix per demostrar la idea. Per provar-ho basta fer un
$ python manage.py shell
des del directori del projecte imatges per a convèncer-vos que la cosa funciona veient com es van creant les imatges dins la carpeta media/photos/ del projecte.
Traducciones/Translations by apertium
0 comentaris, 0 trackbacks (URL) , Tags: Python Django
Nou ressaltat de sintaxis pel blog
Escrit per Aaloy a 11 de April , 2009 a les 5:24 p.m.
Fins ara Trespams tenia un ressaltat de sintaxi basat en Javascript, el que feia que no resultàs molt natural escriure apunts amb molt de codi font, ja que significava tenir que posar codi html dins el markdown per tal de marcar aquell tros de codi com a ressaltable pel Javascript.
Com a part de les modificacions he canviat això pel ressaltat basat en Pygments.
Aquest plugin permet fer servir tota la potència de Pygments per al ressaltat, podem resaltar HTML, plantilles Django, Javascript, Java, gairebé qualsevol cosa, i a més fent servir una sintaxi molt més natural, basta afegir un SheBang típic de Unix per a indicar el llenguatge.
La implementació està pensada per a no generar el codi HTML des de Markdown cada vegada, sinó que es guarden dues versions, una amb el codi original i l'altra amb el codi convertit a HTML i ja renderitzat amb els plugins. Això fa que el guardar l'apunt sigui un poc més lent (ha de convertir, reindexar i guardar) però la plana es pot mostrar molt més ràpidament. Optimització prematura? Potser sí, però tanmateix és d'aquelles coses que consideraria una millor pràctica.
El canvi fa que el codi antic no aparegui ressaltat, a poc a poc ho aniré canviant així com vagi revisant els apunts. El que no fa (crec) és trencar res, manté el codi dins un <pre> però no ho ressalta.
Traducciones/Translations by apertium
0 comentaris, 0 trackbacks (URL) , Tags: Python Django
Django regroup
Escrit per Aaloy a 10 de April , 2009 a les 7:36 p.m.
He fet quatre cosetes al programari del blog, per tal de mirar de passar al màxim la validació del w3c, encara tenc problemes amb els pre que em genera el markdown dins paràgrefs i que no passen la validació, però al manco ara la plana es veu prou bé amb Konqueror.
Fent neteja m'he trobat amb la necessitat de refer la jerarquia de l'històric d'apunts. No era gens neta i com que ara el blog té un menú prou potent, he decidit posar molta cosa que estava al lateral com a opció del desplegable.
Per fer la jerarquia d'articles el que es ja el primer de tot és obtenir la llista d'arxius. Això ho feim cridant un tag creat ad-hoc pel blog
{% get_yearly_archive as archive_list %}
que el que fa és executar
Entry.objects.current_active().dates('pub_date', 'month', order='DESC')
i posar-ho a una variable que pot ser usada a la plantilla.
Amb aquest tag obtenim la llista de mesos en els que hi hem fet apunts. Un element d'aquest llista és per exemple:
datetime.datetime(2008, 2, 1, 0, 0)
Com podem veure és un tipus datetime de python i com a tal podem obtenir-ne fàcilment l'any objecte.year o el mes objecte.month.
El que volem, com dic, és fer una jerarquia, és a dir, tenir una cosa com:
- 2009
- Abril 2009
- Març 2009
- 2008
- Desembre 2008
Això normalment duria força feina, però és aquí quan hom veu que Django està pensat per als reptes de la vida real, ja ve amb un template tag predefinit que ens permet fer agrupacions, el regroup
Donada una llista ordenada regroup ens crea una estructura de dades que contindrà els elements pels quals volem fer el grup i dins aquests els element de la llista que tenen aquest element.
En el nostre cas hem posat l'historial dins una variable anomenada archive_list, que recordem és una llista de datetime. Farem
{% regroup archive_list by year as historial_list %}
Amb això hem creat la nostra estructura de dades. Fixem-nos que l'exemple que duu la documentació de Django diu que regroup té com a paràmetre una llista de diccionaris. És part de la gràcia del tipat feble, pel que fa al funcionament de la funció l'estructura datetime es comporta com espera l'iterador groupby que és el que fa servir internament.
Podem simular-ho perfectament a la consola de iPython
1 2 3 4 5 | >In [43]: p = itertools.groupby (x, lambda z: z.year) >In [44]: for i in p: > ....: print i[0] > ....: for j in i[1]: > ....: print j |
Ja tenim l'estructura de dades, així que ara sols quedar recorre i presentar la informació tal com la volem. Per això cal tenir en compte que la nostra estructura defineix la variable grouper que fa referència al valor del camp pel qual estam fent l'agrupació
1 2 3 4 5 6 7 8 9 10 11 12 | {% for year in historial_list %} <li><span class="dir">{{year.grouper }}</span> <ul> {% for d in year.list %} <li><a href='{% setting "BLOG_ROOT" %} {{ year.grouper }}/{{d|date:"m" }}/' class='menu_content'> {{ d|date:"M Y" }}</a> </li> {% endfor %} </ul> </li> {% endfor %} |
Com és habitual, és més llarg d'explicar que de fer.
Traducciones/Translations by apertium
0 comentaris, 0 trackbacks (URL) , Tags: Python Django
Comentaris funcionant (esper)!
Escrit per Aaloy a 04 de April , 2009 a les 12:26 a.m.
Gràcies a Hugo me n'he assabentat que la darrera actualització del blog havia fet malbé el sistema de comentaris.
He pujat una nova versió sense el codi de depuració que feia que els comentaris no es poguessin processar ja que hi havia un punt de ruptura :(
Bé, res greu, coses de les actualitzacions a les males hores del vespre. En Pau també m'ha passat una incidència amb la visualizació del calendari amb Firefox 3.5. Pau, d'això se'n diu viure al límit!
Encara he de netejar molts divs innecessaris de l'anterior disseny. Vull jugar també amb algunes APIs de llocs socials i integrar-les, una de les primeres segurament serà la de Delicius.
Com que a mi també m'agrada viure perillosament estic aprofitant l'edició del blog per provar les versions de desenvolupament de Netbeans per Python. Estic tirant de les construccions del Hudson i provant-ho damunt el PowerPC.
No puc comparar-ho amb l'Eclipse perquè tanmateix damunt el PowerPC com ja he contat l'Eclipse no va gens bé, així que ja no hi ha comparació possible.
Res, que si algú s'anima a posar un comentari donaré per tancat el bug!
Traducciones/Translations by apertium
8 comentaris, 0 trackbacks (URL) , Tags: Python Django
Trucs de l'administrador de Django
Escrit per Aaloy a 19 de March , 2009 a les 11:50 p.m.
Encara que a la documentació de l'administrador ho diu, sovint estam tan acostumats a fer feina amb camps que ens oblidam que al list_display de l'admin de Django hi podem fer servir qualsevol atribut o mètode susceptible de ser cridat (entre d'altres opcions).
Això obre tot un món de possibilitats a l'hora de presentar informació en forma tabular dins l'admin, podem crear enllaços cap a altres seccions, mostrar imatges, el que se'ns pugui ocorre i que tengui algun tipus de lligam amb el model.
Un exemple ben senzill, suposem que tenim un model que té una imatge associada, el que voldríem és que a la informació ens aparegui aquesta imatge, llavors faríem:
Al model afegim
1 2 3 4 5 6 7 | def get_foto(self): if self.foto: return '<img src="%s" width="64">' % self.foto.url else: return "" get_foto.allow_tags = True get_foto.short_description = 'Foto' |
i a l'admin.py afegirem a la configuració de l'administrador del nostre model
1 | list_display = ('get_foto', ...) |
Podem complicar-ho tant com vulguem, un exemple ho tenim al Django Snippet d'Admin list Thumbnail, que complica la funció per a crear les miniatures de les imatges que es presentaran i disminuir així el pes de la plana.
El que és important d'aquest exemple és veure l'ús que es fa de allow_tags, si el mètode té aquesta propietat Django no escaparà l'html que li passem, donant-nos joc a presentar tot tipus de codi html, baix la nostra responsabilitat, clar.
Traducciones/Translations by apertium
0 comentaris, 0 trackbacks (URL) , Tags: Python Django
Fent net
Escrit per Aaloy a 19 de March , 2009 a les 6:52 p.m.
Una de les primeres coses que estic fent al Blog és la part de neteja. El codi és heretat de Blogmaker i hi ha molta cosa que amb les noves versions de Django es pot fer d'una altra manera o senzillament que es pot fer quan abans no es podia.
Ara la part d'administració de Django es pot personalitzar bastant, no és per tirar coets i normalment si s'han de fer coses complexes preferesc crear un backoffice ad-hoc, però pel que és redactar un apunt ja va prou bé.
Això m'ha permès eliminar força codi relacionat amb la part de presentació dels apunts i l'edició. Com que estic fent servir Markdown per a l'edició dels apunts he posat un editor anomenat Markitup, que permet tenir un entorn més amigable per a l'edició de Markdown i personalitzar les accions.
Per posar el codi dins l'administrador ha estat senzill. Amb la versió 1.0 Django va separar la part de vissualtizació i edició del model, amb la qual cosa podem definir com s'editarà el nostre model mitjançant un objecte que hereda de admin.ModelAdmin. Un dels aspectes més interessants és que podem afegir arxius Javascript i css, de manera que afegir el javascript necessari per fer servir l'editor de Markitup enlloc del simple text area ha estat cods de fer
class Media:
"Javascript configuration for Entry model in Admin"
js = [(settings.BLOG_MEDIA_PREFIX + 'js/jquery.js'),
(settings.BLOG_MEDIA_PREFIX + 'js/entry_change_form.js'),
(settings.BLOG_MEDIA_PREFIX + 'js/markitup/jquery.markitup.pack.js'),
(settings.BLOG_MEDIA_PREFIX + 'js/markitup/sets/markdown/set.js'),
(settings.BLOG_MEDIA_PREFIX + 'js/editor.js')
]
css = { 'screen': (
(settings.BLOG_MEDIA_PREFIX +
"js/markitup/skins/markitup/style.css"),
(settings.BLOG_MEDIA_PREFIX +
"js/markitup/sets/markdown/style.css") )
}
A fer dissabte s'ha dit!
Traducciones/Translations by apertium
0 comentaris, 0 trackbacks (URL) , Tags: Python Django
Nova versió del blog de Trespams
Escrit per Aaloy a 18 de March , 2009 a les 10:56 p.m.
Avui hem posat en producció (beta total) una nova versió del programari que fa que aquest blog pugui llegir-se. Això potser ho haureu notat els que feis servir l'Akregator, ja que els fils RSS us apareixeran duplicats (al manco a mi me passa), em sap greu.
Les novetats són sobretot en la part tècnica: nou cercador, més javascript en la part d'administració i l'execució baix un entorn virtualenv que permetrà fer-ne actualitzacions de manera separada de la resta d'aplicacions Django del servidor.
Aniré escrivint damunt les millores així com tengui temps. Aquest apunt és sols per avisar que Trespams està en beta i que si peta miserablement i m'ho voleu fer saber us estaré agraït.
Traducciones/Translations by apertium
0 comentaris, 0 trackbacks (URL) , Tags: Python Django
Validació de formularis amb Ajax, jQuery i Django
Escrit per Aaloy a 01 de March , 2009 a les 2:37 p.m.
Validació Ajax amb Django
A un dels projectes que estam fent el client va demanar una funcionalitat que requeria que s'enviàs un formulari al servidor i se'n fessin les validacions, però que la plana no es tornàs a recarregar. Un treball per Ajax, se'ns dubte. El problema era que tal com funciona el mecanisme habitual de processament de formularis amb Django, es requereix que es torni a recarregar la plana.
Una opció podria ser la de fer tota la validació del formulari amb Javascript, però llavors tendríem el perill de que l'usuari desactivàs el Javascript al navegador i pogués enviar dades directament sense validar. Podría fer-se tot per duplicat, és a dir, fer la validació amb Javascript i també al servidor, però això va en contra de la norma DRY (Don't repeat yourself).
Així doncs el que volem és el següent:
Enviar el formulari per ajax, però que sigui també possible fer un POST normal.
El servidor ha de fer les validacions i la plana presentar el missatge d'error si hi ha problemes.
Primer sense javascript
El primer que hem de fer és veure que tot funciona abans de posar-hi el javascript, així que crearem la nostra aplicació, crearem el formulari i hi posarem les valicacions que vulguem.
Farem un formulari molt senzill, el típic formulari de contatcte, però ho aprofitarem per introduïr-hi una opció nova a Django 1.0: la personalització dels missatges de validació.
from django import forms class ContactForm(forms.Form): "Defines the login for as in the Django sample" subject = forms.CharField(label="subject: *", max_length=100) message = forms.CharField(label="Request", widget=forms.Textarea(attrs={'rows':'10', 'cols':'80'})) email = forms.EmailField(label = "Your email *", max_length=120, error_messages = {'required': u"No e-mail, no message"}) cc_myself = forms.BooleanField(required=False) def clean_message(self): "We wan't to verify that it containts some words" msg = self.cleaned_data['message'].strip() if len(msg.split(None)) >5: raise forms.ValidationError(u"Really? This is quite short for a message") return msg
La manera més ràpida de mostrar el formulari és quelcom com
<table>
<form action='.' method="POST">
{{form}}
<tr><td span="2"><input type="submit" value="send" name="enviar" /></td></tr>
</form>
</table>
Però té un problema, que no controlam on es posen els missatges d'error i nosaltres volem controlar-ho. En aquest cas, i per simplicitat, posarem tots els missatges d'error a la part superior de la pantalla. Així que hem de rescriure la plantilla per a que ho contempli.
El nostre formulari queda doncs com
{% if form.errors %}
{{ form.errors }}
{% endif %}
<table>
<form action='.' method="POST" >
<tr><td>{{form.subject.label}}<td>{{form.subject}}</td></tr>
<tr><td>{{form.message.label}}<td>{{form.message}}</td></tr>
<tr><td>{{form.email.label}}<td>{{form.email}}</td></tr>
<tr><td>{{form.cc_myself.label}}<td>{{form.cc_myself}}</td></tr>
<tr><td span="2"><input type="submit" value="send" name="enviar" /></td></tr>
</form>
</table>
És a dir, ara tenim exactament el que teníem abans (no us hi fixeu amb la maquetació), però ara si hi ha errors es presentaran a la part superior del formulari.
Concentrem-nos ara en la manera de presentar els errors. Com que la nostra idea és que es puguin mostrar els errors que venguin per Ajax, el que farem és no presentar-los així, sinó que els posarem dins un div i farem que aquest es presenti o no en funció de si hi ha errors.
{% block errors %}
<div class="errores" {% if not form.errors %} style="display: none" {% endif %} >
<p id="error_msg">
{{form.errors}}
</p>
</div>
{% endblock errors %}
Això a efectes pràctics és el mateix que el cas anterior, ja que com que si no
hi ha errors from.errors no mostrarà res. El que sí hi ha ja és
tota l'estructura que ens servirà per mostrar els errors dins l'arbre DOM de la
plana web.
I ara i afegim l'Ajax
Necessitam tres coses: enviar el formulari per ajax, que si hi ha errors de validació els puguem presentar i que si tot va bé es faci la redirecció cap a la plana que toqui.
Hi ha alguns projectes que volen fer aquest tipus de coses de manera més o manco genèrica, però ara per ara no n'hi ha cap que em convenci, així que millor ho feim a mà. Si més no us deixo alguns enllaços:
El que sí faré es manllevar codi d'aquests projectes per als nostres propòsits, especialment del Django Ajax Validation.
En concret, adaptam el codi que ens permet obtenir els errors i passar-los a una estructura json:
class LazyEncoder(JSONEncoder): def default(self, obj): if isinstance(obj, Promise): return force_unicode(obj) return obj def validate(request, form, new_url=''): if form.is_valid(): data = { 'valid': True, 'url': new_url } else: if request.POST.getlist('fields'): fields = request.POST.getlist('fields') + ['__all__'] errors = dict([(key, val) for key, val in form.errors.iteritems() if key in fields]) else: errors = form.errors final_errors = {} for key, val in errors.iteritems(): if key == '__all__': final_errors['__all__'] = val if not isinstance(form.fields[key], forms.FileField): html_id = form.fields[key].widget.attrs.get('id') or form[key].auto_id html_id = form.fields[key].widget.id_for_label(html_id) final_errors[html_id] = val data = { 'valid': False, 'url': new_url, 'errors': final_errors, } json_serializer = LazyEncoder() return HttpResponse(json_serializer.encode(data), mimetype='application/json')
El validate és una funció prou genèrica, ens tornará una variable, valid que ens indicarà si hi ha errors de validació o no, la url on s'ha de redireccionar i la matriu d'errors.
Django ja posa al request una variable per indicar-nos si la petició s'ha fet per HttpRequest o no, request.is_ajax() això ens permetrà distingir com s'ha enviat el post i fer la validació d'una manera o altra
def index(request): redirect_url = '/thanks' if request.method == 'POST': contact_form = ContactForm(request.POST) if request.is_ajax(): return validate(contact_form, redirect_url) else: if contact_form.is_valid(): # do whatever you need as everything is valid return HttpResponseRedirect(redirect_url) else: contact_form = ContactForm() return render_to_response('index.html', {'form': contact_form})
El codi del view.py és d'allò més senzillet, sols feim que si la petició ve per post, tornam una cosa o altra en funció de si aquesta és una petició Ajax o si és una petició normal.
Fins ara no hem posat gens de javascript i tot segueix funcionant amb normalitat.
És temps de posar-ho les llibreries javascript. Ho farem amb jQuery, que és la que tenc més per mà, supòs que amb altres llibreries no hi ha d'haver cap problema, i a més faré server un plugin força bo per al tractament de formularis amb jQuery, el jQuery Form Plugin.
Com presentar els missatges, si s'han de presentar tots, els efectques que volguem, ja es cosa nostra. El json el que ens torna és l'identificador del camp i una matriu amb tots els errors que hi hagi. El procés que facem ja és cosa nostra.
$(document).ready(function(){
form_options = {
timeout: 3000,
dataType: 'json',
type: 'POST',
beforeSubmit: function(formData, jqForm, options) {
// you can add additional validation here and return
// false if it's not valid
jQuery('#boton').toggle();
},
success: function(responseJson, statusText) {
if (responseJson.valid) {
// redirect to hte new url
document.location.href = responseJson.url;
} else {
// create the error structure
var msg = ""
for (key in responseJson.errors) {
camp = key.split('_')[1];
msg = msg + camp+":"+responseJson.errors[key][0]+"<br/>";
}
$('#error_msg').html(msg+'<br/>');
$('.errores').show(100);
jQuery('#boton').toggle();
}
}
};
$('#testform').ajaxForm(form_options);
});
A l'exemple el que he fet és montar un missatge amb el primer error de cada camp sols a efectes demostratius, podeu fer el que us vengui millor: validar abans d'enviar, mostrar efectes als camps dels errors, mostrar un diàleg, sols estau limitats per la vostra imaginiació i el Javascript.
El codi complet de l'exemple l'he pujat al projecte appfusedjango
Traducciones/Translations by apertium
6 comentaris, 0 trackbacks (URL) , Tags: Python Django
Llibres a febrer de 2009
Escrit per Aaloy a 26 de February , 2009 a les 9:19 p.m.
Ahir em vàren arribar dos llibres nous:
- jQuery in Action de Bear Bibeault i Yehuda Katz, editorial Manning, ISBN 978-1933988351
- Django 1.0 Template Development de Scott Newman, editorial Packt Publishing, ISBN 978-1-847195-70-8
Pels títols ja sabeu de què van, així que com sempre us dic un poc el que esper de cada un i les primeres impresions:
JQuery in Action
De l'estil d'aquesta sèrie de Manning: bona edició i continguts interessants explicats correctament. Estic encara als primers capítols de la primera lectura ràpida i m'està agradant força. Al JQuery l'estic fent servir cada cop més i necessitava un llibre com aquest per poder-ne tenir una idea completa. Hi ha molta documentació del JQuery, però està prou dispersa com per a que un llibre com aquest tengui sentit.
Django 1.0 Template Development
Aquest ho vaig comprar perquè m'interessava veure com s'introduïa el tema del desenvolupament Django orientat a dissenyadors més que a programadors. Com sabeu sóc de la opinió que el dissenyador/maquetador ha de formar part activa dins un equip de programació web, amb el mateix nivell d'implicació que un analista/programador. Això vol dir fer anar un subversion, maquetar amb un editor de text com Eclipse o Netbeans i com no entendre com es poden fer servir les plantilles Django de la mateixa manera que es coneixen les possibilitats del CSS. La conclusió que en tenc per ara és: a poc a poc i amb molts exemples. Me pareix una bona aproximació i esper tenir l'oportunitat de posar-ho en pràctica. El llibre pot ser un bon material de referència per a propers cursos.
Traducciones/Translations by apertium
2 comentaris, 0 trackbacks (URL) , Tags: Llibres i revistes Python Django
Django a l'empresa: liquidacions de despeses
Escrit per Aaloy a 22 de February , 2009 a les 6:57 p.m.
Aquesta setmana hem posat a preproducció una aplicació interna per a la gestió de les despeses feta amb Python, Django i Postgres. La idea és poder saber quines són les despeses més comuns per tal de fer-ne un seguiment i si s'escau poder fer una negociació amb els preoveïdors habituals.
Per això era necessari que les liquidacions de despeses que fins ara es feien amb un excel (quin mal fan els excels a les empreses!) es facin ara mitjançant una aplicació web. D'aquesta manera a la primera etapa l'usuari té exactament el que tenia abans, a saber, una fulla de paper que el seu cap pot signar i després presentar al caixer; però a més l'empresa començarà a tenir dades que es podran analitzar.
L'autentificació dels usuaris es fa contra l'Active Directory de l'empresa, en les darreres versions de Django és trivial canviar de sistema d'autentificació i han aparegut diferents snippets que ens permeten canviar el sistema d'autentificació estàndard per un altre. En el meu cas l'snipet d'autentificació per l'Active Directory ens ha anat força bé. De la mateixa manera seria trivial fer l'autentificació contra un LDAP o contra un altre sistema és força senzilla i la documentació de Django és molt aclaridora.
En aquesta aplicació a més de l'autenticació hem fet molta feina amb jQuery, hi ha moltes dades amb autocompletat (per les companyies aèries i pels aeroports per exemple), validacions, quadres de diàleg modals, etc. Es tractava de fer una aplicació el més àgil possible, però sense deixar de ser una plana web, per tal de no assustar massa als nostres usuaris. És una cosa en la que s'ha d'anar alerta, m'agrada molt l'article de Jefff Atwood Avoiding The Uncanny Valley of User Interface que explica el perquè es pot produïr un rebuig de l'usuari quan les coses volen ser massa paregudes al que ja coneix però sense arribar a ser-ho. En el nostre cas els usuaris conèixen les aplicacions d'escriptori i les interfícies de Forms i qui més qui manco sap fer anar un navegador web.
Podríem haver fet una aplicació RIA amb Extjs per exemple, però per molts dels usuaris que faran servir l'aplicació veurien que és una aplicació d'escriptori sense notar que s'executa al navegador. Per ells això implica complexitat, que algú en faci la formació individualitzada com es fa a les altres aplicacions. Massa cosa per el que ha d'esser una aplicació que té per objectiu imprimir un paper. Així doncs vàrem preferir que l'aplicació es semblàs a una plana web, amb formularis i enllaços, on el que s'imprimeix també és una plana web. Potser encara així algú trobarà l'aplicació complexe, però crec que ja serà per por i reticència al canvi més que per mor de l'aplicació en sí.
L'aplicació ha estat desenvolupada per un equip relativament nou en la programació Python i Django i tot i això s'ha pogut entregar en el temps previst (que ja considerava aquest fet per una altra banda). Això no fa més que refermar-me en el convencimient de que a més de la potència del llenguatge hem de considerar també la corba d'aprenentatge a l'hora de triar amb què farem el desenvolupament.
En aquest projecte i en els temps de crisi en que estam, encara em resulta sorprenent que hi hagi algú que es qüestioni si la base de dades en lloc de ser Postgres no hauria de ser Oracle. En aquests casos m'agradaria poder amollar allò de "It's about costs stupid!" i dedar-me tan ample. El que més em sobta és que s'amolla com si tu fossis el que has de justificar la decisió, quan realment hauria de ser a l'inrevessa. Per què fer una cosa damunt Oracle quan es pot fer amb Postgres (o Mysql)? Amb preocupa aquest tipus d'inèrcia informàtica, de curtor, que no s'atura a pensar en els costs actuals i futurs, tant és si hi ha al mateix moment algú d'Oracle demanant pel nombre de llicències. Sorprenent!
Però bé, és d'aquestes coses que acabes assumint com a comportament normal i que potser ni amb tota la pedagogia del món s'arribarà a res. Tenc l'esperança que aquesta crisi que vivim al manco servesqui per a que la gent es plantegi un poc la relació costs beneficis. Sols que s'aturi a pensar-ho un moment de ben segur que ja no caldrà el justificar el perquè s'ha anat cap a una solució de programari lliure. La part ètica? Què voleu que us digui, si la part econòmica ja costa (i estam parlant d'empreses!) imaginau-vos que pot arribar a costar que s'entengui el que representa el programari lliure en la vessant social i ètica.
I és una llàstima, perquè hi ha moltes empreses sortint de l'armari, que publiquen el codi que fan servir internament en forma de projectes lliures, de llibreries. El darrer exemple que se m'acud ve del Washington Times que ha obert un blog amb un bon conjunt de projectes Django o també un post damunt robots controlats amb Python.
Projectes com els que treim darrerament venen a mostrar que la tan cercada reutilització, els mètodes per lluitar contra la crisi del software, potser són molt més aprop del que ens pensam. El codi lliure permet la reutilització a nivells mai somiats, Python a més ho fa més fàcil, Django ho fa divertit per la web.
Traducciones/Translations by apertium
6 comentaris, 0 trackbacks (URL) , Tags: Python Django
Caçant bubotes
Escrit per Aaloy a 14 de February , 2009 a les 1:43 p.m.
Poc a poc hem anat evolucionant la nostra arquitectura d'aplicacions des de aplicacions monolítiques fetes amb J2EE cap a aplicacions on la capa de serveis està en Java (per ara!) i aquests es consumeixen amb Python, utilitzant Django com a bastiment web.
Per a consumir serveis SOAP feim servir una llibreria anomenada ZSI que ens permet generar les classes Python a partir el WSDL dels serveis.
ZSI és un projecte que avança molt a poc a poc, veus que no està mort, que funcina, però la versió 2.1 fa estona que està en alfa. Tot i això, la versió 2.0 funciona prou bé com per poder consumir els serveis sense problemes.
L'altra dia però, ens vàrem trobar amb un problema d'aquests que classific com a "bubota". Atacàvem al servei que tenim de Transfers i tot anava com a la seda, atacàvem al d'excursions i el mateix. Atacàvem als dos dins la mateixa aplicació i l'espai de noms es feia un embolic, de manera que ens posava l'espai de noms del primer servei que s'executava.
Ho va descobrir en Juanjo i jo li deia que no podia ser fins que vaig fer un petit test de manera independent de l'aplicació que estam fent i ho vaig tocar amb les mans. Hi havia un problema i ara el problema era poder identificar a on, al webservice (poc probable), a la capa de servei que cosumia el SOAP (probable), al mapeig del ZSI (menys probable) o al ZSI en si (encara menys probable).
Quan un es troba amb un problema així, el primer és anar de més probabilitat a menys i anar descartant coses. La idea a més és aprofitar que hom està fent la depuració per a refactoritzar el codi, de manera que la feina de depuració serveixi a més per deixar un codi més clar i per tant més fàcil de depurar (recordau la primera regla de la refactorització: l'aplicació ha de funcionar exactament com funcionava abans, no introduïm nova funcionalitat).
Així doncs va començar un procés de refactorització de la primera capa, la que consumia el servei mapejat amb ZSI. Vaig organitzar millor el codi en paquets, vaig eliminar importacions innecesàries i vaig aillar totalment la part de transfers de la part d'excursions. Els tests però treien sempre el mateix, error quan atacàvem als dos serveis a l'hora.
La següent passa va ser anar a veure el codi generat pel ZSI (amb wsdl2py). Els generador de codi van molt bé, però sovint són massa genèrics. Així que també vaig fer-ne la refactorització. Eliminar totes les importacións amb arterisc, canviar nom comuns als dos paquets de transfers i excursions, i passar la part de defnició de tipus Soap a un arxiu independent, de manera que sols importàssim els realment necessaris. Tampoc! Els tests seguian donant el mateix... Bé al manco la refactorització era conseqüent...
Ara ja sols quedava la passa de la depuració línia a línea, fins i tot anant al codi del ZSI. Per això vaig començar amb un depurador senzillet comp el pdb (amb la seva versió d'ipdb) i vaig poder identificar a quin punt hi havia el problema. Pareixia estar en les cridades que feia el codi generat cap el ZSI. Per a facilitar la depuració en aquest punt ja vaig passar a un depurador gràfic, el winpdb, ja que permet veure d'una ullada totes les variables locals i globals.
Entrant al ZSI vaig descobrir la bubota. El ZSI cacheja els tipus d'objectes generats, però de tal manera que ho fa de manera global a l'aplicació, per tant, si dos objectes tenenen el mateix nom (com era el nostre cas), retorna el primer nom generat amb el seus espai de noms (cagada del ZSI al meu entendre). Potser és poc comú el que feim, però crec que és un cas típic d'optimització prematura. Es cachegen els tipus generats potser sense importar-hi.
La solució doncs va ser llevar les cachés, fes que generàs el tipus cada cop. Esperava pot ser una pèrdua de rendiment, però no és així, pareix que fins i tot posar i treure de la caché és comparable al temps d'execució a la genereació del tipus. He d'afinar un poc més amb aquesta apreciació, però el que està clar és que per al consum que es fa del servei, la diferència de temps no és significativa a l'ordre dels milisegons, i del tot menyspreable si ho comparam amb el *lag * produït per les línees de comunicació. Amb la caché activada 1 segon, i sense 1 segon igual.
La bubota està caçada, els fantasmes desapareixen quan es fa la llum i amb tot el procés he après molt millor com funciona el ZSI per dintre.
Traducciones/Translations by apertium
0 comentaris, 0 trackbacks (URL) , Tags: Python Django
Entorns de treball virtuals per Python
Escrit per Aaloy a 12 de February , 2009 a les 6:23 p.m.
Si he fet feina amb Python amb projectes diferents us haureu trobat amb algun d'aquest problemes:
La llibreria que heu devallat del svn per a un projecte té canvis, les necessitau pel projecte actual però són incompatibles amb el projecte anterior.
Necessitau provar l'aplicació que teniu amb les mateixes llibreries i versió que hi ha a producció. Podeu tirar de svn per la versió de producció, però les dependències de les llibreries han canviat.
Teniu tants paquets i llibreries instal·lats al
site-packagesque les utilitats d'autocompletat es tornen bojes per mostrar-vos les ajudes.
Per a resoldre tots aquets problemes hi d'altres semblants podem fer ús de dues utilitats com són el virtualenv i el virtualenvwrapper, creades per Ian Bicking i Doug Hellmann respectivament. Veurem en aquest article com fer-les servir.
El primer que hem de fer és instal·lar-les. Ho podem fer amb easy_install virtualenv i easy_install virtualenvwrapper. La primera utilitat és la que realment farà la feina, l'altra és un script que ens facilita el maneig i la gestió dels distints entorns.
Suposaré que ens trobam en la situació de que volem crear un entorn net, on crearem la nostra aplicació i que tendrà totes les llibreries i dependències necessàries per a executar-la. Per començar el primer que farem serà configurar el nostre entorn de treball
$mkdir ~/.virtualenvs
Ara editarem el nostre arxibu .basrch i hi afegim
export WORKON_HOME=$HOME/.virtualenvs source /usr/bin/virtualenvwrapper_bashrc
La localització del virtualenvwrapper_bashrc pot canviar segons la distribució, adaptau-la segons convengui.
Després d'editar el .bashrc feim un source ~/.bashrc per a activar els canvis i ja podem crear el nostre primer entorn virtual.
Crearem un entorn anomenat estable que tindrà la versió estable de Django i poca cosa més. Prèviament ens haurem baixat dita versió i l'haurem descomprimida a un directori temporal.
$ mkvirtualenv --no-site-packages estable $ workon estable
Això ens haurà creat un entorn de Python amb les llibreries per defecte. És el que li hem dit amb --no-site-packages. Si no li haguéssim posat aquesta opció, ho hagués creat, però amb tot el que tenim al site-packages actual, si ens interessa bé, però si no, com és el cas, millor pensar en crear els entorns virtuals fent servir aquesta opció.
Ara podem anar al directori on hem descomprimit Django i fer un python setup.py install. Si us hi fixau veureu que Django ara no s'instal·la al directory site-packages del sistema, sinó al del entorn virtuals. Això és així perquè hem activat l'entorn amb la comanda workon. Aquesta comanda ens permet veure els entorns que tenim definits i a més, posant-hi el nom del entorn, canviar d'entorn virtual, a partir del canvi, les comandes d'instal·lació via setup.py o easy_install es faran a l'entorn virtual actiu.
Si voleu tornar a l'entorn de treball del sistema podeu desactivar l'entorn virtual amb un deactivate.
Tenim un petit problema però, necessitam el PIL i és una tudada d'espai tenir-ho que instal·lar des de zero. Per això podem fer dues coses, o bé crear un enllàç simbòlic dins l'entorn virtual, en el nostre cas dins $WORKON_HOME/stable/lib/python2.5/site-packages/ o bé fer ús de la comanda que ens proporciona el virtualenvwrapper anomenada add2virtualenv. Així si executam
$workon estable $add2virtualenv /usr/lib/python2.5/site-packages/PIL
Hauríem afegit al nostre entorn virtual el paquet PIL del sistema. Ho podeu anar fent amb paquets que necessiteu, però teniu en compte que l'objectiu de l'entorn virtual és tenir un sistema net i no acabar amb tots els paquets que hi ha al sistema llincats.
Podem crear tants entorns virtuals com necessitem i vulguem, fins i tot un per cada aplicació. La comanda workon ens permetrà visualitzar-los i canviar entre ells amb tota facilitat.
Si optau per crear un entorn virtual per aplicació segurament trobareu molt útil saber que a directori bin del nostre entorn virtual podem crear dos scripts que d'existir s'executaran associats a l'entorn virtual, es tracta de postactivate que s'executa després d'activar l'entorn i predeactivate que s'executa abans de la desactivació.
En algunes aplicacions el meu postactivate és tan senzill com un canvi de directori cap a la carpeta de feina de l'aplicació i una actualització (via svn up) del codi.
Anem ara al tema de l'autocompletat. Com ja he comentat darrerament estic fent servir Netbeans per Python com a entorn de desenvolupament. Netbeans ens permet definir quins entorns d'execució farem servir (i gràcies a Xus vaig saber del cromo que deia que podíem fer-ho servir per a indicar a Netbeans que fes servir aquest entorn.
Una vegada creat el nou intèrpret de Python dins el Netbeans segurament encara haureu de pensar en fer una cosa més, assignar-ho al nostre projecte ja que en cas contrari tindria l'entorn antic. Així doncs anau a les propietats del projecte i assignau com a Python Platform l'entorn virtual que acabau d'afegir.
Podeu crear tants entorns d'execució de Python com siguin necessaris, cada un amb el seu entorn virtual associat i fer que sigui l'entorn d'execució per defecte del projecte. Això farà que l'autocompletat sols us mostri les opcions que es poden agafar d'aquestes llibreries, resolent un dels emperons més grans que particularment li trobava a l'autocompletat de Netbeans. Amb això i amb la recent descoberta de que depurar Javascript des de Netbeans és possible i més àgil que amb firebug (encara que ho faci servir, deu ser cosa del refresc del navegador), encara fa que em decante més per Netbeans com a entorn de desenvolupament per defecte per Python amb el permís de Vim, clar.
Us aconsello jugar amb els virtualenvs, fer proves, mirar quins paquets i utilitats feis servir més, si els vostres aplicatius s'executen bé en un entorn net (com possiblement serà l'entorn de producció), podeu crear-ne tants com vulgeu. Un entorn típic per a desenvolupar amb Django té un tamany de 35M (du -h $WORKON_HOME/estable/), que no és res pels tamanys dels discs durs actuals.
Traducciones/Translations by apertium
0 comentaris, 0 trackbacks (URL) , Tags: Python Django
Balanç del 2008
Escrit per Aaloy a 31 de December , 2008 a les 11:32 a.m.
Aquest serà el darrer apunt d'aquest any, i com a tal toca fer balanç del que ha significat aquest 2008 que aviat s'acaba.
Programació
A l'aspecte de programació ha estat un bon any: Django ha tret la versió 1 i Python l'esperada versió 3. Hem vist com han aparegut força projectes basats en Django que marcan la línia cap a l'esperada reutilització de codi.
Ha estat l'any on personalment he fet molta més programació amb Django que en Java. Els tipus de projectes que han sortit feien que fos molt més ràpid i optim fer-ho d'aquesta manera. Alguns dels llocs web han tingut força visites i hem pogut comprovar el bé que se comporta Django.
L'aparició de Netbeans per Python també crec que és una notícia prou important. Fins ara pareixia que Python estava apartat dels plans de Sun (no així dels de Google com s'ha vist amb l'AppEngine) i pel que apunta pareix que a mitjà plaç i amb permís d'Eclipse i Aptana podria ser l'editor de referència per Python.
Gestió de projectes
He seguit amb discussions filosòfiques damunt el que és un projecte i el que no és. Per mi un projecte per definició ha de tenir una data d'acabament, però ja he perdut l'esperança de que algunes coses canviïn.
Scrum com a metodologia cada cop m'agrada més. Estic molt còmode amb la idea de que les persones són el més important de l'equip i de que el cap de projecte ha de ser un facilitador. Scrum s'adapta molt bé a aquesta manera de pensar i de fer.
De la família
Llums i ombres enguany. Llum pel naixement del meu segon nebot, ombres per la pèrdua de gent propera, en un cas molt jove que va omplir la família de consternació, i un petit ensurt amb el Pare que afortunadament no ha anat a majors.
Els meus menuts creixent dia a dia, enguany ja saben colcar en bicicleta (amb 3 sessions en tingueren prou per aprendre'n!) i trobar la IP del seu ordinador Linux.
Dels amics
Donar-vos les gràcies per ser-hi, per aguantar-me els acudits dolents i pels bons moments que hem passat junts. La vida no seria digne de ser dita vida sense els amics.
De la feina
La feia principal rara, moltes baixes, buids de poder i una fusió que pareix que no s'arriba a concretar. Personalment he demanat una reducció de jornada que esper es concreti al 2009. No m'agraden les situacions d'impàs i mentre tot es defineix prefereixo dedicar més temps a la família.
De la feina "pla B" bé, molt bé. Projectes molt interessants que esper es concretin molt aviat. El 2008 ha estat un any de transició, d'agafar embranzida per l'any següent i crec que s'ha aconseguit.
Gràcies a tots i totes per seguir aquest blog i pels comentaris que anau deixant. Esper que el 2008 hagi estàs profitós per les vostres vides. Ens llegim al 2009!
Traducciones/Translations by apertium
7 comentaris, 0 trackbacks (URL) , Tags: General Python Django
Django no és PHP ni JSP
Escrit per Aaloy a 27 de December , 2008 a les 1:38 p.m.
Alguna de la gent que s'atraca a Django per desenvolupar web ve de PHP o del món Java (amb JSP) i intenta aplicar les mateixes tècniques que havia fet servir abans, trocejant les planes i fent servir includes, desaprofitant la potència de les plantilles de Django.
Includes per la maquetació
Els includes existeixen en Django, però no tenen la mateixa importància que en PHP a l'hora de fer la maquetació.
Les plantilles a Django es poden heretar. Això és un concepte nou per la gent que ve de PHP o JSP, però és la manera de muntar les planes web. El que feim és definir la nostra plantilla base (o plantilles) i programarem per diferències. És a dir, quan hem creat la plantilla, la plana següent la maquetarem pensant "és com la plantilla base però amb aquesta secció i aquest altra diferent".
Cada una de les diferències la podem posar dins un bloc. Així les plantilles hereten els continguts i el blocs de les plantilles pare i podem sobreescriure els continguts dels blocs.
Això vol dir que normalment no trocejarem la nostra plana en capçalera, menú, contingut i peu, en quatre arxius diferents, sinó que estarà tot dins el mateix arxiu i el que farem serà definir distints blocs segons convengui o no sobrescriure'ls en les plantilles filles.
No hem de passar pena de posar massa blocs, de fet millor trocejar massa que massa poc. I encara així, sempre som a temps d'editar la plantilla base per anar afegint els blocs que necessitem.
Includes per a carregar llibreries
JSP té el concepte de taglibs per a realitzar funcions com el formateig (fmt), estructures de control (core), etc.
A Django la majoria d'aquestes estructures estan ja dins el llenguatge de plantilles, definint els tags i els filtres. Els primers realitzen accions complexes i contenen les estructures de control de fluxe, els segons ens permeten modificar el contingut final que es mostrarà a l'usuari.
Com al Java amb el JSP Django ens permet definir llibreries de tags i filtres que es poden utilitzar en la generació de la plantilla amb un simple {% load "nom-de-la-llibreria" %}.
Les plantilles de Django no permeten incloure codi Python i el que es pot fer és limitat. La idea és que la plantilla tracti sols amb la capa de presentació i tot el que requereixi tractar amb regles de negoci o funcionalitat complexa es delegui a la capa Python.
Els includes de Django
Estan pensats per evitar tenir que repetir codi no per a la maquetació dels continguts. Pensem en el cas que tenim funcionalitat semblant però que ha d'anar a plantilles amb unes estructures molt diferents. Llavors el que feim és extreure aquell troç de funcionalitat i posar-ho a un arxiu separat per tal que es pugui reaprofitar el codi.
És més, si la repetició encapsula funcionalitat i aquesta es pot reaprofitar en altres aplicacions l'adequat seria crear un tag que generàs l'HTML. Django proporciona fins i tot decoradors que ens permeten crear molt fàcilment plantilles html que agafin paràmetres, o crear tags que generin HTML tal i com nosaltres el volem.
En definitiva, abans de posar-vos a maquetar convé fer una ullada a la documentació de Django, començant per Django template for designers que ens introduirà en els principals conceptes, després pegar una bona llegida als tags i filtres i una vegada dominem Python anar cap a la creació de les nostres pròpies llibreries de tags.
Traducciones/Translations by apertium
4 comentaris, 0 trackbacks (URL) , Tags: Python Django
Cursos de Python i Django
Escrit per Aaloy a 23 de December , 2008 a les 6:03 p.m.
Els que em coneixeu segurament haureu notat que m'agrada ensenyar de la mateixa manera que m'agrada aprendre. Ensenyar permet compartir experiències i coneixements amb un nivell de comunicació que és difícil d'assolir en un llibre o un article, i poder ensenyar allò que a més t'agrada ho fa tot molt més divertit.
Com en moltes coses, per mi la màxima és fer les coses com m'agradaria que algú les fes si fossin per mi. Això sovint fa que m'ho prengui de vegades massa seriosament, sóc exigent amb els altres i per tant també ho sóc amb mi.
Per a que un curs sigui productiu la gent que hi assisteix hi ha d'anar mínimanent motivada. Una manera de tenir aquesta motivació és que la matèria que s'hi imparteix es vaig a utilitzar en un futur proper o ja s'estigui utilitzant.
Python i Django fa que sigui molt ràpid poder tenir un entorn de proves i començar a fer coses. La corba d'entrada és molt suau i es poden començar a fer aplicacions molt ràpidament, per tant, augmenten les possibilitats de que la gent ho pugui fer servir en el seu dia a dia, fins i tot si no s'hi dedica al 100%. Tasques d'administració de sistemes, scripts d'importació, generació de codi, etc. són molt ràpides de fer en Python i per tant es pot veure un resultat immediat del que s'ha après. La realimentació que s'aconsegueix, la gratificació de l'ego, fa que la gent pugui veure una utilitat en allò que fa.
A mi els formadors que m'agraden són aquells que a més de dominar la matèria (no ho doneu per suposat, no és la primera vegada que vaig a un curs on que l'impartia sols sabia el que deien les presentacions que duia preparades) hi fan feina. Sobretot en la part tècnica això es nota molt. Qui fa feina dia a dia amb una tecnologia a més de tenir-ne els coneixements també us podrà donar consells, millors pràctiques, trucs de la professió que no estan als manuals. Llavors és cosa de cada un aplicar-los o no, però el que està clar que a més d'una simple explicació de coneixements hi ha la part de transmissió d'experiència.
Per això m'agrada que els formadors no es dediquin professionalment a la formació, sinó que la seva feina principal impliqui fer feina amb aquella tecnologia i obviament m'aplic el mateix criteri. M'agrada ensenyar, però em trob molt més còmode amb mi mateix ensenyant allò que faig servir, en el meu cas Python, Django o gestió de projectes informàtics.
Ensenyar implica també preparar-se les classes. Per mi aquesta preparació no significa llegir-se la matèria (que també) sinó a més preparar exemples, la línia argumental de la classes i un conjunt de "plans bé". És a dir, tu prepares el que se suposa que serà el contingut de la classe d'aquell dia, però pot ser que s'avanci més ràpid del que s'havia pensat, o que sorgeixi més interès per un tema concret o que trobis que és millor fer un exemple més senzill per acabar de lligar el que s'havia explicat. Poder anticipar-se a tot això i tenir-ho previst també fa que s'avanci molt més en la formació. A mi que el formador se quedin pensant en el que ha de donar a continuació no m'agrada gens.
És difícil trobar gent bona per donar formació, de la mateixa manera que és difícil trobar bons programadors. Particularment aspir a que la gent acabi amb el convenciment de que després dels cursos en sap més que abans de començar, que la formació se li faci curta i es quedi amb ganes de saber-ne més.
Traducciones/Translations by apertium
6 comentaris, 0 trackbacks (URL) , Tags: Informàtica Python Django
Formulari compost a Django
Escrit per Aaloy a 23 de November , 2008 a les 7:48 p.m.
Get the Englist translation of this post at: Gencat
Obtén la traducción al castellano de este post en: Gencat
Django té un sistema de tractament de formularis molt bo. Ens permet per una banda la reutilització de codi, ja que separa el que és la definició del formulari i la seva validació de la part de presentació.
La validació de la informació és d'allò més interessant, ja que assegura que si un camp del formulari l'hem definit com a sencer, el valor de la variable que obtindrem serà un sencer, o en cas contrari hi haurà un error de validació.
Per tot això, la idea és utilitzar el formularis de Django sempre que sigui possible i reutilitzar tot el que tinguem.
El problema es presenta quan en una plana ens n'adonam que necessitam dos formularis, no hi ha cap exemple de com fer-ho a la documentació. Veurem que una vegada solucionat el problema és tant obvi i simple que efectivament no importa documentar-ho, encara que estaria bé :)
Per a aquest exemple he posat el codi al repositori d'appfusedjango
Anem per feina
La idea és ben senzilla, crearem dos formularis i els farem servir a la nostra vista. Per a facilitar-no les coses, podem posar els dos formularis dins una llista de manera que per muntar la plana sols haurem de recorre la llista de formularis que li passam.
A l'exemple he creat dos formularis LoginForm i ContactForm, que es generaran a two_forms.html
def two_forms(request):
if request.method == 'POST':
contact_form = ContactForm(request.POST)
login_form = LoginForm(request.POST)
if login_form.is_valid() and contact_form.is_valid():
# do whatever you need as everything is valid
return HttpResponseRedirect('/thanks/')
else:
contact_form = ContactForm()
login_form = LoginForm()
forms = [contact_form, login_form]
return render_to_response('two_forms.html', {'forms': forms})
Com podem veure el funcionament és el mateix que en el cas d'un formulari normal, sols que hem crear-ne dos (o els que vulguem) i anar cridant al mètode is_valid per fer les validacions.
Això funciona perquè cada formulari agafa del post sols les dades que coincideixen amb els camps que té definits i perquè a l'hora de muntar la plana Django mira si el formulari té errors (la validació es fa tant cridant a is_valid com accedint als errors), d'altra manera i donat que Python fa servir l'avaluació curta de l'and, no tindríem els errors del segon formulari si el primer ja no validàs. Això explica el perquè hi ha aquesta doble via de llançar la validació a Django.
I això ens duu de manera natural a la següent qüestió, què passa si als formularis hi ha camps amb al mateix nom?
Doncs que Django no serà capaç d'esbrinar a quin camp correspon cada cosa in ens trobarem amb un petit problema.
Però tranquils, això està controlat, basta afegir un prefix que sigui únic per a cada formulari. Aquest prefix s'afegirà al nom dels camps que es generen per a l'HTML i d'aquesta manera en recuperar la informació Django sabrà quin camp correspon a cada formulari.
Hem de tenir cura d'assignar el prefixe tant a la part del GET com a la part de POST de la funció.
I això és tot, fàcil i net!
Observacions finals
A l'exemple de tres formularis amb el mateix nom he deixat codi de depuració, el import ipdb; ipdb.set_trace(), ho podeu llevar, levar sols la i del ipdb o bé instal·lar el ipdb, un depurador un poc més potent que el pdb de tota la vida i amb ressaltat de sintaxis.
Traducciones/Translations by apertium
0 comentaris, 0 trackbacks (URL) , Tags: Python Django
Netbeans IDE 6.5 edició Python
Escrit per Aaloy a 22 de November , 2008 a les 11:51 a.m.
Fa no res ha sortit l'edició Early Accés de Netbeans per Python.
L'IDE integra un editor amb autocompletat de codi, depuració integrada, integració de subversion i tot el que un normalment pot esperar d'un entorn avançat de programació.
En la documentació he vist que hi ha una branca per Django, però encara no he trobat com accedir-hi. Tanmateix no té molta importància ara per ara, ja que si és un bon editor per Python ho serà per editar projectes Django.
En l'execució damunt el PPC amb la màquina virtual IBM JDK6, funciona acceptablement bé, però he tingut que variar alguns dels paràmetres d'inici per a que l'entorn no s'arrossegàs tant. Res de nou, per Ecipse també ho vaig fer, i la impressió general és que llevat els casos en que a la màquina virtual li pega per consumir el 99% de les dues CPUs, l'entorn es comporta molt bé.
El paràmetres de tunejat:
-J-Xmx356m
-J-Xverify:none
-J-XX:CompileThreshold=100
-J-XX:PermSize=64m
-J-XX:MaxPermSize=96m
-J-Djava.net.preferIPv4Stack=true
Aspectes destacats
Entorn de feina: net, molt net. Aprofita molt l'espai de treball i es deixa un gran espai per a l'editor.
El completat de codi és del milloret que he vist. A més han aconseguit extreure la documentació i presentar-la de manera poc intrussiva i elegant.
Possibilitat de crear plantilles. És una de les coses que faig amb Vim i que molts editors tenen, però que no acaben de funcionar tan bé com les de Vim. Netbeans ho fa. He portat algunes de les plantilles de Vim sense problemes.
Navegació entre objectes pitjant amb la tecla Ctrl damunt l'objecte. És un de les coses que més m'agraden d'Eclipse per Java i Netbeans també ho ha incorporat a Python.
Neteja de imports innecessaris. Ens deixa el codi més net.
Tecles ràpides per gairebé totes les opcions. Per mi és important que allò que faig habitualment permeti ser executat sense tenir que llevar les mans del teclat.
Possibilitat de crear les nostres pròpies plantilles.
L'eina de gestió de diferències entre fitxers manté el l'acoloriment de sintaxi. Una gran millora respecte a tot el que hi ha. És una eina fantàstica.
Punts a millorar
L'autocompletat té un preu: l'inici del programa és lent ja que parseja tot el que troba. Si teniu l'opció del la finestra de tasques activada la cosa pot arribar a ser insuportable. Afortunadament si posam que sols ens mostri les tasques de l'editor actiu la cosa millora. Tot i així crec que falta algun tipus de configuració per poder definir per projecte les llibreries que s'utilitzaran i les que no per agilitar-ne el parseig i evitar que algunes vegades a l'autocompletat surtin cents d'opcions que no tenen res a veure amb el projecte.
La integració amb subversion és bona, però la interfície de selecció del que s'ha de pujar i del que no és molt farragosa, ja que s'ha de seleccionar element a element amb un desplegable.
Pareix haver hi un error quan es selecciona una plantilla, ja que sols te deixa triar les de Python i Others. Es posa tot allà dintre i ja està, però sorprèn.
Refactorització. La que té Eric4 amb Rope o la de pyDev mateix permeten més opcions.
Hi ha un bon grapat de coses que vull explorar com són les macros, la inserció de codi i mirar més a fons el comportament de les plantilles, crec que se n'hi pot treure molt de suc.
Com a conclusió dir que m'ha agradat molt aquest entorn. Potser estic molt condicionat a que ha funcionat força bé en el PPC, però en la meva opinió és un entorn fantàstic per al desenvolupament de Python en general i de projectes Django en particular.
El faré servir una temporada, sols hi duc dos dies, a veure si tot confirma ser tant bo com sembla i puc trobar com instal·lar la branca per Django. Això sí, el vim seguirà essent un dels meus principals entorns de desenvolupament: quan un ha de fer modificacions de codi ràpidament o treballar per ssh no hi ha res millor. Sempre serà o bé una eina principal o complementària. Sempre és bo tenir dos editors de capçalera.
Traducciones/Translations by apertium
3 comentaris, 0 trackbacks (URL) , Tags: Python Django
El django-admin no és per fer aplicacions d'usuari final
Escrit per Aaloy a 15 de November , 2008 a les 10:24 a.m.
Quan la gent dóna les seves primeres passes amb Django sovint queda enlluernada pel django-admin, una aplicació Django que ens permet definir un gestor de les nostres aplicacions, de manera que podem gestionar els usuaris, donar-hi permisos, gestionar les bases de dades, etc.
Hi ha que dir que l'aplicació està molt ben feta i es pot configurar moltíssim: indicar quins camps s'han de visualitzar, per quins camps es cercaran, fins i tot posar-hi javascript o modificar-ne l'aparença per a que la nostra aplicació faci el que nosaltres volem.
Aquesta facilitat però, té un perill, la gent té tendència a pensar que Django serveix per fer aplicacions web amb l'admin, que la seva aplicació ha de ser l'admin, així que recordem-ho:
El django-admin no és per fer aplicacions d'usuari final
Django admin fa molta màgia per dintre i aquesta màgia es basa en convencions que s'apliquen als models i a la definició del ModelAdmin, i aquestes convencions fan que per una part pugem tenir un gestor per a la nostra aplicació en quatre potades, però per altra, que si volem fer alguna cosa que surti del que està definit i establert ens durà molta feina.
La millor manera d'encarar-ho és tenir molt clar per a què serveix l'admin i per a què no:
Manteniment de les nostres taules de configuració i mestres? sí. Normalment ens anirà fantàstic per això i a meś ho podem tenir molt ràpidament. És ideal en les primeres etapes del desenvolupament, ja que es pot donar a l'usuari per a que carregui dades de prova i ja veu com pot funcionar l'aplicació.
Com a gestor web de la nostra base de dades? També anirà molt bé. Podem mapejar les taules i els camps que volem administrar, fer cerques, filtrar, editar i esborrar.
Com a eina d'usuari final per a que pugui configurar l'aplicació? Sí, si aquesta sols implica operacions senzilles, per exemple l'edició d'una plana web, afegir registres a una taula, etc. Podem definir quines taules pot tocar cada usuari i definir-ne perfils.
Com a eina d'usuari final on l'aplicació té molta lògica de negoci o bé un fluxe complex. NO. Millor començar un gestor pel nostre compte. Fer formularis CRUD amb Django és molt senzill i encara que pareix que dupliquem el que hi ha al django-admin, aquesta feina extra es veurà compensada a l'hora de definir els fluxs més complexos, ja que tindrem tota la potència de Python i Django al nostre abast i no estarem limitats a les convencions i funcionament del Django admin.
Les coses funcionen molt millor quan s'utilitzen per allò que estan pensades.
Traducciones/Translations by apertium
0 comentaris, 0 trackbacks (URL) , Tags: Python Django
Senyals a Django
Escrit per Aaloy a 03 de November , 2008 a les 8:35 p.m.
El mecanisme de senyalització de Django ens permet desacoblar les nostres aplicacions, ja que permet notificar d'accions que es produeixen a tot una sèrie de receptors o subscriptors d'aquestes senyals.
Podem distingir dos tipus de senyals a Django:
Les senyals que venen predefinides a bastiment i que podem trobar a la documentacio de Django. En aquest cas Django se n'encarrega de llançar l'avís de que una acció concreta s'ha produït i nosaltres com a programadors podem generar codi per tal de subscriure'ns a aquest tipus de missatges i disposar-ho tot per a que s'executi el codi que ha de respondre a aquestes accions.
Les senyals definides per l'usuari. Es un cas més general que l'anterior. Django ens permet definir les nostres pròpies senyals i enviar-les. En aquest cas doncs, podem definir tant la senyal com el tractament que se'n faci.
Escoltant senyals
Per a escoltar una senyal necessitam fer dues coses:
Definir la funció que rebrà la senyal amb el codi que s'ha d'executar.
Connectar la funció amb la senyal, és a dir subscriure's a la senyal.
Les funcions que haurem de definir han de ser de la forma:
def nom_funcio (sender, kwargs): "Aqui va la documentacio" # aqui el codi pass
Hem de tenir en compte que el nombre d'arguments que pot transmetre una senyal per definició pot canviar en qualsevol moment. Els arguments es passen per nom, i la nostra funció ha d'estar preparada per gestionar aquest possible canvi d'arguments.
Per a connectar la senyal amb la nostra funció importarem la senyal a la que ens vulguem subscriure i ens hi connectarem amb el mètode connect de la pròpia senyal.
Per exemple per connectar-nos a la senyal que s'emet quan una petició http ha acabat bastaria fer:
from django.core.signals import request_finished request_finished.connect(nom_funcio)
Les senyals tenen un paràmetre per defecte, el sender, que ens permet subscriure'ns sols a aquelles senyals que provenguin d'instàncies d'una classe determinada.
Definint les nostres senyals
Per a definir les nostres senyals hem fer dos passos:
Definir la senyal i els arguments que passarà
Allà on ens interessi enviar la senyal amb els arguments que hem definit
Pel primer pas basta importar django.dispatch i crear una instància de Signal amb els paràmetres que hem definit.
import django.dispatch pizza_done = django.dispatch.Signal(providing_args=["toppings", "size"])
Enviar la senyal és encara més senzill, basta cridar al mètode send amb els paràmetres que hem definit, sense oblidar-nos d'establir la variable sender amb l'objecte que ha enviat la senyal.
Suposem que volem que el sistema ens avisi automàticament cada cop que algú esborra un usuari de la base de dades.
Per a mostrar-ho farem ús del senyal pre_delete que es troba a django.db.models.signals. Per al registre crearem una taula a la BD que guardarà l'usuari que se va esborrar i el seu nom.
La senal pre_delete s'envia quan s'entra dins el mètode delete del model, com a arguments té el sender que és la classe que envia la senyal i instance que recull l'objecte que s'eliminarà de la base de dades.
Nosaltres ens volem registrar sols a les senyals que impliquin eliminar usuaris de l'aplicació, així que sols ens subscriurem a les senyals que provenguin de django.contrib.auth.models.User
El nostre model seria
#!/usr/bin/env python # -*- coding: UTF-8 -*- # Autor: aaloy from django.db import models from django.contrib.auth.models import User from django.db.models.signals import pre_delete class Eliminat(models.Model): user = models.CharField(max_length=30) created = models.DateTimeField(editable=False, auto_now_add=True, blank=False, db_index = True) def __unicode__(self): return self.user def registra(sender, **kwargs): instancia = kwargs['instance'] usuari = Eliminat(user=instancia.username) usuari.save() pre_delete.connect(registra, sender=User)
Per a veure com podem utilitzar les nostres pròpies senyals, crearem una senyal que registrarla la IP i el nom de la vista.
Per això crearem una nova classe, per exemple LogIp, que mantindrà el registre i connectarem el senyal amb la funció registre_ip
Hem de tenir en compte que es un exemple acadèmic, té poc sentit fe aquest tipus de coses, és a dir, definir una senyal dins el mateix model i enviant-la a la vista. Té molt més sentit que el senyal s'envii en resposta a una acció dins el propi model o a una acció particular de la vista i que una aplicació externa s'hi subscrigui.
class LogIp (models.Model): "Logs the ips and calling functions" name = models.CharField(max_length= 200) ip = models.IPAddressField() created = models.DateTimeField(editable=False, auto_now_add=True, blank=False, db_index = True) in_view = django.dispatch.Signal(providing_args=['ip', 'name']) def registra_ip(sender, **kwargs): ip = kwargs['ip'] name = kwargs['name'] log = LogIp(name= name, ip=ip) log.save() in_view.connect(registra_ip)
in_view és la nostra senyal, com a paràmetres hem posat ip i name, això vol dir que la funció que es registri com a escoltadora els pot fer servir i ha de ser capaç de admetre'ls. Per una altra banda també ens diu que allà on enviem la senyal amb el send haurem de proporcionar també aquests paràmetres.
Si llançam el senyal a la vista quedaria com
def list(request): "Lists the deleted users" deleted = Eliminat.objects.all() in_view.send(sender=chivato.views.list, ip=request.META['REMOTE_ADDR'], name=request.get_full_path()) return render_to_response('list.html', {'deleted':deleted})
Amb això cada cop que algú des de el seu navegador accedeixi a la funció quedarà registrada la url d'accés i la seva ip.
Tot el codi font amb un projecte complet llest per funcionar, sols heu de canviar las rutes al properties.py a appfuse o directament
svn checkout http://appfusedjango.googlecode.com/svn/trunk/signals
Traducciones/Translations by apertium
0 comentaris, 0 trackbacks (URL) , Tags: Python Django
Avaluació pererosa a Python: un exemple amb Django
Escrit per Aaloy a 04 de September , 2008 a les 3:16 p.m.
Aprofitant que tenc vacances (eps! una setmana i ja s'acaben) aprofitaré per comentar un codi que serveix tant per entendre un poc més Python com per fer feina amb Django. La idea és del blog themorge.org
class PersonForm(ModelForm): "Simple form for the Person model" class Meta: model = Person
def edit(request,id=None): """Edits the Person model""" formulario = PersonForm(request.POST or None, instance = id and Person.objects.get(id = id) ) if request.method == 'POST': if formulario.is_valid(): persona = formulario.save() return HttpResponseRedirect('/fitxa/guardada/%s/'%persona.id) return render_to_response('edit.html', {'formulario': formulario})
Això representa un formulari d'edició amb Django (versió trunk). Tenim definit als models de l'aplicació una classe Person i el que volem és crear un formulari d'edició a partir d'aquest model.
A la classe PersonForm podem veure com hem creat el formulari a partir de la definició del model. Podem personalitzar el formulari afegint-hi validacions específiques, canviar la manera que s'edita etc, però si volem una cosa bruta i ràpida amb quatre línies en sortim (sí, la documentació sí és necessària).
Definim ara la part d'edició en sí, en el meu cas l'he anomenada edit, i fa referència a una plantilla edit.html, que bàsicament conté
<form action="." method="post">
<table>
{{formulario}}
</table>
<button name="submit" value="submit" type="submit">Send</button>
<button name="reset" value="reset" type="reset">Reset</button>
</form>
L'embolcall de l'html ho deixo com a exercici pel lector.
Anem a veure què passa quan a través de la url anam a parar a l'edit. Tenim dos casos possibles inicialment, que vulguem afegir un mou registre o que en volguem modificar-ne un ja existent. En el primer cas se'ns ha de mostrar un formulari en blanc, i en el segon un formulari amb les dades omplertes del l'objecte Persona agafat de la base de dades per l'ORM de Django.
Els urls que jo he definit són
(r'^edit/(?P\d+)/$','edit'),
(r'^add/$','edit'),
Com veim hi ha dues urls que apunten al mètode edit, la primera agafa l'id com a paràmetre, per la qual coses les urls serien de la forma /edit/10/ i la segona no agafa cap paràmetre i per tant queda com /add/. De fet podríem no haver fet la distinció entre edit i add, però si la feim el codi html ens quedarà molt més bo de llegir, més semàntic.
Estudiem el cas add. Quan entrem al mètode edit, com que no li passam l'id agafarà el valor per defecte, a saber None ja que així ho me definit al mètode, i ara comença la part interessant...
Definim un objecte de tipus PersonForm, la inicialització de la classe agafa dos paràmetres, el primer són els valors inicials del formulari i el segon és la instància de la classe que volem modificar.
Quan seleccionam editar un registre o afegir-ne un de nou, el que feim és un GET i quan guardam el registre hem definit al nostre codi HTML que el que farem és un POST.
Així doncs, ara hem fet un GET. L'operador or en Python funciona de la següent manera: x or y primer avalua x, si x és vertader retorna x i si no avalua y i retorna el resultat. Fixem-nos que deim que és una avaluació pererosa perquè si x dóna vertader, y mai s'arribarà a avaluar. Com que hem fet un GET la primera expressió és False i n'avaluarà la segona, amb la qual cosa obtenim un preciós None com a resultat de la inicialització de valors.
Anem ara a la segon part, la que ens defineix la instància, en aquest cas tenim un and. L'and funciona de la següent manera: x and y primer avalua x si x és fals llavors retorna el seu valor i si no s'avalua y i es retorna el seu valor. En el cas doncs de que x sigui fals ja no s'avaluarà y.
En el nostre cas hem passat el valor id=None llavors ja no itentarà ni crear una instància de l'objecte, senzillament passarà None com a valor de la instància, és a dir, res, i això indicarà a Django que es tracta d'un registre nou.
Per tant, quan hem entrat via add que que obtenim és un formulari buid. Com que hem entrat per GET l'if no s'avaluarà i Django ens presentarà una plana html amb un formulari buid.
Suposem ara que feim un /edit/2/, en aquest cas també haurem passat per GET però el valor de l'id serà 2.
formulario llavors tendrà None com a valors inicials per la mateixa raó que abans, però ara el primer terme de l'and és vertader i se n'avalua el segon, això fa que obtinguem com a valor per la instància l'objecte Person que té id igual a 2. En aquest cas tendrem un formulari ple amb els valors del registre i Django ens presentarà un formulari amb les dades de l'objecte com a valors i lligarà la instància al formulari (això significa que podrem fer formulario.save() per guardar el registre).
Molt bé, ja tenim un formulari, ple o buid, tant fa, ara el que feim és afegir o modificar dades i pitjam damunt l'opció de guardar. Això farà un post i cridarà a la mateixa url (el punt a l'action així ens ho garanteix), això significa que per a la inserció l'id serà None i per afegir l'id serà un número, 2 en el nostre cas.
Ara entram per POST, la qual cosa fa que la primera part de l'or sigui vertadera, amb la qual cosa el formulari tindrà com a valors inicials el que hagem posat, perquè això? dons per si no hem passat la validació del formulari, si anam per POST i el formulari no passa la validació, anirem novament a la mateixa plantilla, però ara tindrem com a valors del formulari el que hem passat, és a dir, si no passam la validació tornarem a tenir un formulari ple amb les dades que hem escrit.
És important fixar-se que les dades inicials a diferència de les dades que passem per la instància no necessàriament han de ser vàlides, no passen pels mecanismes de validació de Django, i això és el que ens permet recuperar tot el que hem escrit encara que estigui malament segons la validació.
Seguim, si hem entrat per edit l'id serà None i per tant ja no es crea cap objecte Person, si hem entrat per add l'id tindrà valor i s'avaluarà el segon terme de l'and amb la qual cosa haurem recuperat el registre de tipus Person que té id igual al nombre passat i l'haurem lligat al formulari.
Com que és un POST i passam la validació el que se fa és guardar el registre (si estava inicialitzat farà un update, sinó farà un insert a la base de dades i ens retornarà la instància de la classe Person que s'ha guardat.
Ara, que ja s'ha guardat sols ens queda fer un redirect de manera que evitem problemes amb dobles pitjades i refrescs varis. En el meu cas vaig a una url que em presentarà la fitxa que tot just acabam de crear/modificar.
Esper que aquest exemple us hagi agradat tant com a mi quan ho vaig veure. És tot un exemple de l'expressivitat de Python i del ben pensat que està Django. Però el més important de tot d'aquesta història és que sempre es pot aprendre alguna cosa i que per aprendre és important dedicar temps a llegir codi dels altres.
Traducciones/Translations by apertium
1 comentari, 0 trackbacks (URL) , Tags: Python Django
Django 1.0
Escrit per Aaloy a 04 de September , 2008 a les 8:48 a.m.
La versió 1.0 de Django fa sis hores més o menys que ha sortit del forn, es pot veure ja el comunicat oficial a la plana web del projecte.
Els números són impressionants, de de la darrera versió estable s'han fet 4.000 commits, s'han resolt 2.000 errors i s'han afegit o llevat prop de 350.000 línies de codi. I el que trob un factor diferenciador de Django: s'han afegit 40.000 línies noves de documentació, pocs bastiments de codi obert dediquen tants esforços a la documentació.
Se m'ha acudit passar-li el sloccount a trunk del projecte
| SLOC | Directory | SLOC-by-Language (Sorted) |
|---|---|---|
| 46235 | django | python=46235 |
| 24683 | tests | python=24683 |
| 341 | docs | python=341 |
| 80 | examples | python=80 |
| 56 | top_dir | python=56 |
| 16 | scripts | sh=16 |
| 0 | extras | (none) |
| 0 | patches | (none) |
Totals grouped by language (dominant language first):
| python: | 71395 (99.98%) |
| sh: | 16 (0.02%) |
Total Physical Source Lines of Code (SLOC) = 71,411
Development Effort Estimate, Person-Years (Person-Months) = 17.68 (212.16) (Basic COCOMO model, Person-Months = 2.4 * (KSLOC1.05))
Schedule Estimate, Years (Months) = 1.60 (19.15)
(Basic COCOMO model, Months = 2.5 * (person-months0.38)) Estimated Average Number of Developers (Effort/Schedule) = 11.08
Total Estimated Cost to Develop = $ 2,388,334
Si a més contam css, javascript i l'html amb una altra eina
./cloc-1.04.pl --exclude-dir=.cvs,.svn --no3 .
http://cloc.sourceforge.net v 1.04 T=11.0 s (80.8 files/s, 10177.7 lines/s)
-------------------------------------------------------------------------------
Language files blank comment code
-------------------------------------------------------------------------------
Python 739 15302 8184 83136
HTML 100 364 15 1725
Javascript 15 107 269 1547
CSS 15 83 82 505
XML 10 49 8 431
make 1 12 4 54
SQL 8 1 9 40
Bourne Shell 1 4 7 17
-------------------------------------------------------------------------------
SUM: 889 15922 8578 87455
-------------------------------------------------------------------------------
Hem d'agraïr la feina feta als desenvolupadors principals de Django i a la comunitat que s'ha format al voltant per tot l'esforç i la bona feina feta.
Django s'ha convertit per mi en un factor diferenciador en el que fa a productivitat i velocitat de desenvolupament, cada nova millora ha estat sempre orientada fer les coses més naturals per al programador, més pythoniques, molt en la línia del llenguatge de programació en el qual està fet Django, el Python.
Ara sols resta anar adaptant el codi per a incorporar les millores i les noves maneres de fer feina. Els canvis dels darrers mesos han estat tan grans i tan ràpids que sovint costava anar seguint el fil, però el resultat és un bastiment com per a sentir-se orgullosos.
Traducciones/Translations by apertium
1 comentari, 0 trackbacks (URL) , Tags: Python Django
AppfuseDjango actualitzat a la versió de Django
Escrit per Aaloy a 16 de August , 2008 a les 11 a.m.
Avui he actualitzat AppfuseDjango per a que funcioni amb la darrera versió de Django.
Darrerament hi ha molts canvis a Django, ja atracant-se a la versió 1.0, i alguns d'ells són incompatibles amb les versions anteriors.
La idea de l'aplicació és que servesqui de punt de partida per altres aplicacions i com a codi de mostra per a la gent que està començant amb Django. El tutorial de Django està força bé per començar, però després, quan has de fer una aplicació amb manteniments, internacionalització, etc. es queda un poco curt. No hi ha res a dir, l'objectiu del tutorial és ensenyar i el d'una aplicació com AppfuseDjango l'objectiu és ser un punt de partida.
De fet ja fa estona que no faig servir la utilitat de Django per a crear projectes i aplicacions, el que faig és fer un export d'AppfuseDjango i començar a partir d'allà.
Pels qui us demaneu què s'hi pot trobar:
- Un manteniment CRUD: llistat, alta, baixa i modificació d'un registre.
- Exemple de paginació
- Mostra com internacionalitzar una aplicació.
- Un exemples de com fer anar l'Ajax i json amb extjs i jqgrid
Però el més important crec que no és tant el codi que es mostra sinó que es vegi l'organització d'un projecte.
Veureu que s'ha tret la configuració del settings.py cap a un properties.py, que no trobareu al codi, sinó que hi ha un properties.py.template. Això es fa per deixar l'aplicació preparada per l'entorn de producció i per a ser utilitzada per molts desenvolupadors. Fent-ho així, poden tenir distintes configuracions per a cada un dels desenvolupadors i com que són pròpies de cada un estan fora del control de versions de l'aplicació. Tanmateix sols és cosa de copiar el properties.py.template i modificar-ho per a adaptar-ho a les nostres necessitats (directori, caché, etc.).
Una altra cosa que he anat posant són algunes optimitzacions i hacks útils trobases pels fòrums o de collita pròpia. Entre elles una que ens permet tenir una caché de plana que depèn de l'idioma de visualització, és a dir, es genera la clau de la caché amb l'idioma de l'usuari.
Com a darrera incorporació, un mètode molt elegant d'editar i inserir registres que fa ús de l'avaluació pererosa en Python, trobat a themorgue.org, el codi ha quedat molt net, potser una mica menys entenidor que abans, però l'estalvi en línies de codi ho compensa.
Els plans futurs crear branques amb distints tipus de projectes per assolir l'objectiu de tenir el punt de partida bàsic per a cada tipus d'aplicació: html pura, html amb ajax, RIA, etc. i així sols tenir que baixar l'estrictament necessari en cada cas.
Traducciones/Translations by apertium
0 comentaris, 0 trackbacks (URL) , Tags: Python Django
A voltes amb els trackbacks
Escrit per Aaloy a 22 de July , 2008 a les 8:01 p.m.
Benvolguts amics, coneguts i saludats,
Aprofitant la calor, la benentesa, i la moguda de Django estic aprofitant per retocar el sistema de trackbacks del blog.
Estic abusant un poc de la confiança i fent proves a altres blogs a més del meu, per veure si hi ha problemes.
Si veis alguna cosa rara, trackbacks que venen de trespams amb poc sentit o antics, perdonau-me, estic abusant de la vostra confiança i amabilitat. Procuraré no fer massa destrossa, i no cal dir-ho, esborrau el que convingui.
Traducciones/Translations by apertium
0 comentaris, 0 trackbacks (URL) , Tags: Python Django
Moguda a ca'n Django
Escrit per Aaloy a 20 de July , 2008 a les 9:49 p.m.
Django camina amb passes fermes cap a la versió 1.0, impulsat per la gent que vol una versió estable amb totes les millores actuals. S'ha definit tot una sèrie d'Sprints per a impulsar el desvenvolupament, anar integrant les millores ja provades i tancar tickets d'error.
En el procés hi ha tot un conjunt de decisions que s'han de prendre i que trenquen amb la versió estable anterior. Els canvis a Django són força graduals, i gairebé no trenquen mai les coses d'una manera bèstia, és veritat, les aplicacions deixen de funcionar, però és relativament senzill arreglar les coses si un ha anat seguint la branca de desenvolupament.
Un d'aquests canvis que "ha trencat coses" ha estat la integració del nou mòdul d'administració, que té força incompatibilitats, ja que refà tot el que era la definició de l'administrador. La gent de Django manté una llista d'incompatibilitats que fan que la feina, encara que se tengui que fer, sigui senzilla.
He actualitzat dos dels projectes que tenc a Google code, aquest blog per exemple. I ara queda anar migrant totes les aplicacions que tenim on-line. El trunk és massa bo i estable com per a no fer-ho servir, però de tant en tant dóna aquestes feinetes.
Traducciones/Translations by apertium
0 comentaris, 0 trackbacks (URL) , Tags: Django
La meva experiència amb Django
Escrit per Aaloy a 22 de June , 2008 a les 12:03 p.m.
En Maties Bonet ens va escriure un e-mail a mi i a Guillem demanant-nos per la nostra experiència en l'ús de Django.
Amic Maties, bona cosa has demanat! Pepara't perquè aquest apunt pot ser llarg :)
La meva relació seriosa amb Django ja té més de dos anys, és difícil estimar la data, però si en tingués que donar una seria la del 3 d'agost del 2006, data del primer commit del major projecte que hem fet amb Django fins ara,amb actualment més de 12.000 línies de codi
Aquest projecte inicialment estava desenvolupat en PHP, però necessitàvem més funcionalitat i essent les nostres filies més tendents cap al Python que cap a altra cosa, començarem a investigar els bastiments que començavent a surgir. Cercàvem quelcom que permetés una bona escalabilitat, facilitat de desenvolpament i una separació molt clara entre codi i capa de presentació. A més una de les restriccions inicials era que el servidor que tenia que dur tot això no havia de ser massa potent, un host virtual baratet hauria de ser suficient per començar.
Amb Juan avaluarem un grapat d'opcions. Anàvem mirant frameworks i en discutíem els avantatges i inconvenients. Férem una ullada a Pylons, a TurboGears i alguns altres, fent algun prototip i discutint-ne els resultats.
TurboGears estava força bé, i Pylons representava gairebé l'estat de l'art en quant a integració de componentes, però Django tenia una avantatja per a nosaltres fonamental: la seva integració entre els distints components (integració que a més no compromet a res), una documentació fantàstica i el ser un bastiment que s'havia fet servir en projectes grans, en projectes que estaven funcionant. Es podria dir que no era sols una idea sinó que el funcionament del framework era una realitat.
Així doncs ens decidirem per Django. Mesos després Guido Van Rossum com el framework que li agradava més. Un any i mig després veuríem Django com un bastiment suportat per Google. Això vol dir que tenim bon ull per les tecnologies? Segurament, però vist en perspectiva l'elecció de qualsevol dels altres dos bastiments també ens hauria anat força bé.
Django, però té aquell quelcom que et fa sentir content i satisfet amb la programació que fas. És entenidor i abastable i quan t'enfrontes a un problema de la vida real trobes que hi ha una solució en Django, per una raó molt senzilla: el bastiment es va crear per resoldre problemes de la vida real, com una resposta a la necessitat que tenien els seus creadors de tenir un bastiment que els proporcionàs una gran rapidesa en el desenvolupament i al mateix temps una gran escalabilitat.
Una vegada vàrem veure la potència del bastiment, junt amb la potència de Python i ho compararem amb el que teníem i feiem en Java, el pas següent va ser lògic: utilitzar el que sabíem en la feina diària per a l'empresa per la qual treballàvem, una multinacional del turisme. Sense deixar Java del tot, ara podíem incorporar una nova eina que ens permetria poder desenvolupar webs a la velocitat que li agradava al negoci.
Posar Django i Python a una empresa molt tradicional no és senzill, "aquest tipus de la web sempre fent coses rares!" Però l'evidència s'imposa i amb un poc de ma ampla del nostre director d'informàtica anterior començarem a fer els nostres primers projectes amb Django per a l'empresa. És una tasca que avui en dia encara costa. Quant més consultors externs té l'empresa més difícil és aquesta tasca. Aquests suposats experts no tenen idea de què és Django i de les seves possibilitats, i tampoc els convé, ja que després de la consultoria hi sol haver un desenvolupament i com que el desenvolupament és més ràpid i ells cobren per hores, doncs que no convé gaire. Però això és una altra història i també serà un altra apunt.
Actualment doncs, feim/faig servir Django i Python en quatre grans tipus de projectes:
- projectes on no s'ataca a la base de dades "legacy" de l'empresa, sinó que s'han desenvolupat des de zero.
- projectes on hi ha una gran part de continguts i a més lògica de negoci senzilla.
- projectes que consumeixen web services en SOAP que estan a la seva vegada desenvolupats en Java.
- el nostres projectes particulars, com aquest blog.
Els resultats són molt bons, un exemple: fa dues setmanes ens telefonaren del dimecres per a un projecte crític, s'havia de crear una web completa per a un client que havia d'estar llesta el divendres al matí de la mateixa setmana. El dimecres horabaixa en tendríem més detalls.
L'horabaixa ens defineixen un poc millor el projecte: bàsicament contingut que se'ns aniria passant en format word i que segurament aniria sofrint modificacions damunt la marxa.
Tot just penjar el telèfon ens posarem en marxa. La gent de sistemes creà el repositori subversion pel projecte (no importa la pressa que tenguis, sempre, sempre un control de versions) i inicià la configuració del nou domini.
Una hora després ja teníem el domini intern funcionant i la primera versió del codi dins el subversions. Havíem reaprofitat la funcionalitat que teníem per a la gestió de continguts i ara sols era cosa de crear el disseny, passar-ho a plantilles i posar el contingut. El que ens feia més por era no tenir els DNS replicats per l'hora d'entrega.
El dijous al matí el disseny ja estava llest i es comença a maquetar i pujar continguts. Es crearen algunes plantilles per a fer que les opcions de menú canviessin dinàmicament i s'anava pujant tot al servidor de producció mitjançant un update del subversions. El temps de pujada d'una nova versió era aproximadament de 30 segons, versió que ja pujava testejada gràcies a que amb Django pots anar provant l'aplicació amb el seu servidor integrat (i amb recàrrega automàtica).
Ens sobraren un parell d'hores del temps limit. Total del projecte 35 hores-home. Entregable: aplicació amb subdomini propi, tipus portal de continguts, multi-idioma, amb menús desplegables, i la maquetació a partir de documents word de l'equivalent a una trentena de planes web amb una mescla de text, fotografies i descàrrega d'arxius. Rendiment de l'aplicació: generació d'una plana web en 1,2 segons sense caché.
Si ho haguéssim tingut que fer en Java encara estaríem muntant el CMS o pujant els continguts. Amb les plantilles de Django i la possibilitat d'herència que tenen, poguérem crear el nostre lloc web amb molt poc temps i passar els continguts a HTML de manera molt més ràpida que l'equivalent a crear el disseny en un CMS clàssic de PHP o Java (ja no en parlem de fer el mateix amb Java i sense CMS).
El millor de tot és que encara que no sabíem que se'ns demanaria estàvem raonablement segurs de que si no era res molt complexe es podria fer, ja que el bastiment no ens fermava (com sovint fan els CMS més habituals) sinó tot el contrari.
La meva experiència amb Django? Fantàstica i demostrable. Fins al punt que quan veig el que puc fer en aquest entorn em fa molta peresa tornar cap a Java, els temps d'espera tot i que desenvolupam en local resulten molests, les posades en producció s'eternitzen. I això que gràcies al nostres administradors de sistemes ho tenim tot que va com una seda a l'entorn J2EE i les posades en producció són ràpides, però quan ho compares amb un pocs segons tot resulta lent.
Consider Django com un avantatge competitiu: ens permet fer desenvolpaments molt ràpidament i a més sabem que escalaran bé. Com que no estam lligats a cap bastiment de javascript concret podem incorporar el que necessitem segons el projecte: jQuery, extjs, res... i la separació que es fa en capes de l'aplicació també es pot fer a l'hora de desenvolupar i permet treballar en paral·lel a la gent de sistemes, disseny i programació.
I així doncs, quan voleu que posem Django a la vostra empresa?
Traducciones/Translations by apertium
4 comentaris, 0 trackbacks (URL) , Tags: Python Django
Millores al blog
Escrit per Aaloy a 08 de June , 2008 a les 11:30 a.m.
De tant en tant faig feina al blog, no per escriure-hi sinó per afegir-hi noves funcionalitats. Encara hi ha moltes coses que m'agradaria posar-hi i millorar, però a poc a poc esper anar arribant-hi.
A la darrera actualització he fet algunes millores que recomanaven al Google Webmaster Tools com la d'afegir descripcions úniques per apunt. Django a les seves plantilles té el filtre truncatewords_html que m'ha anat fantàstic per això.
Una de les millores que volia fer també era la d'amagar un poc tota la llista de mesos que hi ha. Aquest blog té apunts des del 2004 i la llista començava a ser molt llarga. Ara veureu que sols apareixen els anys (sempre que tingueu el javascript activat clar) i que en pitjar damunt ells es despleguen els mesos. Fer això ha estat entretingut perquè ha implicat jugar amb dos tags més de Django, el for per obtenir si estava a la primera posició del bucle o a la darrera, per tal de poder tancar els divs, i el tag ifchanged que ens permet saber si una variable (en el meu cas l'any) ha canviat o no respecte al seu valor anterior.
Amb aquestes eines ha estat possible muntar l'estructura necessària per a utilitzar un el Animated Collapsible DIV, una petita utilitat per jquery que permet ocultar i mostrar divs a voluntat, agrupant-los per categories si convé.
Ara la plana principal al meu entendre queda un poc més neta i amb prou espai a la columna lateral esquerra per anar afegint més coses.
Traducciones/Translations by apertium
0 comentaris, 0 trackbacks (URL) , Tags: Informàtica Python Django
Capa de negoci a Django
Escrit per Aaloy a 28 de May , 2008 a les 11:55 p.m.
Del post anterior em quedava el tema de tractar el tema de la capa de negoci en els llenguatges dinàmics. Com en el cas anterior faré servir Django com a exemple i deix al lector la feina d'extrapolar cap el seu llenguatge dinàmic+bastiment preferit.
En Domingo diu que els llenguatges dinàmics mostren molta de la seva potència a la capa de presentació, que és allà on en treuen més profit. Això és veritat però es queda curt. És a dir, quan un usuari demana un canvi, el més habitual és que aquest s'acabi reflexant en la capa de presentació,però això no vol dir que no se canvii la capa de negoci. El fet però és que anar des de la capa de persistència, passant per negoci i mostrar el resultat al navegador, té un cicle de temps més curt en el llenguatge dinàmic, interpretat, que en el compilat, ja que sol ser molt més curt fer els canvis (gearing factor i totes aquestes herbes) i necessitea un temps més curt de desplegament, però no ens enganem, és tot el procés que és curt, si no afecta a capa de presentació el que passarà és que el desenvolupament serà encara més ràpid.
Sovint amb les aplicacions del tipus crea el teu blog en 10 minuts en Django, Rail o qualsevol altra, es dóna una idea equivocada del que se pot fer, donant la imatge de que aquestes aplicacions van molt bé per fer webs que depenen d'un conjunt de taules senzilles i que tenen una lògica de negoci poc complexa.
Bé, supòs que podríem demanar-li a Ricardo, que és un exemple que tenim ben aprop,de com s'ho fa per calcular el karma de Meneame o per saber si una notícia ha de sortir en portada o quan ha de deixar de ser-hi. Tot això es lògica de negoci, està feta en un llenguatge interpretat, el PHP.
En el cas de Django no oblidem que el que tenim per davall és Python i que l'ORM que ens proporciona Django ho podem fer servir o no, substituir-ho per un altre, fer les cridades directament a BD o senzillament, utilitzar-ho com a part del nostre model de negoci i posar-i les regles que ens convenguin.
Segons la nostra aplicació podem anar afegint custom managers que ens permetran definir regles damunt les dades que volem obtenir, mètodes de classe per definir les funcionalitats que vagin damunt tots els elements de la taula, i missatges que ens ajudin a manipular la informació i les seves relacions.
Això ho podem fer directament des de la classe de l'ORM, el model, o bé, recordem que tenim Python a la nostra disposició, crear un nou paquet, crear una classes o un conjunt de classes i utilitzar el model per aplicar les regles de negoci que vulguem quan aquestes s'hagin de convertir en registres de la base de dades o la seva informació hagi de venir de la base de dades. Per cert, i abans que algún ho demani, sí hi ha transaccions!
Això vol dir que podem fer servir els llenguatges dinàmics per a modelar la nostra capa de negoci, doncs sí, ho podem fer. Això vol dir que la nostra aplicació ha d'estar escrita en un llenguatge dinàmic sempre? No, depèn de l'aplicació. Potser per una aplicació concreta amb un requeriments concrets tenir un contenidor EJB amb transaccions distribuïdes serà el que necessitam, el que vull dir és que no podem descartar fer l'aplicació en un llenguatge dinàmic sols perquè ens hem quedat amb la idea de que sols va bé perquè el seu ORM te crea molt fàcilment les taules i els CRUDs.
De fet la part d'administració de Django va molt bé com a eina administrativa, però no és una eina d'usuari final. Com a desenvolupador te va molt bé perquè pots fer cerques ràpidament, començar a omplir les dades del manteniments mestres i anar directament a les butzes de les dades, però no hem de confondre això ni amb l'aplicació final i amb que és tot el que se pot fer amb els llenguatges dinàmics.
I encara hi ha un punt que no hem tractat, el tema de la integració dels llenguatges dinàmics amb els llenguatges compilats (jpython, jruby, groovy, etc). Segons la nostra aplicació i els nostres usuaris, tenir un llenguatge interpretat, fins i tot creat per al domini de la nostra aplicació pot ser una opció molt interessant. Permeteu-me una batalleta: fa un grapat d'anys vaig desenvolupar el programa de gestió d'excursions de l'empresa on feia feina (Delphi + IBObjects + Firebird), una aplicació client servidor clàssica amb un grapat de procediments amagatzemats quan feia falta. La cosa és que hi havia excursions que tenien ofertes que el proveïdor donava i que s'havien de cotitzar, ofertes del tipus: "si es reserva una excursió per dos adults i un nin, el nin tendrà un 25% de descompte damunt la tarifa" o "el tercer adult es gratis i els nins no paguen" etc. Això ho podria haver desenvolupat amb un bon conjunt de camps de la base de dades i tenir previstes les condicions, però la solució hagués estat visualment atractiva però mala de programar i molt propensa a errors. Per contra vaig optar per a modelar-ho con si fos una fórmula de una fulla de càlcul, a la que els meus usuaris estaven acostumant on es podia jugar amb el preu per data, amb sumes, restes, if i tant per cents i parèntesi, un mini intèrpret pensat per a tractar amb fórmules matemàtiques. Les condicions així posades eren flexibles i amb unes possibilitats pràcticament il·limitades respecte al que haurien estat si ho hagués fet amb una "interfície amigable". En aquest cas, integrar un intèrpret a l'aplicació va ser la millor solució.
Com no me cansaré de repetir, no es tracta de dinàmics/interpretats respecte d'estàtics/compilats. Es tracta de perdre la por i els perjudicis i poder triar en cada cas aquella tecnologia que millor s'adapti al problema a resoldre de manera que aquest es pugui resoldre amb el menor cost actual i futur per al nostre client, en alguns casos aquest llenguatge serà C, C++, C#, Java o el que sigui, però en altres, en molts altres un llenguatge dinàmic serà la millor opció per als nostres clients.
Traducciones/Translations by apertium
5 comentaris, 0 trackbacks (URL) , Tags: Gestió de projectes Python Django
D'errors i línies de codi
Escrit per Aaloy a 26 de May , 2008 a les 9:30 p.m.
En Domingo al seu blog fa una referència al meu apunt damunt llenguatges dinàmics i un bon grapat de bones reflexions.
Això de no creure's el que dic és una postura molt sana, sobretot perquè en la redacció d'un apunt me puc deixar detall i dades que són interessants. Una postura crítica ajuda a reflexionar i a completar les frases que d'altra banda s'haurien deixat com a dogmes de fe. Jo sóc del mateix tarannà, hi ha coses que me crec i coses que a poc que vegi indicis de contradiccions, doncs cerc més informació o deman explicacions. Això, he de dir també, m'ha causat força problemes en el món empresarial, on sovint el "no pensis" és una qualitat que ajuda a progressar.
Bé, però no me'n vull anar per les bardisses, o sí, ja que hi sóm, aprofitaré per dir perquè no faig servir el Twitter: no hi ha espai abastament :)
Afirmació: El nombre de línies de codi que escriu un programador al dia és una constant. Això és un efecte estadístic. Fa temps es parlava de que un programador escriu cinc línies de codi sense errors al dia. Està clar que és una dada estadística i fins i tot controvertida, ja que que és molt complexa definir què és i que no és una línia de codi i de que les línies de codi no s'han de fer servir per mesurar el rendiment. Tot i això podem trobar referències capítol 5 de Software estimation de McConnell on a la Taula 5.1 torbam la relació entre les línies d'un projecte i les línies de codi per programador i any. Això no vol dir que tots els programadors escriguin les mateixes línies de codi (de fet la productivitat entre programador pot arribar a un factor 10 -Peopleware-, però sí que en mitja i per a una organització i projecte, el nombre de línies de codi que s'escriu en promig és constant. A Sofware Mesurement and Estimation diu "Studies have shown that a proficient programmer can programm aproximately the same number of debugged lines of code per day regarless of the language". D'aquí que gent com QSM o David Consulting Group facin comparatives entre llenguatges per a comparar l'expressivitat de cada llenguatge. Per exemple C té un gearing factor de 128 i Java de 53. Això vol dir que una funció que en C necessita 128 línies de codi en Java en necessitarà en promig 53. Si un programador experimentat escriu, essent optimistes 10 línies de codi depurat al dia, llavors necessitarà 13 dies en C i 6 en Java (nombres redons).
D'aquí llavors que els llenguatges dinàmics, al necessitar menys línies de codi per fer el mateix poden completar els projectes en un temps més curt. Com que el temps significa doblers, implica que els projectes surten més barats.
Afirmació: el número de errores directamente proporcional al número de líneas que se escriben Aquí també entram en temes estadístics. De fet es parla de nombre d'errors d'un programa per mils de línies de codi. De la mateixa manera que en el cas de les línies de codi que escriu un programador, hi ha moltes diferències, però Casper Jones a "Program Quality and programmer Poductiviy (1997)" va recopilar algunes dades, que també cita en McConnell. També Reifer en va fer estudis, per exemple per la part Web estudià 65 projectes, i trobà que el ratio és de 6 errors per KESLOC (KESLOC Kilo (Thousand) Equivalent Source Lines of Code). Aquest nombre depèn força del tipus del projecte i de l'organització, però és estadísticament significatiu. Per tant, si donam com a bo el que els llenguatges dinàmics necessiten menys línies de codi per expressar el mateix que els llenguatges compilats tradicionals (Java, C, C+, ...) tendrem que el nombre d'errors en valor absolut també serà menor.
He trobat també una comparativa entre el nombre d'erros per Java i C++ a un paper de Geoffrey Phipps anomenat Comparing Observed Bug and Productiviy Rates for Java and C++, me l'he d'acabar de llegir, però conclou que amb un 95% de confiança C++ té 9,7 vegades més defectes per KLOC que Java i que a més els defectes en Java són més fàcils de corregir. D'aquí a fer una extrapolació cap a la banda dels llenguatges dinàmics hi ha sols un pas, sols falta que algú s'animi a fer l'estudi.
En Domingo també demana l'opinió damunt els llenguatges dinàmics per la capa de negoci, però això crec que dóna per un apunt per ell sol, així que ho deixaré per la propera vegada, però promet, amenaç, amb tractar-ho.
Traducciones/Translations by apertium
0 comentaris, 0 trackbacks (URL) , Tags: Gestió de projectes Informàtica Python Django
Llenguatges dinàmics
Escrit per Aaloy a 25 de May , 2008 a les 11:18 a.m.
Al blog de Ricardo he esta llegint l'apunt anomenat "Lenguajes dinámicos, programadores y FUD" així com els seus comentaris. Tot i que hi he deixat allà un comentari amb la meva opinió, crec que com a programador en llenguatges dinàmics i tradicionals he de dir la meva.
El primer de tot que voldria fer és evitar el FUD damunt els llenguatges dinàmics, per una raó molt senzilla: la realitat és molt cruel i estam cansats de veure i de llegir damunt projectes web realitzats en Python, Ruby o PHP que estan triomfant i que no desapareixen o exploten per mor de no tenir un llenguatge compilat al darrera. Per tant, la primera cosa que hem de tenir clara és que la teoria pot estar molt bé, però la realitat ens està dient dia a dia que és possible escriure i mantenir programes en llenguatges dinàmics. A un li poden agradar més o menys aquests tipus de llenguatges, però el que no es pot fer és negar la realitat.
També m'agradaria focalitzar l'àmbit de discussió: estam parlant de programes web. Per programes d'escriptori no tenc dades abastament, potser perquè els llenguatges dinàmics brillen en el desenvolupament web més que en la part d'escriptori, llevat de que considerem el Visual Basic com a llenguatge dinàmic, clar.
La cosa està en que amb les màquines actuals els llenguatges dinàmics son ja prou ràpids com per ser competitius amb els llenguates compilats. Això no vol dir que siguin més ràpids, sinó que són prou ràpids per fer la feina i que la diferència de velocitat que hi hagi no sigui rellevant. Que PHP ens serveixi una plana en 1 segon i en Java la serveixi en 0,98 segons, doncs no és rellevant ja que per l'usuari de l'aplicació no significarà cap diferència en l'espera.
Això vol dir que ens hem de passar ja a programar en un llenguatge dinàmic i deixar els compilats? No. Vol dir que és necessari que a més d'un llenguatge tradicional convé que tinguem al caixò de les eines un llenguatge dinàmic. D'aquesta manera quan ens demanin una aplicació podrem sospesar els pros i els contres i fer-la en el llenguatge que s'adapti millor al projecte. Fins i tot si el nostre projecte es farà en un llenguatge tradicional, tenir un llenguatge dinàmic se pot integrar al projecte en forma de rutines de generació de codi, scripts de testeig, etc.
Dit això, i considerant l'àmbit de les aplicacions web, anem a veure avantatges i inconvenients d'elegir un llenguatge dinàmic per al nostre projecte, com que el que més conec és el Python, em permetreu la llibertat de donar els exemples i les referències en aquest llenguatge, in en Java en el cas de llenguatge compilat, en el ben entès que segur que hi ha el mateix tipus de solucions en Ruby, PHP o el vostre llenguatge dinàmic preferit i en C, C++ o .Net, etc.
Cicle de desenvolupament Agafem una aplicació web típica Java desplegada a un servidor Tomcat i la mateixa aplicació feta en Python. El cicle al primer cas és: escriure codi, compilar, corregir els errors de sintaxi, desplegar-ho al servidor, provar i iniciar novament tot el cicle, bé per escriure nou codi o bé per corregir els errors. A l'aplicació Python+Django tendríem: escriure codi, provar i tornar a escriure el nou codi. L'augment de productivitat la tenim no per el fet de no tenir que corregir errors, sinó pel fet de no tenir temps d'espera en el desplegament i per haver d'escriure menys línies de codi.
Errors de sintaxi. Aquí algun haurà pensat, "s'ha deixat els errors de sintaxi", efectivament, ho he fet perquè vull dedicar-lis el seu propi apartat. El compilador caça els errors de sintaxi i ens n'informa, però això vol dir que hem d'executar el compilador, és veritat que això els IDEs com Eclipse ho fan automàticament, però tot i això s'ha de fer. Realment podem fer el mateix amb eines com Pylint o Pychecker i a més el primer a més és capaç de treure tot un conjunt d'estadístiques damunt la qualitat del codi, talment com ho fan eines Java com PMD. El pylint per exemple és el que fa servir PyDev per a indicar els errors de sintaxi i donar avisos damunt el codi. Està clar que aquests errors potser no seran tan acurats como els dels compiladors, però són prou bons com per fer la feina, amb l'avantatge, però que ho podem llançar quan nosaltres vulguem i que no s'ha de passar necessàriament pel compilador per a executar el programa.
Per una altra banda, que el compilador o pylint ens caci els errors de sintaxi ens pot donar una falsa sensació de seguretat, el errors de sintaxis poden fer petar l'aplicació, és veritat, però els errors més greus solen ser els errors en la lògica de negoci de l'aplicació. Quan aquests se detecten el que s'ha de fer és corregir-los el més aviat possible, i en aplicacions web entram en
La fase de desplegament El temps que necessitam per posar una aplicació en producció seria anecdòtic si les aplicacions no tinguessin errors. Necessitar 30 minuts o 10 minuts per una aplicació que es posi en producció i que estigui mesos o anys sense modificar-se no és significatiu. Però, si l'aplicació sofrirà canvis o els errors que es detectin s'han de resoldre el més aviat possible, una diferència de 30 minuts en el desplegament pot marcar la diferència. En el cas d'un error en la lògica de l'aplicació que afecti a un .jar, significa en el millor dels casos compilar l'aplicació o el jar corresponent, substitur-ho i reiniciar el servidor d'aplicacions o l'aplicació, cosa que pot dur entre uns 30 segons y un bon grapat de minuts, depèn del que hi hagi desplegat. El el cas d'un llenguatge dinàmic sovint basta actualitzar els arixus afectats (svn update per exemple) i fer un reload del servidor Apache que tenguem. Cosa d'un parell de segons tot plegat. Està clar que podem minimitzar el primer cas amb servidors redundants i configuracions d'alta disponibilitat, però a un cost major de complexitat.
L'orientació a la tasca Les aplicacions web tracten fonamentalment amb text i produeixen text. Agafam dades de formularis, les tractam, obtenim dades de les bases de dades i les manipulam per a presentar-les a l'usuari en una sortida HTML, etc. Tractar cadenes és part fonamental de les aplicacions, i això és una cosa que els llenguatges compilats fan en general força malament des del punt de vista del nombre de línies de codi que s'han de picar. Els llenguatges dinàmics són força bons tractant informació textual. Posaré un exemple que ens pot servir per il·lustrar aquest fet: les plantilles. En Java tenim tres llenguatges de plantilles principalment: JSP-EL (ja ho sé està agafat pels pels però acceptau-me'l), Freemaker i el venerable Velocity i en general són força infumables. Comparem-ho amb el que hi ha per Python i veurem la diferència (Django, Web String, Cheetath, Mako, Jinja, ...). No crec que sigui anecdòtic, la diferència és que és força senzill manipular text en Python i per tant és força senzill crear-te el teu propi llenguatge de plantilles.
Productivitat El nombre de línies de codi que escriu un programador al dia és una constant. Això vol dir que si volem tenir més productivitat s'ha d'anar cap a llenguatges que ens permetin escriure més funcionalitat en menys codi. Donat el boom que viuen actualment els llenguatges dinàmics encara no hi ha molta literatura al respecte in encara no hi ha ni Ruby, ni PHP ni Python al QSM, però sí que hi ha forces comparatives, una de les més interessants és la d'Stephen Ferg que senyala que programar en Python és de 5 a 10 vegades més productiu que fer-ho en Java.
Diversió i motivació Els llenguatges dinàmics són sexy. Permeten al programador un alt grau de realimentació. Pot provar les seves idees molt ràpidament i això el motiva molt més que tenir que esperar a que el compilador acabi per poder fer les proves.
Pel demés, els llenguatges dinàmics també necessiten unit tests (sols que es poden escriure més ràpidament i amb menys línies), una gestió acurada dels projectes, planificació del que volem fer i refactorització del codi.
La conclusió de tot això és que deixem de banda FUDs que no duen a res i a l'hora d'avaluar un projecte considerem també si és apte per a ser fet en un llenguatge dinàmic, si ho és endavant, el nostre cul no perilla per dita elecció, ja que tant Python, com Ruby com PHP seran prou capaços de fer la feina.
Traducciones/Translations by apertium
3 comentaris, 0 trackbacks (URL) , Tags: Informàtica Python Django
vim ide per python
Escrit per Aaloy a 17 de May , 2008 a les 9:22 a.m.
Aquesta setmana i gràcies a l'entrada del blog de sontek he retornat al vi com a editor principal per a la programació en Python.
Periòdicament estic canviant entre vim, kate o Eclipse amb PyDev, segons la màquina en que faig feina i el que estic fent, però pas gran part del temps fent feina amb la consola i trobar la configuració que permet tenir el millor de els entorns gràfics a vim m'ha sorprès gratament.
Amb la configuració i el conjunt de plugins que ha seleccionat sontek tenim un editor que permet tabs, autocompletat, plantilles, integració amb subversion i tota la potència d'edició de vim. El tema del depurador integrat està un poc més verd, però tampoc l'he trobat massa a faltar.
A un dels comentaris hi havia la recomanació per a la instal·lació a més de NERDTree un navegador de fitxers integrat al vim que facilita molt la vida, i també ho vaig posar. També he adaptat les plantilles que venen per Django per a que agafi la sintaxi del trunk i he afegit algun retoc més com que posi la codificació als fitxers i coses així, les plantilles són tan bones de modificar que me pareix que puc acabar amb un bon repositori de codi :)
Tot i que no poseu la configuració crec que és interessant fer-hi una ullada a com ha organitzat Sontek (John M. Anderson) el seu fitxer de configuració per veure un exemple del que es pot fer amb vim i de com es pot incloure codi Python dins l'arxiu de configuració. Una de les maneres d'aprendre a programar és llegir codi d'altres (bon codi si és possible) i en l'arxiu de configuració de vim podem dir el mateix: llegint configuracions d'altra gent podem arribar a personalitzar l'editor fins a límits insospitats.
