lunes, 12 de octubre de 2015

Desempolvando Django (4)

Hasta ahora hemos desarrollado algo muy básico: un listado de marcadores. Casi todo lo que hemos hecho ha sido definir y configurar cosas.

Veamos los siguientes pasos:

  • Cada marcador podía llevar asociada una imagen, mostrémosla en el listado

  • Debemos asociar una hoja de estilo a nuestras páginas

  • Debemos añadir alguna forma de crear nuevos marcadores y editar los existentes

  • Hay que preparar la aplicación para ser servida en un entorno "de producción", no desde el servidor de desarrollo

  • Y muchas más características y mejoras, por supuesto, pero vamos poco a poco


Mostrando la imagen en el listado


Recordemos cómo era nuestro modelo de datos (models.py):
from django.db import models

class Marcador(models.Model):
    titulo = models.CharField(max_length=200)
    url = models.CharField(max_length=500)
    descripcion = models.CharField(max_length=500)
    image = models.ImageField(upload_to='savebookmarks')

    def __str__(self):
        return self.titulo + ' (' + self.url + ')'

Al definir el atributo imagen especificamos la ruta donde se subirán las imágenes, es relativa al parámetro MEDIA_ROOT de settings.py

Si hemos creado unos cuantos objetos desde el "/admin", podremos mostrar su imagen en el listado. Para ello, en desarrollo, definiremos en settings.py una ruta más para ser servida como recurso estático. Esto "en producción" habrá que cambiarlo, pero por ahora seguimos con ello así:

En settings.py
STATIC_URL = '/static/'
MEDIA_ROOT = BASE_DIR + '/media'
STATICFILES_DIRS = (BASE_DIR + '/savebookmarks', MEDIA_ROOT)

STATIC se refiere a cómo se sirven recursos estáticos (hojas de estilo, JS, iconos, etc). MEDIA_ se refiere a cómo se sirven ficheros subidos por los usuarios. Por seguridad, en producción, separaremos totalmente ambos tipos de recursos. En desarrollo, los servimos igualmente con la app 'django.contrib.staticfiles' mediante la definición STATICFILES_DIRS.

En la template, incorporamos la imagen (marcador_list.html):
{% load staticfiles %}
<link rel="stylesheet" type="text/css" href="{% static 'savebookmarks/estilo.css' %}" />

<h2>Marcadores</h2>
<ul>
    {% for m in object_list %}
        <li>
            <img src="{% static m.image %}" width="40" />
            <a target="_blank" href="{{m.url}}">{{ m.titulo }}</a>
            | (<a href="{% url "editMarcador" m.id %}">Editar</a>)
        </li>

    {% endfor %}
</ul>

<p><a href="{% url "addMarcador" %}">Añadir otro</a></p>

El tag {% static %} es atendido por la app 'django.contrib.staticfiles' y nos proporciona la URL relativa para ser servida por el servidor de desarrollo. En producción habrá que revisar esto. El resto de código lo revisaremos a continuación.

Enlazando una hoja de estilos


directorios

Como definimos en settings.py un
STATIC_URL = '/static/'

ahora podemos poner bajo esta URL nuestras hojas de estilo. El árbol de directorios del proyecto se va pareciendo a la figura.

La app 'django.contrib.staticfiles' encontrará los ficheros bajo static/ y los servirá adecuadamente.

Para enlazar estos ficheros, usamos el tag {% static %}, como se ha mostrado en la template.

En el paso a producción lo que se hace es utilizar un servidor web independiente para servir los recursos, en la documentación de Django se explica cómo hacerlo, por ahora mantenemos esta configuración en desarrollo.

Crear y editar marcadores


En este punto, utilizaremos el sistema de formularios de Django, que está muy bien pensado.

 

 

 

lunes, 5 de octubre de 2015

Desempolvando Django (3)

Lo siguiente que vamos a hacer es una vista para mostrar los marcadores ya guardados. Una de las tareas más típicas es sacar un listado de objetos guardados en la base de datos, para ello, Django tiene ya una vista preparada y lista para usar. Editemos nuestro savebookmarks/views.py
from django.views.generic import ListView
from savebookmarks.models import Marcador

class MarcadoresList(ListView):
    model = Marcador

Ya está, tenemos una vista que hereda de ListView en la que lo único que tenemos que hacer es decirle cuál es el modelo que se corresponde con los objetos que queremos listar.

En savebookmarks/urls.py, la invocamos con el método as_view() (que devuelve una respuesta adecuada como objeto HttpResponse)
from django.conf.urls import url
from . import views

urlpatterns = [
    url(r'^$', views.MarcadoresList.as_view()),
]

Finalmente, necesitaremos una plantilla o template que muestre los objetos que está recuperando la ListView para nosotros.

Para que Django encuentre las plantillas, vamos a editar el settings.py dejando así la clave DIRS (en TEMPLATES):
'DIRS': os.path.join(SETTINGS_PATH, 'templates'),

(Se supone que con el ajuste  'APP_DIRS': True, Django ya es capaz de encontrar nuestras templates, pero parece que no funciona)

A continuación, escribimos la template en /savebookmarks/templates/savebookmarks/marcador_list.html:
<h2>Marcadores</h2>
<ul>
    {% for m in object_list %}
        <li><a target="_blank" href="{{m.url}}">{{ m.titulo }}</a></li>
    {% endfor %}
</ul>

La ListView nos proporciona una lista de objetos, object_list, sobre la que iteremos para mostrarlos.

marcadores

viernes, 2 de octubre de 2015

Desempolvando Django (2)

En la anterior entrada vimos cómo montar el entorno de desarrollo, ahora empezamos a esbozar el proyecto.

Se trata de una web básica que permite guardar marcadores, como el antiguo delicious.

Empezaremos con una sola pantalla que liste los marcadores guardados y ofrezca un campo (input) para guardar uno nuevo.

Recién creado el proyecto, la estructura de directorios es la siguiente:

  • <Raíz del proyecto>/

    • manage.py

    • marcadores/ (en este directorio editaremos los siguientes archivos):

      • urls.py

      • settings.py






Recordamos que el proyecto se creó con el comando
django-admin startproject marcadores

Ahora tenemos que crear una "app" dentro del proyecto
  python manage.py startapp savebookmarks

En esta app es donde escribiremos nuestro código. Por ahora seguiremos más o menos el tutorial de Django "Writing your first Django app".

Finalmente, en /marcadores/urls.py, mapearemos las dos URLs principales que utilizaremos, /admin y /savebookmarks
from django.conf.urls import include, url
from django.contrib import admin

urlpatterns = [
    url(r'^savebookmarks/', include('savebookmarks.urls')),
    url(r'^admin/', include(admin.site.urls)),
]

Con esto, tras ajustar los parámetros necesarios en el settings.py y hacer un python manage.py migrate, podemos echar a andar el servidor de desarrollo.

(En versiones anteriores de Django no se disponía de migrations, si no recuerdo mal, había que hacerlo con una herramienta externa, South).

Empecemos a hacer algo útil en la app "savebookmarks"

Definamos el modelo (/savebookmarks/models.py)
from django.db import models

class Marcador(models.Model):
    titulo = models.CharField(max_length=200)
    url = models.CharField(max_length=500)
    descripcion = models.CharField(max_length=500)
    image = models.ImageField(upload_to='savebookmarks')

    def __str__(self):
        return self.titulo + ' (' + self.url + ')'

También crearemos un /savebookmarks/admin.py para poder actualizar desde la interfaz administrativa:
from django.contrib import admin
from .models import Marcador
admin.site.register(Marcador)

En este momento, como ha habido cambios en los modelos de datos, habrá que hacer el flujo de makemigrations/migrate como se describe en el tutorial.

En este punto, ya podemos empezar a crear desde la interfaz administrativa (http://127.0.0.1:8000/admin) algunos objetos del tipo marcador para no empezar con una base de datos vacía.

Lo siguiente, es crear la pantalla inicial. Lo más sencillo es recuperar el listado de marcadores ya creados, necesitamos definir una URL y una vista que la sirva. Definamos que la URL "/" sea atendida por la vista index, en /savebookmarks/urls.py
from django.conf.urls import url
from . import views
urlpatterns = [
    url(r'^$', views.index, name='index'),
]

Recordemos que las URL "maestras" se definen a nivel proyecto (marcadores/urls.py) y aportan el prefijo, de tal forma que una URL con el patrón /savebookmarks será definida por savebookmarks/urls.py (fichero de definición de URLs de la app).

Sólo para comprobar que funciona, definimos una función index() en savebookmarks/views.py
from django.http import HttpResponse

def index(request):
    return HttpResponse("Aquí iría el listado de marcadores")

Y eso es todo por ahora. Lo siguiente es pensar cómo vamos a montar estas vistas, porque hay varias alternativas a la hora de escribir vistas:

  • Vistas como funciones (ejemplo anterior)

  • Class-based views (https://docs.djangoproject.com/en/1.8/topics/class-based-views/), que a su vez pueden ser

    • Genéricas

    • Escritas por nosotros



sábado, 5 de septiembre de 2015

Desempolvando Django (1)

Hace como 3 años que no toco una línea de código Python, y, teniendo en cuenta que estuve una buena temporada trabajando como desarrollador Django, se me cae un poco la cara de vergüenza de estar tan oxidado.

Como estoy un poco cansado del ecosistema PHP, voy a ponerme de deberes para este curso que empieza volver a ser competente en Django.

Empezamos con lo más básico, la instalación. La forma fácil (y poco práctica en un entorno profesional) es bajarse el zip o tgz y darle al setup.py, o, peor todavía, usar la versión que nuestro gestor de paquetes nos proporciona (por ejemplo, apt-get install django).

En el momento de escribir esto, la versión en curso de Django es 1.8.4, y la forma recomendada de instalación es con pip.

Personalmente, recuerdo que es muy cómodo usar virtualenv para desarrollar Python, nos libramos de problemas con diferentes versiones, podemos probar actualizaciones en cada virtualenv, etc.

Suponiendo que tenemos ya instaladas las utilidades virtualenv y pip, allá vamos:

  1. Creamos un virtualenv con Python 3:
    yo@home:~/Desarrollo$ virtualenv --python=python3 django1.8.4

    Esto crea un virtualenv con Python3 en el directorio "django1.8.4"

  2. A continuación, nos metemos en el directorio recién creado y activamos el virtualenv:
    yo@home:~/Desarrollo$ cd django1.8.4/
    yo@home:~/Desarrollo/django1.8.4$ source bin/activate


  3. Instalamos Django ya con el virtualenv activado:
    (django1.8.4)yo@home:~/Desarrollo/django1.8.4$ pip install Django==1.8.4

    Vemos que nos queda Django sólo instalado para este entorno:
    (django1.8.4)yo@home:~/Desarrollo/django1.8.4$ ls lib/python3.4/site-packages/
    django Django-1.8.4.dist-info ...

    Con esto ya tenemos montado el virtualenv con una instalación propia de Python 3 y Django 1.8.4, sin afectar a la instalación que haya en el sistema.

  4. Con este entorno activado ya podemos salir del directorio donde hemos instalado el virtualenv y crear el proyecto Django en cualquier otro directorio. Procedemos de la forma habitual, con un startproject y comprobando que se crea el manage.py y el directorio para el código del proyecto:
    (django1.8.4)yo@home:~/Desarrollo/django1.8.4$ cd ..
    (django1.8.4)yo@home:~/Desarrollo$ django-admin startproject marcadores
    (django1.8.4)yo@home:~/Desarrollo$ cd marcadores/
    (django1.8.4)yo@home:~/Desarrollo/marcadores$ ls
    manage.py  marcadores


  5. Nos puede surgir la duda... pero si yo ahora invoco Python... ¿cuál estaré usando? Respuesta: si estamos con el virtualenv activado, será el que tenemos en nuestro directorio virtualenv:
    (django1.8.4)yo@home:~/Desarrollo/marcadores$ which python 
    /home/yo/Desarrollo/django1.8.4/bin/python

    Si desactivamos el virtualenv, volvemos al intérprete por defecto instalado en el sistema:
    (django1.8.4)yo@home:~/Desarrollo/marcadores$ deactivate
    yo@home:~/Desarrollo/marcadores$ which python
    /usr/bin/python



Por ahora ya hemos preparado el entorno. En la siguiente sesión, empezaremos a programar.

 

lunes, 6 de julio de 2015

Guarreando con los límites de un array

Como no puede ser de otra forma, en el día a día, por muchos años que lleve uno desarrollando, siempre nos encontramos con el típico error latoso provocado por acceder fuera de los límites de memoria de una estructura de datos. Algunos lenguajes son totalmente permisivos y no avisan, otros sacan "warning" y siguen adelante, otros mueren ante estas situaciones. Me entró el otro día la curiosidad de ver cómo reaccionan diferentes lenguajes antes estos errores, así que me preparé unas líneas de código triviales en diferentes lenguajes para comprobarlo. Se trata de acceder fuera de los límites de un array. En Perl:
#!/usr/bin/perl
use strict;
my @input = ("This", "is", "an", "array");
my $max = 4;
for (my $x=0; $x<=$max; $x++) {
printf("Index: %d // Value: %sn", $x, $input[$x]);
}
printf "Goodbye, I'm Perl!n";

En PHP:
<?php
$input = array("This", "is", "an", "array");
$max = 4;
for ($x=0; $x<=$max; $x++) {
echo sprintf("Index: %d // Value: %sn", $x, $input[$x]);
}
echo "Goodbye, I'm PHP!n";

En Python:
#!/usr/bin/env python

input = ["This", "is", "an", "array"];
max = 4;
for x in range(max+1):
print "Index: %d // Value: %s" % (x, input[x]);
print "Goodbye, I'm Python";

En C:
#include 

int main() {
char *input[] = {"This", "is", "an", "array"};
int max = 4;
for (int x=0; x<=max; x++) {
printf("Index: %d // Value: %sn", x, input[x]);
}
printf("Goodbye, I'm C!n");

}

En Java:
class out {
public static void main(String[] args) {
String[] input = {"This", "is", "an", "array"};
int max = 4;
for (int x=0; x<=max; x++) {
System.out.printf("Index: %d // Value: %sn", x, input[x]);
}
System.out.println("Goodbye, I'm Java");
}
}

En Pascal:
program Out;
Uses sysutils;

Var
max : Integer = 4;
x : Integer;
input : Array [0..3] of string[20] = ('This', 'is', 'an', 'array');
begin
for x := 0 to max do
begin
Writeln('Index: ' + IntToStr(x) + ' // Value: ' + input[x]);
end;
end.

He ignorado deliberadamente los modismos de cada lenguaje para hacer algo similar en todos (jamás recorrería una lista en Python de esta forma). Al ejecutarlo, es curioso ver cómo reacciona cada lenguaje en cada plataforma. Por ejemplo, en un MacOSX:




Veamos en un Linux:



Es curioso el comportamiento de la versión en C. En Mac OSX devuelve un valor sin fallar (accede a un área de memoria que no le pertenece) y en Linux hace un "segfault". La línea de compilación es la misma en ambos casos: gcc -std=c99 -o outc out.c, pero el ejecutable resultante no se comporta igual. Tendría que mirarlo más a fondo ;)

lunes, 15 de junio de 2015

Adiós, Sourceforge, que te zurzan

Durante muchos años, Sourceforge fue uno de los principales repositorios de software "open source". GitHub le quitó el puesto, pero me temo que son ellos mismos (SF) los que se acaban de poner el último clavo al ataúd.

Desde hace un tiempo, empezaron a poner adware en los instaladores de los desarrolladores, llegando al límite de quedarse con la cuenta de algunos proyectos emblemáticos, como GIMP, VLC, Nmap o Notepad++, al más puro estilo Softonic (parece que estos últimos ya se han reformado, menos mal).

Todos estamos de acuerdo en que hay que buscar vías de financiación, pero apoderarse del código e instalar crapware a los usuarios finales... no puede ser.

Les deseo una pronta desaparición y que se busquen la vida de una forma más honrada y respetuosa con los usuarios y desarrolladores.

Actualización: parece que también han llenado de crapware un instalador de Firefox. Morir matando, lo llaman.

viernes, 8 de mayo de 2015

El venerable cowsay

Lo bien que me lo pasaba "tuneando" la shell con cowsay... Lo divertido es que se lo enseñas a gente joven y se queda alucinada ;-)

pantallazo-2015-05-08_091358

miércoles, 11 de marzo de 2015

10 años de desarrollo web

Ante todo, debo felicitarme a mi mismo. Este blog cumple 10 años. No voy a hacer el propósito de actualizarlo más a menudo (sobre todo porque no lo puedo mantener), pero seguirá existiendo por ahora.

¿Cómo ha ido cambiando el desarrollo web en estos años?

En 2005 teníamos la versión 5 de PHP recién salida del horno, la gente empezó a dejar de usar el "register_globals" (lo que costó...). No obstante, durante mucho tiempo se siguió utilizando PHP 4 en muchos sitios.

Por entonces se empezó a extender la idea en el mundillo PHP de que separar la lógica de la presentación era bueno y se empezó a extender el uso de motores de plantillas como Smarty. Parece que no, pero empezar a ver código PHP limpio de echo y sprintf fue un alivio.

Perl seguía fuerte en desarrollo web (Movable Type,...). Python no tenía mucha relevancia en el desarrollo web y Ruby era prácticamente un desconocido por estos lares.

Afortunadamente, ya empezaba a calar la idea entre los responsables IT de que quizás hacer las webs con tecnología J2EE no era lo adecuado en todos los casos. El tiempo nos ha dado la razón a los que pensábamos que era viable realizar proyectos de envergadura mantenibles con lenguajes de scripting.

Un punto de inflexión en el desarrollo web fue la aparición y popularización de varias soluciones que simplificaban mucho el trabajo: los frameworks. En el lado del servidor, herramientas como Ruby On Rails, Symfony, Django, y similares demostraron que era posible hacer aplicaciones web sin tener que estar tirando consultas SQL a pelo y manteniendo el código de forma organizada y previsible. En el lado cliente, las librerías de Javascript Prototype y jQuery (entre otras) nos ayudaron a abstraernos en cierta forma de las peculiaridades de cada navegador.

En 2008 ocurrieron dos cosas muy interesantes: llegaron los primeros iPhone a España y se lanzó el navegador Chrome. Nada ha vuelto a ser igual. Desde ese momento, Internet Explorer empezó a perder cuota de mercado inexorablemente.

2007

2015

Recuerdo alguna anécdota de entonces: le pregunté a un responsable editorial...
"Si la cuota de usuarios de alguna de las versiones de Internet Explorer baja del 5%, ¿nos autorizas a dejar de darle soporte?"

Je, je, je. A día de hoy, ya no tenemos que dar soporte a los Explorer más antiguos. Los diseñadores y maquetadores respiran aliviados.

Por otra parte, el incremento de uso de los teléfonos móviles con navegador nos obligó a todos a empezar a hacer webs diferenciadas para estos dispositivos.

De esta época también data la locura de las "apps". Todo el mundo quería tener una app, sin saber ni siquiera diferenciar una app nativa de un acceso directo en el "escritorio" del móvil con el iconito a la versión web. El tiempo parece que también ha colocado las cosas en su sitio. No era normal que unos dispositivos con un navegador tan completo (el Safari de los iPhone o el stock browser de Android, ambos basados en Webkit, también estaba el Opera Mini) necesitasen una aplicación nativa para mostrar lo que el navegador podía hacer perfectamente.

En 2010 se empezó a comercializar el iPad y poco después todas las tabletas de otros fabricantes. Otro cachivache más al que dar soporte. La maquetación y diseño se han complicado bastante, así que se ha terminado haciendo un diseño único que se adapte al dispositivo en el que se ve la página. No era viable tener tres versiones diferentes de una misma web, una para "desktop", otra para tablet y otra para móvil.

JavaScript ha evolucionado mucho y parece que prácticamente está estandarizado. Librerías como jQuery, Motools,.. y multitud de frameworks y librerías han surgido para facilitarnos el trabajo. Ya no vemos cosas como document.getElementById().

El lenguaje se ha rehabilitado a los ojos de los desarrolladores. JS ya no es el lenguaje que se usa para hacer monerías en una página web. Es un estándar, con muchas implementaciones y ya no está restringido a ejecutarse en el navegador. Node.js es la implementación más conocida de JS fuera del navegador, pudiendo correr en el "backend".

La sintaxis de objetos de JS (JSON) se ha convertido en muchos casos en el formato estándar "de facto" para intercambiar información estructurada, desplazando al voluminoso XML.

AJAX... Asynchronous Javascript And Xml... las siglas se han quedado, pero nadie utiliza ya XML para intercambiar datos, mejor JSON, que es más compacto y se parsea más rápido.

HTML5, casi más una buzzword que otra cosa. Pero está muy  bien tener un <video>, <audio> y <canvas> nativos y estructurar un poco mejor el HTML (<header>, <section>, <footer>,...), pero parece que no nos terminamos de quitar de encima al COBOL de la web: Flash.

Volviendo a JavaScript, en los últimos años ha habido una "explosión" de utilidades, implementaciones, frameworks y librerías. Los que nos hemos quedado un poco en la parte del desarrollo del backend estamos un poco desconcertados (yo al menos).

La última tecnología que se ha popularizado y ha pasado a ser "mainstream" en los últimos años son las bases de datos no relacionales (NoSQL). Otro campo en el que profundizar, sobre todo los que hemos seguido en el paradigma relacional.

Pese a todos estos cambios, hay una constante que permanece: hacer webs se lleva mucho tiempo. Tenemos mejores herramientas y entornos, pero los requisitos ahora son mucho más exigentes.