Este capítulo tiene como finalidad introducir los conceptos básicos concernientes a la generación dinámica de contenido en el servidor web.
Inicialmente se realiza una revisión general de los componentes de un servidor web. Luego se realiza un breve análisis sobre los lenguajes dinámicos en el contexto de la web y sus principales frameworks.
Los frameworks son conjuntos de componentes de software genéricos y reutilizables que simplifican el desarrollo.
Por último se detallan las características que hacen relevante el estudio de los lenguajes dinámicos como plataforma de desarrollo de aplicaciones web dinámicas.
En el enfoque dinámico, cuando un usuario realiza una solicitud, el mensaje enviado tiene como objetivo la ejecución de un programa o secuencia de comandos en el servidor. Por lo general, el procesamiento involucra el uso de la información proporcionada por el usuario para buscar registros en una base de datos y generar una página HTML personalizada para el cliente.
Tradicionalmente, la tecnología utilizada se conoce como CGI [*], estándar que consiste en delegar la generación de contenido a un programa. CGI se limita a definir la entrada y salida de éste.
| [*] | Common Gateway Interface |
Un enfoque más moderno para la generación de contenido dinámico es la incrustación de secuencias de comandos dentro de las páginas HTML. Estos comandos son leídos y ejecutados en el servidor al momento de responder a la solicitud del cliente, como en el caso de los lenguajes PHP o ASP.
Los servidores web primigenios y monolíticos evolucionaron a una arquitectura modular [ApacheMod2009] [MicrosoftIIS2009], en la cual un módulo brinda soporte para una tarea específica. Los módulos más comunes son los de autenticación, bitácora, balance de carga, así como también los de generación de contenido.
| [ApacheMod2009] | Módulos del servidor Apache 2.2, último acceso, Septiembre de 2009, http://httpd.apache.org/docs/2.2/mod/ |
| [MicrosoftIIS2009] | Módulos en Microsoft IIS, último acceso Septiembre 2009, http://msdn.microsoft.com/en-us/library/bb757040.aspx |
Las nuevas formas de interacción entre un servidor web y un programa se desarrollan con el objeto de satisfacer necesidades más complejas, que no fueron tenidas en cuenta en la genericidad de CGI.
En la plataforma Java, se especificaron los Servlets [SunServlet2009], con implementaciones como Tomcat [ApacheTomcat2009], y en 1996 se publicó la especificación J2EE, que formula una arquitectura de aplicación web dividida en capas que ejecuta un servidor de aplicaciones.
Basados en la especificación J2EE, se crearon numerosos frameworks cuyos lineamientos determinaron la manera de concebir las aplicaciones web durante casi una década.
En junio de 2004 se publica el proyecto Ruby On Rails, un framework de aplicaciones web, desarrollado en el lenguaje de programación Ruby, que revolucionó la forma de concebir los frameworks y la web.
James Duncan Davidson, el creador de Tomcat y Ant, llegó a decir que Ruby On Rails es el framework web mejor pensado que él ha usado. Davison pasó 10 años desarrollando aplicaciones web, frameworks y la especificación de los Servlets para el lenguaje Java [RailsQuotes2009] [†].
| [RailsQuotes2009] | (1, 2) Citas de Ruby On Rails, http://rubyonrails.org/quotes |
| [†] | “Rails is the most well thought-out web development framework I’ve ever used. And that’s in a decade of doing web applications for a living. I’ve built my own frameworks, helped develop the Servlet API, and have created more than a few web servers from scratch. Nobody has done it like this before.” |
| [ApacheTomcat2009] | http://tomcat.apache.org/index.html |
| [SunServlet2009] | http://java.sun.com/products/servlet/ |
Un servidor web, o web server, es un software encargado de recibir solicitudes de recursos de un cliente, típicamente un navegador web, a través del protocolo HTTP y de generar una respuesta. Mediante la especificación MIME, que se incluye en el encabezado de la respuesta, se puede identificar qué tipo de archivo es devuelto, siendo el tipo más común el HTML.
El contenido que se envía al cliente puede ser de origen estático o dinámico.
El contenido estático es aquel que proviene desde un archivo en el sistema de archivos sin ninguna modificación.
El contenido dinámico, en contraposición al contenido estático, proviene de la salida de algún programa, un script o algún tipo de API invocada por el servidor web (como SSI, CGI, SCGI, FastCGI, JSP, ColdFusion, NSAPI o ISAPI).
La única forma de acceder a los recursos del servidor web es a través de una URL, independientemente de su naturaleza.
Common Gateway Interface [‡] surge alrededor del año 1998, como el primer estándar de comunicación o API entre un servidor web y un programa de generación de contenidos.
La salida del programa invocado por el servidor web puede ser un documento HTML entendible para el navegador, o cualquier otro tipo de archivo, como imágenes, sonidos, contenido interactivo, etc.
Las variables de entorno y la entrada estándar constituyen los mecanismos de entrada del programa, mientras que el contenido devuelto al servidor web proviene de la salida estándar.
Dentro de la información provista por el servidor web se tienen los parámetros HTTP (como la URL, el método, el nombre del host, el puerto, etc.) y la información sobre el servidor. Estos datos se transfieren mediante variables de entorno.
Si existiese un cuerpo en la petición HTTP, como por ejemplo el contenido de un formulario, la aplicación CGI accede a esta información como entrada estándar.
El resultado de la ejecución de la aplicación CGI se escribe en la salida estándar, anteponiendo las cabeceras HTTP de respuesta. En dichos encabezados, el tipo MIME determina cómo se interpreta la respuesta. Es decir, la invocación de un CGI puede devolver diferentes tipos de contenido al cliente (HTML, imágenes, JavaScript, contenido multimedia, etc.).
Proceso de una solicitud con CGI.
Dentro de las variables de entorno, la Wikipedia [WikiCGI2009] menciona:
QUERY_STRING
Cadena de entrada del CGI cuando se utiliza el método GET sustituyendo algunos símbolos especiales por otros. Cada elemento se envía como una pareja Variable=Valor. Si se utiliza el método POST esta variable de entorno está vacía.
CONTENT_TYPE
Tipo MIME de los datos enviados al CGI mediante POST. Con GET está vacía. Un valor típico para esta variable es: Application/X-www-form-urlencoded.
CONTENT_LENGTH
Longitud en bytes de los datos enviados al CGI utilizando el método POST. Con GET está vacía.
PATH_INFO
Información adicional de la ruta (el “path”) tal y como llega al servidor en la URL.
REQUEST_METHOD
Nombre del método (GET o POST) utilizado para invocar al CGI.
SCRIPT_NAME
Nombre del CGI invocado.
SERVER_PORT
Puerto por el que el servidor recibe la conexión.
SERVER_PROTOCOL
Nombre y versión del protocolo en uso. (Ej.: HTTP/1.0 o 1.1).
Las variables de entorno que se intercambian del servidor al CGI son:
SERVER_SOFTWARE
Nombre y versión del software servidor de www.
SERVER_NAME
Nombre del servidor.
GATEWAY_INTERFACE
Nombre y versión de la interfaz de comunicación entre servidor y aplicaciones CGI/1.12.
Debido a la popularidad de las aplicaciones CGI, los servidores web incluyen generalmente un directorio llamado cgi-bin donde se albergan estas aplicaciones.
CGI posee dos limitaciones importantes. Una es la sobrecarga producida por la ejecución de un programa y la segunda es que no fue diseñado para mantener información sobre la sesión. Cada petición se trata de manera independiente [DwightErwin1996].
| [WikiCGI2009] | Interfaz de entrada común, Wikipedia, 2009, último acceso Agosto 2009, http://es.wikipedia.org/wiki/Common_Gateway_Interface#Intercambio_de_informaci.C3.B3n:_Variables_de_entorno |
| [DwightErwin1996] | Limitaciones de CGI, Using CGI, http://www.bjnet.edu.cn/tech/book/seucgi/ch3.htm#CGILimitations |
| [‡] | A veces traducido como pasarela común de acceso. |
A continuación se analizan las características que hacen relevante el estudio de los lenguajes dinámicos como plataforma de desarrollo de aplicaciones web dinámicas.
Una de las clasificaciones más generales que se realizan de los lenguajes de programación es según la identificación de su objetivo. Los lenguajes de programación de propósito general están orientados a resolver cualquier tipo de problema, mientras que los lenguajes de propósito específico, o DSL [§], están enfocados en resolver un tipo de problema de manera más eficaz. Un ejemplo muy popular de DSL es el lenguaje de macros embebido en la planilla de cálculo Microsoft Excel [DavidPollak2006].
| [§] | Domain Specific Language. |
| [DavidPollak2006] | Ruby Sig:How To Design A Domain Specific Language, Google Tech Talk, 2:44, http://video.google.com/videoplay?docid=-8103284744220333344 |
Otra clasificación es la de lenguajes interpretados y lenguajes compilados. Un lenguaje de programación interpretado es aquel en el cual los programas son ejecutados por un intérprete, en lugar de realizarse una traducción a lenguaje máquina (proceso de compilación).
En un lenguaje interpretado el código fuente es el ejecutable. En cambio, para llegar a ejecutar un programa escrito en un lenguaje compilado se deben atravesar dos etapas. La primera consiste en la traducción de las sentencias a código máquina y la segunda, en el enlace en la cual se ensambla el código objeto resultado de la compilación. En esta última etapa también se resuelven los enlaces entre los diferentes módulos compilados.
Existe un mecanismo intermedio de ejecución, conocido como máquina virtual. En éste existe un proceso de compilación del código fuente a un lenguaje intermedio, comúnmente denominado bytecode. Este bytecode es luego ejecutado sobre un intérprete, al cual se denomina máquina virtual. La traducción del código fuente a bytecode puede ser explícita, como en el lenguaje Java, o implícita como en Python, donde se mezcla en el intérprete la funcionalidad de compilación a bytecode e interpretación.
Lenguaje interpretado con máquina virtual.
Los lenguajes de programación interpretados suelen ser de alto nivel y de tipado dinámico, es decir que la mayoría de las comprobaciones realizadas en tiempo de compilación son evaluadas durante la ejecución [¶].
| [¶] | Estas comprobaciones comprenden el chequeo de tipos de datos, la resolución de métodos, etc. |
A diferencia de los lenguajes compilados, en los cuales se requiere disponer de un compilador para la plataforma [#] y compilar el código, la mayoría de los lenguajes interpretados permiten ser ejecutados en varias plataformas (multiplataforma), ya que sólo es necesario disponer de un intérprete compilado.
| [#] | “Una plataforma es una combinación de hardware y software usada para ejecutar aplicaciones; en su forma más simple consiste únicamente de un sistema operativo, una arquitectura, o una combinación de ambos. La plataforma más conocida es probablemente Microsoft Windows en una arquitectura x86” [WikiPlataforma2009]. |
| [WikiPlataforma2009] | Multiplataforma, Wikipedia, 2009, último acceso, Agosto de 2009, http://es.wikipedia.org/wiki/Multiplataforma |
Generalmente los lenguajes interpretados son lenguajes dinámicos. Esto permite el agregado de código, extensión o redefinición de objetos y hasta inclusive modificar tipos de datos, en tiempo de ejecución. Cabe destacar que si bien estas características son factibles de implementar sobre lenguajes estáticos, esto no resulta sencillo.
En base a la clasificación antes mencionada, a continuación se analizan los lenguajes de programación más populares para el desarrollo de aplicaciones web.
Es un lenguaje de programación de propósito general diseñado por Larry Wall en 1987. Perl toma características del lenguaje C, del lenguaje interpretado shell (sh), de los lenguajes AWK, sed y Lisp, y, en menor medida, de muchos otros lenguajes de programación. Se usa principalmente para el procesamiento de texto, siendo muy popular en programación de sistemas. Muchos sistemas basados en CGI están escritos en Perl (sistemas de administración de servidores, correo, etc.). Perl está disponible para muchas plataformas, incluyendo todas las variantes de UNIX.
Estructuralmente, Perl está basado en un estilo de bloques como los del C o AWK, y fue ampliamente adoptado por su destreza en el procesamiento de texto.
La principal crítica que se le hace a este lenguaje es la ambigüedad y complejidad de su sintaxis, ya que una tarea puede ser realizada de varias maneras diferentes, dando lugar a confusión en los grupos de trabajo.
Es un lenguaje de programación orientado a objetos, multiplataforma y de propósito general, basado en máquina virtual, de compilación explícita. Java ha definido algunos elementos importantes en cuanto a la web dinámica, como los applets [♠] y la especificación J2EE. Dos desventajas que presenta este lenguaje son la sintaxis verborrágica y el tipado estático, heredados de su predecesor C++ [SeanKellyRecFromAdict2009].
Como contrapartida resulta una alternativa veloz para ciertas tareas, lo que motivó el desarrollo de lenguajes dinámicos [TolksdorfJVM2009] que hacen uso de la máquina virtual de Java [♥]. Entre ellos, se destaca Scala, que hoy cuenta con frameworks con una concepción posterior a RubyOnRails.
| [♠] | Pequeños programas que se ejecutan en el navegador web. |
| [♥] | Estos lenguajes interpretados utilizan compilación JIT a bytecode de la JVM. |
| [SeanKellyRecFromAdict2009] | Recuperándose de la adicción, Sean Kelly, Vídeo hospedado en The Internet Archive, último acceso Septiembre de 2009, http://www.archive.org/details/SeanKellyRecoveryfromAddiction |
| [TolksdorfJVM2009] | Programming languages for the Java Virtual Machine JVM, Robert Tolksdorf, último acceso Septiembre de 2009, http://www.is-research.de/info/vmlanguages/ |
Es un lenguaje interpretado, originalmente diseñado para ser embebido dentro del código HTML y procesado en el servidor, lo cual lo convierte en un DSL. Con los años evolucionó hacia un lenguaje de propósito general. Toma elementos de Perl, shellscript, C y recientemente de Java.
Tradicionalmente su ciclo de ejecución consiste en:
- A partir de la URL de la solicitud del cliente se determina el archivo PHP que se encargará de generar la respuesta.
- El servidor activa el módulo encargado de la interpretación de PHP, con el archivo y la solicitud como entrada.
- La salida es devuelta al cliente.
Presenta importantes ventajas sobre CGI, ya que no es necesario confeccionar un programa de usuario y la resolución de URLs está dada por la estructura del sistema de archivos. Si bien es muy popular [PHPNetPopularity2009] y está disponible en la gran mayoría de los servidores UNIX, PHP es criticado por no poseer ámbito de nombres para los módulos, promover el código desordenado y tener serios problemas a la hora de la optimización [BlogHardz2008]. Los autores de los frameworks Django y Ruby On Rails provienen del lenguaje PHP [SnakesAndRubies2005].
| [PHPNetPopularity2009] | Utilización de PHP según php.net, último acceso Septiembre de 2009, <http://www.php.net/usage.php> |
| [SnakesAndRubies2005] | Video de la charla Snakes and Rubies, Universidad DePaul, Chicago http://www.djangoproject.com/snakesandrubies/ |
| [BlogHardz2008] | http://hardz.wordpress.com/2008/02/07/php-hipertexto-pre-procesado/ |
Es un lenguaje orientado fuertemente a objetos, multiplataforma, creado en 1995 por Yukihiro “Matz” Matsumoto, en Japón. A menudo es comparado con Smalltak y se suele decir que Ruby es un lenguaje de objetos puro, ya que todo en él es un objeto. Posee muchas características avanzadas como metaclases, clausuras, iteradores, integración natural de expresiones regulares en la sintaxis, etc. Su sintaxis es compacta, gracias a la utilización de simbología, parte de la cual fue tomada de Perl.
Existen varios intérpretes de Ruby; el oficial está escrito en C. Además se conocen: YARV [YARV2009], JRuby [JRuby2009], Rubinius [Rubinius2009], IronRuby [IronRuby2009], y MacRuby [MacRuby2009].
| [YARV2009] | Yet Another Ruby VM, escrita por Sacasada Kiochi http://www.atdot.net/yarv/ |
| [JRuby2009] | JRuby es una máquina virtual de Ruby escrita sobre la máquina virtual de Java, http://jruby.codehaus.org/ |
| [Rubinius2009] | Rubinius es una máquina vritual de Ruby escrita en C++ http://rubini.us/ |
| [IronRuby2009] | IronRuby es una implementación de Ruby sobre la plataforma .Net http://www.ironruby.net/ |
| [MacRuby2009] | MacRuby es una implementación de Ruby sobre Objective-C para el sistema Mac OS X, http://www.macruby.org/ |
La aplicación que popularizó al lenguaje es el framework Ruby on Rails [♦]. Su versión estable oficial fue liberada en el año 2005 y representó un cambio radical al enfoque complejo de J2EE [RailsQuotes2009].
Ruby aún posee baja aceptación debido, quizás, a que la documentación oficial solía estar en el idioma japonés (aunque la situación se ha venido revirtiendo últimamente). Otra desventaja importante es que la velocidad del intérprete oficial es bastante baja y varía según las plataformas (cuando se la compara con otros lenguajes dinámicos similares como Python).
| [♦] | http://www.rubyonrails.com |
Es un lenguaje de programación interpretado multiparadigma, de propósito general. Fue creado por Guido van Rossum en el año 1991. Se trata de un lenguaje dinámico que toma elementos de varios lenguajes, como C, Java y Scheme, entre otros.
Python puede ser extendido mediante módulos escritos en C o C++, y se puede embeber el intérprete en otros lenguajes. También permite cargar bibliotecas de enlace dinámico.
La comunidad UNIX considera a Python como una evolución de Perl, con sintaxis limpia y potente. Eric Raymond, en el artículo “Why Python?”, explica su conversión de Perl a Python [EricRaymon2000].
| [EricRaymon2000] | Why Python, Linux Journal, publicado el 1° de Mayo de 2000, http://www.linuxjournal.com/article/3882 |
Muchos de las sistemas web basados en CGI, están escritos en Perl, por lo cual no es sorprendente encontrar una buena cantidad de proyectos Python orientados a la web [PythonPyPi2009].
| [PythonPyPi2009] | (1, 2) En el repositorio de proyectos del lenguaje se encuentran más 1100 resultados para paquetes relacionados con el término “web”. http://pypi.python.org/pypi?%3Aaction=search&term=web&submit=search |
Se han desarrollado varios módulos para la interconexión del intérprete de Python con un servidor web. Phillip J. Eby formuló un método, denominado WSGI (Web Server Gateway Interface), que sigue la filosofía del lenguaje [PEP333]. Informalmente se puede decir que WSGI es una traducción de CGI al lenguaje Python. Su objetivo principal es estandarizar, sobre el lenguaje, el mecanismo de comunicación entre el servidor y una aplicación.
| [PEP333] | PEP Python Enhancement Proposals son documentos en los que se proponen mejoras para el lenguaje Python, publicados en el sitio oficial http://www.python.org. |
Para satisfacer una solicitud bajo WSGI, se invoca la función de entrada con dos argumentos:
- Un diccionario con las variables de entorno, al igual que en CGI.
- Una función u objeto llamable, al cual se invoca para iniciar la respuesta.
En el siguiente ejemplo se mustra una aplicación mínima en WSGI. La función app es el punto de entrada y devuelve la cadena Hello World!. Utiliza la función que recibe como segundo argumento, start_response, para que el cliente determine cómo tratar la respuesta (en el ejemplo como texto plano).
def app(environ, start_response):
start_response('200 OK', [('Content-Type', 'text/plain')])
return ['Hello World\n']
En la figura 2.3 se detallan las capas intervinientes en WSGI: el cliente genera una solicitud, el servidor discrimina qué tipo de recurso está siendo requerido. Si se trata de un recurso dinámico, la respuesta se genera a través del módulo WSGI. En este caso interviene el intérprete de Python con sus liberías (Site Packages) y la aplicación WSGI.
Esquema WSGI.
Los motivos por los cuales se seleccionó el lenguaje Python para el desarrollo de la presente tesina, fueron su popularidad [PythonPyPi2009], un desempeño adecuado en relación a la cantidad de líneas de código escritas [DhananjayNene2009] y su filosofía de simplicidad [PythonOrgZen2009].
| [DhananjayNene2009] | Performance Comparison – C++ / Java / Python / Ruby/ Jython / JRuby / Groovy, blog de Dhananjay Nene, último acceso, septiembre de 2009, http://blog.dhananjaynene.com/2008/07/performance-comparison-c-java-python-ruby-jython-jruby-groovy/ |
| [PythonOrgZen2009] | El zen de Python, último acceso Septiembre de 2009, http://www.python.org/dev/peps/pep-0020/ |
Según la Wikipedia [WIK001] un framework de software es “una abstracción en la cual un código común, que provee una funcionalidad genérica, puede ser personalizado por el programador de manera selectiva para brindar una funcionalidad específica”. Además, agrega que los frameworks son similares a las bibliotecas de software (a veces llamadas librerías) dado que proveen abstracciones reusables de código a las cuales se accede mediante una API bien definida.
| [WIK001] | Software Framework, Wikipedia, 2009, http://en.wikipedia.com/software_framework, última visita Agosto de 2009. |
Sin embargo, existen ciertas características que diferencian a un framework de una librería o de una aplicación de usuario:
Inversión de control
Tradicionalmente las aplicaciones se escriben haciendo llamadas a las bibliotecas de manera explícita y el flujo de control es definido por el programador. En el caso de una aplicación escrita sobre un framework, el flujo es definido por éste.
Comportamiento por defecto
En cada elemento del framework existe un comportamiento genérico con alguna utilidad.
Extensibilidad
El comportamiento predefinido de cada componente del framework generalmente es modificado por el programador con algún fin específico. Los mecanismos utilizados en los frameworks desarrollados en lenguajes orientados a objetos suelen ser de redefinición o de especialización.
No modificabilidad del código propio del framework
Las extensiones y definiciones propias de una aplicación se realizan sobre el código del proyecto y no sobre el código del framework.
Framework vs Librería.
Al utilizar un framework para simplificar el desarrollo de un proyecto, los programadores no deben preocuparse en resolver detalles comunes de bajo nivel.
Por ejemplo, en un equipo en donde se utiliza un framework web para desarrollar un sitio de banca electrónica, los desarrolladores pueden enfocarse en la lógica necesaria para realizar las extracciones de dinero, en vez de la mecánica para preservar el estado entre las peticiones del navegador.
Sin embargo, existen una serie de argumentos comunes en contra de la utilización de frameworks:
- La complejidad de sus APIs.
- La incertidumbre generada por la existencia de múltiples alternativas para un mismo tipo de aplicación.
- El tiempo extra que suele requerir el aprendizaje de un framework, que debe ser tenido en cuenta.
Normalmente en el desarrollo de las aplicaciones web, mediante lenguajes de etiquetas como PHP o ASP, el diseño de la interfaz, la lógica de la aplicación y el acceso a datos suelen estar agrupados en un solo módulo. Esto conlleva un mantenimiento dificultoso y una baja interacción entre los diseñadores (artistas) y los programadores.
El patrón arquitectural MVC (Modelo Vista Controlador) propone discriminar la aplicación en tres capas: la interfaz con el usuario (vista) se desacopla de la lógica (controlador) y ésta, a su vez, del acceso a datos (modelo). Esta división favorece la reutilización de componentes.
Este patrón fue descripto por primera vez en 1979 por Trygve Reenskaug [Tryg1979], quien se encontraba entonces trabajando en Smalltalk en los laboratorios de investigación de Xerox. La implementación original se detalla en “Programación de Aplicaciones en Smalltalk-80(TM): Como utilizar Modelo Vista Controlador” [SmallMVC].
| [Tryg1979] | Trygve Reenskaug, http://heim.ifi.uio.no/~trygver/themes/mvc/mvc-index.html |
| [SmallMVC] | Steve Burbeck, Ph.D. http://st-www.cs.illinois.edu/users/smarch/st-docs/mvc.html |
Las capas del patrón son:
Modelo
Define los datos, sus relaciones y la manera de acceder a éstos. Asegura la integridad de los datos y permite definir nuevas abstracciones de datos.
Vista
Es la presentación del modelo, seleccionando qué mostrar y cómo mostrarlo, usualmente es la interfaz de usuario.
Controlador
Responde a eventos, usualmente acciones del usuario, e invoca cambios en el modelo y, probablemente, en la vista.
J2EE fue el primer framework que aplicó el concepto de MVC en el desarrollo de aplicaciones web. Gran cantidad de frameworks que surgieron desde entonces han aplicado en mayor o menor grado este concepto.
Un framework web MVC suele contar con los siguientes componentes [WIKI002]:
Acceso a datos
Los tipos de datos de los lenguajes orientados a objetos difieren del modelo entidad-relación utilizado para la definición de bases de datos. Un mapeador objeto-relacional tiene como objetivo acortar esta brecha, permitiendo definir entidades en objetos que luego se transportan a la base de datos, además de la capacidad de CRUD [♣] y consultas sobre éstos.
Seguridad
Existen diversos aspectos de seguridad que deben ser tenidos en cuenta cuando se implementa una aplicación web, como la validación de entrada, protección de XSS [**], etc.
Mapeo de URLs
Cada URL activa algún componente del controlador. La definición de esta asociación (o mapeo) generalmente está centralizada y bien especificada en un framework.
Sistema de plantillas
Las plantillas corresponden a la capa vista del patrón MVC. Su objetivo principal es definir la forma en la cual se muestran los datos al usuario, provenientes del controlador. Para definir la plantilla se utiliza un lenguaje básico que se integra con HTML o XML. La utilización de plantillas facilita la modificación de la fachada sin acceder al modelo o al controlador.
Caché
La generación de ciertas páginas dentro de la aplicación puede requerir un tiempo considerable por lo que es, normalmente, utilizada alguna técnica de caché, que consiste en almacenar la salida durante un tiempo determinado, de manera de obtener una respuesta previamente almacenada, lo que repercute en mayor velocidad. Algunos frameworks hacen uso de algún mecanismo unificado para realizar caché.
AJAX
Hoy en día es común la técnica de incorporar elementos dinámicos que modifican el estado de una página de manera asincrónica. Es decir, partes de la página pueden realizar nuevas requerimientos al servidor cuya respuesta no fuerza una recarga de la página. Esta técnica es conocida como AJAX. Algunos frameworks, como, Ruby On Rails soportan AJAX directamente en el sistema de plantillas.
Configuración mínima y simplificada
Existen ciertos parámetros de las diversas capas que, en algunos frameworks, se encuentran especificados en un único archivo XML. Hoy en día se prefieren los lenguajes más amigables para los programador como YAML, los archivos INI o código en el lenguaje de implementación.
| [♣] | Create, Retrieve, Update, Delete |
| [**] | Cross Site Scripting |
| [WIKI002] | Web Framework, Wikipedia, última visita Agosto de 2009, http://en.wikipedia.org/wiki/Web_application_framework, |
Los lenguajes orientados a objetos utilizan clases, atributos y referencias para modelar el dominio de la aplicación, mientras que las bases de datos relacionales lo hacen a través de tablas y relaciones entre ellas. En el paradigma orientado a objetos una de las características que se enuncia es la persistencia; sin embargo, las implementaciones actuales de persistencias de objetos no brindan la performance ni la facilidad de consultas que ofrecen las bases de datos relacionales (RDBMS). El lenguaje utilizado para definir y modificar los datos en los RDBMS se conoce como SQL, el cual difiere radicalmente de la concepción de los lenguajes orientados a objetos (OO).
Como, generalmente, se utiliza una base de datos relacional para el almacenamiento, un mapeador objeto-relacional (ORM) libera la carga de la conversión explícita del lenguaje OO a SQL y logra independizar la estructura de almacenamiento del modelo de dominio.
Un ORM realiza, en principio, tres tipos de conversión del lenguaje OO a SQL:
- Conversión de definición de entidades.
- Creación, modificación y eliminación de entidades a partir de instancias de clases del modelo (persistencia).
- Definición de consultas en lenguaje OO y recuperación de datos en instancias de clases de modelo.
A través de una API bien definida un ORM se presenta como un mecanismo de persistencia de objetos.
Django es un framework web escrito en Python que se apoya en el patrón MVC. Surge para la administración de páginas de noticias, lo que se pone de manifiesto en su diseño proporcionando una serie de características que facilitan el desarrollo rápido de páginas orientadas a contenidos (CMS). Posteriormente los desarrolladores encontraron que su CMS es lo suficientemente genérico como para cubrir un ámbito más amplio de aplicaciones.
Su código fuente fue liberado bajo la licencia BSD en julio de 2005 (Django Web Framework). El slogan del framework es “Django, El framework para perfeccionistas con fechas límites” [††].
| [††] | Del inglés “The Web framework for perfectionists with deadlines”. |
En junio de 2008 fue anunciada la creación de la Django Software Fundation, que se encarga de su desarrollo y mantenimiento.
Django, como framework de desarrollo, consiste en un conjunto de utilidades de consola (CLI) que permiten crear y manipular proyectos. Un proyecto está constituido por una o más aplicaciones, las cuales se clasifican en:
Aplicaciones de usuario
Son aquellas que resuelven algún problema específico (por ejemplo: ventas, alquileres, blog, noticias, etc.).
Aplicaciones genéricas
Son aquellas que resuelven problemas comunes, como la autenticación, bitácora, sindicación, etc. Algunas aplicaciones genéricas se distribuyen con Django, de las cuales resulta interesante destacar:
django.contrib.admin
Provee de manera automática un sitio de administración que permite realizar operaciones CRUD sobre el modelo (definido a través del ORM) y administrar usuarios y permisos, llevando un registro de todas las acciones realizadas sobre cada entidad (sistema de logging o bitácora).
django.contirb.comments
Es un sistema de comentarios genérico que permite comentar diversas entidades del modelo.
django.contrib.syndication
Consiste en herramientas para sindicar contenido vía RSS y/o Atom.
django.contrib.gis
Es un sistema de información geográfico.
django.contrib.localflavour
Provee localización (l10n) e internacionalización (i18n).
La concepción de MVC de Django difiere ligeramente del enfoque tradicional. Se denomina vista al controlador y a la vista, plantilla (template); resultando en el acrónimo MTV. El controlador de MVC se presenta en Django como conjunto de funciones y asociaciones de URLs a ellas. Cada función de la vista delega la presentación de los datos al sistema de plantillas.
Django simplifica el patrón MVC, sin comprometer la separación de las capas. Debido a su creciente popularidad y a su sencillez, se seleccionó a Django como plataforma para la implementación del desarrollo de esta tesina, puesto que se buscan tales características en el trabajo a realizar.
Durante la instalación del framework en el sistema del desarrollador, se añade a la variable de sistema PATH un comando con el nombre django-admin.py. Mediante este comando se crean proyectos y se los administra.
Un proyecto es un paquete Python que contiene tres módulos:
settings.py
Configuración de la base de datos, directorios de plantillas, etc.
manage.py
Interfaz de consola para la ejecución de comandos.
urls.py
Mapeo de URLs en vistas (funciones).
El proyecto funciona como un contenedor de aplicaciones regidas bajo una misma configuración que comprende:
- Base de datos
- Templates
- Clases de middleware
El siguiente ejemplo muestra la creación de un proyecto:
$ django-admin.py startproject mi_proyecto # Crea el proyecto mi_proyecto
En este ejemplo, un listado jerárquico del sistema de archivos mostraría la siguiente estructura:
mi_proyecto/
__init__.py
settings.py
manage.py
urls.py
Se analiza a continuación la función de cada uno de los tres módulos de un proyecto.
El modulo settings es código fuente y configura globalmente al proyecto. Define las aplicaciones instaladas (INSTALLED_APPS), la base de datos que utilizarán (DATABASE_*), el módulo de URLs inicial (ROOT_URLCONF), la ruta en la cual se encuentran los medios estáticos y la URL en donde serán publicados [‡‡] (MEDIA_URL, MEDIA_ROOT). Otras configuraciones son: idioma, zona horaria, clases de middleware [§§], ruta a las plantillas, etc.
Es posible realizar configuraciones en tiempo de ejecución, tarea que no es factible utilizando lenguajes de marcado como XML, YAML, archivos INI, etc.
| [‡‡] | Imágenes, sonido, flash, etc. |
| [§§] | En Django una clase middleware conecta los componentes de MTV. El usuario puede definir un comportamiento personalizado. |
Este módulo a diferencia de settings y urls, es ejecutable. Sirve como interfaz con el framework. Su invocación recibe como primer argumento un nombre de comando, como startapp que crea una aplicación en el proyecto, o runserver que lanza el servidor de desarrollo o syncdb que, mediante el ORM, crea el esquema en la base de datos a partir del módulo models de cada aplicación.
Existen otros comandos que permiten realizar testing, iniciar la consola interactiva, administrar usuarios, etc.
Este módulo define las asociaciones entre las URLs y las vistas a nivel de proyecto. Dentro de éste, se puede derivar el tratado de ciertas URLs en módulos similares de aplicaciones instaladas en el proyecto.
Cuando el usuario realiza una solicitud, Django busca una asociación que concuerde con la URL dada. De encontrarla ejecuta la vista correspondiente a ésta.
La URL en las asociaciones se define mediante expresiones regulares que soportan recuperación de grupos nombrados (una subcadena identificada con un nombre). Los grupos nombrados definidos en cada asociación son argumentos de la función vista.
La asociación URL-vistas se define bajo el nombre urlpatterns. Por ejemplo, derivar el tratado de todo lo que comience con la cadena personas al módulo de urls de la aplicación personas:
from mi_proyecto.personas.views import ver_personas
urlpatterns = patterns('',
# Derivación en módulo de aplicación personas
(r'^personas', include('mi_proyecto.personas.urls')),
# Recuperación de datos de la url
(r'^personas/(?P<persona_id>\d{1,4}})', ver_persona),
)
El sistema de plantillas aparece en la última fase de la generación de la respuesta al cliente. Tiene como objetivo la separación del procesamiento de datos de la presentación. Una vez hallada la vista asociada a la URL de la solicitud, Django ejecuta la vista. Ésta, tras completar su procesamiento, deriva la presentación de los datos al template o plantilla.
Django incluye por defecto funciones cargadoras de plantillas [¶¶] que realizan la búsqueda de la plantilla que requiere la vista.
| [¶¶] | Definidas bajo TEMPLATE_LOADERS en el módulo settings. |
Con los datos provistos por la vista, la plantilla escogida es renderizada. Este proceso consiste en reemplazar etiquetas y ejecutar alguna lógica básica de iteración y bifurcaciones condicionales.
Los componentes que puede contener una plantilla son:
Cualquier texto encerrado por un par de llaves es una etiqueta de variable. Esto le indica al sistema de plantillas que debe reemplazarla por el contenido de la variable entregada por la vista.
La representación de una variable de etiqueta puede ser alterada mediante un filtro. El cual se define mediante el caracter |. Ej:
{{ fecha|date:"D d M Y" }} Si la variable fecha es un datetime, la salida es 'Wed 09 Jan 2008'.Cualquier texto que esté rodeado por llaves y signos de porcentaje es una etiqueta lógica o define algún bloque. Algunas etiquetas son if, for, extends, load, entre otros.
Es posible definir etiquetas de plantilla de usuario (o template tags), que actúan como una función en la plantilla.
Django posee un sistema jerárquico de plantillas que propone una estructura de herencia. Una plantilla base puede definir una serie de bloques, diseño general de una página, etc. Una plantilla particular puede utilizar la etiqueta de plantilla extends y redefinir uno o más bloques, conservando intacto todo lo que se encuentre fuera de los bloques redefinidos.
Cualquier otro texto pasa literalmente a la salida.
Si bien las plantillas suelen estar orientadas a la generación de HTML, es posible obtener otros formatos.
Normalmente los archivos de plantilla se ubican en el directorio templates en la raíz del proyecto. Puede incluirse también un directorio templates en cada aplicación del proyecto. Los TEMPLATE_LOADERS definidos en el módulo settings realizan la búsqueda cuando la vista lo requiera.
A continuación se presenta una simple plantilla de ejemplo:
<html>
<head><title>{{title}}</title></head>
<body>
<p>Hola {{ nombre }},</p>
</body>
</html>
Esta plantilla es un HTML básico con algunas variables y etiquetas agregadas. Si esta plantilla fuera “base.html”, una vista para la misma podría ser:
def mi_vista(request):
return render_to_response('base.html', {'nombre': 'pepe',
'titulo': 'Mi página bonita'})
La salida sería:
<html>
<head><title>Mi página bonita</title></head>
<body>
<p>Hola pepe,</p>
</body>
</html>
Una aplicación es un paquete Python que consta de dos módulos: models y views.
Para crear una aplicación se utiliza el comando startapp del módulo manage, de la siguiente manera:
$ python manage.py startapp mi_aplicacion # Crea la aplicación
El resultado de este comando genera la siguiente estructura en el proyecto:
mi_proyecto/
mi_aplicacion/
__init__.py
models.py
views.py
tests.py
Como antes se mencionó, puede crearse un módulo urls dentro de la aplicación para que delegue el tratado desde el urlpatterns raíz sobre urls de la aplicación.
Se analiza a continuación la función de cada uno de los dos módulos de una aplicación.
Al crear una aplicación se genera un módulo models en cual se definen las clases que extienden de django.db.models.Model. Estas definiciones son transformadas por el ORM en tablas relacionales [##].
Por ejemplo:
class Persona(models.Model):
nombre = models.CharField(max_length = 50)
apellido = models.CharField(max_length = 50)
| [##] | Mediante el comando syncdb del módulo manage del proyecto. |
Cada aplicación posee un módulo views, donde se definen las funciones que responden al cliente y se activan por medio del mapeo definido en el módulo urls del proyecto o de la aplicación.
Las funciones que trabajan como vistas deben recibir como primer parámetro el request y, opcionalmente, también reciben parámetros obtenidos de los grupos nombrados en la URL.
El siguiente ejemplo integra un fragmento de los módulos urls y views:
# Tras un mapeo como el siguiente
(r'^persona/(?P<id_persona>\d{1,4}})/$', mi_vista)
# la vista se define como
def mi_vista(request, id_persona):
persona = Personas.objects.get(id = id_persona)
datos = {'persona':persona, }
return render_to_response('plantilla.html', datos)
Django no se ocupa de generar contenido estático, sin embargo éste es necesario para el desarrollo completo de una aplicación web (imágenes, JavaScript y hojas de estilo). Delega esta tarea al servidor web [DjangoDoc2009], pero para el desarrollo se provee de una vista que devuelve contenido estático (django.views.static.serve).
Al momento de la puesta en producción del proyecto desarrollado en Django, se deberá realizar la configuración del soporte para ejecución de Python (en la presente tesis se optó por el estudio de WSGI ya que es un estándar totalmente definido en Python). En ese momento se reemplazará el uso de las vistas estáticas para ser delegadas al servidor web, tal como antes se mencionó.
Puesta en producción de un proyecto en Django
| [DjangoDoc2009] | Sirviendo archivos estáticos con Django, Django Wiki, http://docs.djangoproject.com/en/dev/howto/static-files/#the-big-fat-disclaimer |
Se describe a continuación de manera más detallada el ciclo de una petición en Django.
Durante este proceso existen clases que realizan tareas transversales, algunas de bajo y otras de alto nivel, como caché, sesión y autenticación, manejo de cabeceras HTTP, etc., que reciben el nombre de clases de middleware. Según qué métodos se implementen, pueden anteponerse o anexarse a una o más etapas del proceso del request.
El ciclo de una petición se completa mediante los siguientes pasos:
Entrada por middlewares de petición (Request)
El primer componente en tratar la solicitud es el conjunto de middlewares de request. Se encarga de envolver la petición en una instancia de la clase HttpRequest (django.middleware.common.CommonMiddleware), adicionar la sesión al request (django.contrib.sessions.middleware.SessionMiddleware) y relacionar la sesión y el usuario de la aplicación de autenticación (django.contrib.auth.middleware.AuthenticationMiddleware).
URLConf y middlewares de vistas (View Middlewares)
El siguiente paso es la búsqueda de la vista a partir de la URL de la solicitud (que se encuentra en el atributo path de la instancia de HttpRequest generada por el middleware de request) en los patrones de URLs asociados a las vistas. La constante ROOT_URLCONF (del módulo settings del proyecto) determina dónde comienza dicha búsqueda. Una vez obtenida la vista, los middlewares de vistas pueden posicionar su ejecución antes o después de ésta.
Un middleware de vista importante es el de transacciones. Previo a la ejecución de la vista, inicia una transacción. Al completarse de manera exitosa dicha vista, realiza un COMMIT. De existir algún error, cierra la transacción con un ROLLBACK.
Salida mediante middleware de respuesta (Response Middleware)
Finalmente, cuando la vista ha generado una respuesta, ésta es procesada por los middlewares de respuesta (Response Middlewares). Algunas tareas que se pueden realizar con este tipo de middlewares son: compresión de la salida, inclusión de librerías de JavaScript muy modulares, como YUI, etc.
Middlewares de excepciones (Exception Middleware)
Si se lanza una excepción que no es capturada en la vista, modelos o URLs, el middleware de excepciones la captura y genera una respuesta informando el error (cuando la bandera DEBUG del modulo settings está activada, se genera una traza del error).
Procesamiento de un request con la interacción de los middlewares.
Las clases de middleware son provistas por Django y se incluyen durante la generación del proyecto en el módulo settings. Una clase de middleware puede implementar uno o más métodos de los mostrados en la figura siguiente:
Interacción de los métodos de una clase de middleware en el proceso del request
Un modelo de Django es una descripción de alto nivel de la estructura de la base de datos. Esta descripción se realiza en lenguaje Python en el módulo models de cada aplicación, en vez de realizarse en SQL.
Si se cuenta con una base de datos preexistente, Django permite inferir la definición de los modelos, con el fin de adaptar el ORM. Dentro del módulo se define cada entidad como una clase que extiende a django.db.models.Model.
Aunque la entidad queda identificada con el nombre de clase, pueden existir colisiones entre nombres iguales en diferentes aplicaciones. Por esto, el nombre completo de las entidades se define como una cadena compuesta por el nombre de la aplicación y el nombre de la entidad unidos por un punto (Por ejemplo: "personal.Persona", "auth.User", "ventas.Producto", etc.).
Las tareas del ORM son:
Creación de tablas relacionales
Esta operación se activa mediante el comando syncdb del módulo manage del proyecto y se realiza mediante el DDL [♠♠] de SQL.
Se toma como nombre de tabla el nombre completo de entidad reemplazando el punto por un guión bajo. Si existiesen relaciones que requieran tablas intermedias, éstas serán creadas.
Por ejemplo, para la siguiente clase:
class Persona(models.Model): nombre = models.CharField(max_length = 50) apllido = models.CharField(max_length = 50)el ORM se encarga de generar el siguiente código SQL al momento de la ejecución del comando syncdb.
CREATE TABLE miapp_persona ( "id" serial NOT NULL PRIMARY KEY, "nombre" varchar(30) NOT NULL, "apellido" varchar(30) NOT NULL );CRUD
Creación, modificación y eliminación
La creación de entradas en la base de datos se realiza cuando una instancia de una clase del modelo recibe el mensaje save() (INSERT). Sobre una instancia existente, el mensaje save() actualiza la entidad con los valores (UPDATE). Cuando una instancia existente en la base recibe el mensaje delete() se elimina (DELETE).
Recuperación (administradores de consultas)
La recuperación de datos de una base de datos relacional se realiza típicamente mediante la sentencia SQL SELECT, pero los criterios de búsqueda pueden llegar a ser complejos, sobre todo cuando existen relaciones. Por esto, Django agrega un objeto (manager) que simplifica la tarea de recuperación de instancias. Existe un manager por cada entidad y cada relación que ésta posea.
Sistema de señales
El sistema de señales permite registrar funciones para ser ejecutadas antes o después de ciertos eventos, como la creación de instancias del modelo, la eliminación, o la creación del esquema (syncdb). Son equivalentes a los triggers en la base de datos.
Inclusión de metadatos
Existen ciertos metadatos que no pueden incluirse fácilmente en SQL de manera estándar, como el nombre visible para el usuario de una entidad, en plural y en singular, o la URL absoluta del elemento en el sistema. Esta información puede almacenarse mediante una clase interna Meta y métodos de instancia.
Herencia de objetos
Django permite crear jerarquías de herencia en los modelos.
Relaciones genéricas
Si bien en los RDBMS se requiere especificar el tipo de los campos relacionados (tipado estático fuerte) al momento de definir una relación, mediante el field GenericRelation (provisto por la aplicación genérica ContentType [DjangoDocsContentType2009]), se pueden realizar relaciones contra instancias de cualquier tipo.
| [DjangoDocsContentType2009] | Framework de ConentType, Documentación de Django 1.0, último acceso Septiembre de 2009, http://docs.djangoproject.com/en/dev/ref/models/instances/#the-pk-property |
| [♠♠] | Data Definition Language son las sentencias encargadas de la definición de la estructura, la componen las sentencias CREATE, ALTER y DROP. |
| [♥♥] | Data Manipulation Language son las sentencias encargadas de la creación, recuperación, modificación. La compoenen las sentencias INSERT, UPDATE, DELETE. |
Los modelos en Django poseen un sólo campo clave. Si ningún campo es definido como clave, con el argumento primary_key = True, se agrega de manera automática un campo id de tipo entero auto incremental [DjangoDocsModelsKey2009].
| [DjangoDocsModelsKey2009] | Definición de claves foráneas, Documentación de Django 1.0, último acceso Septiembre 2009, http://docs.djangoproject.com/en/dev/ref/models/instances/#the-pk-property |
El atributo pk es un alias del campo id o del campo que se definió como clave primaria.
Django no soporta claves compuestas, pero permite definir combinaciones únicas de campos para suplir esta carencia. Dentro de la clase de metadatos Meta se pueden definir campos unique y unique_together, por ejemplo:
class Persona(models.Model):
''' Clase persona '''
nombre = models.CharField(max_length = 30)
apellido = models.CharField(max_length = 30)
class Meta:
unique_together = ('nombre', 'apellido', )
Uno de los comandos provistos por el módulo manage es shell, que invoca el intérprete de Python de manera interactiva agregando al PYTHONPATH el proyecto. De esta manera se pueden importar las aplicaciones, vistas, y modelos.
El siguiente listado ejemplifica la importación de la clase Persona desde la aplicación mi_aplicacion:
>>> from mi_aplicacion.models import Persona
>>> p1 = Persona(nombre='Pablo', apellido='Perez')
>>> p1.save()
>>> personas = Persona.objects.all()
El ejemplo anterior permite observar los siguientes puntos:
- Para crear un objeto se importa la clase del modelo apropiada y se crea una instancia asignando valores para cada campo.
- Para guardar el objeto en la base de datos se usa el método save().
- Para recuperar objetos de la base de datos se usa Persona.objects, que constituye el manager de la entidad.
Los managers se encargan de realizar las querys sobre las entidades de la base de datos. Presentan una API para realizar las consultas y devuelven instancias de objetos QuerySet. Un QuerySet representa una consulta perezosa, es decir, la consulta se hace efectiva cuando es necesario acceder a los datos [♥♥].
| [♦♦] | Generalmente esto ocurre en la iteración en el template. |
Dado el siguiente ejemplo:
from django.db import models
class Persona(models.Model):
nombre = models.CharField(max_length = 30)
apellido = models.CharField(max_length = 30)
class Vehiculo(models.Model):
marca = models.CharField(max_length = 4)
modelo = models.DateField()
propietario = models.ForeignKey(Persona)
Los managers están presentes en:
El atributo objects de cada entidad del modelo.
El programador puede definir nuevos managers para consultas frecuentes.
Por ejemplo:
Persona.objects.all() # Recupera todas las personas Vehiculo.objects.all() # Recupera todos los vehículosCada relación, ya sea 1 a N o N a N.
Por ejemplo:
p = Persona.obects.get( nombre = "nahuel" ) Vehiculo.objects.filter( propietario = p ) # O lo que es lo mismo Persona.obects.get( nombre = "nahuel" ).vehiculo_set() # Relación inversa resuelta por Django
La API provista por los managers consiste en dos grupos de métodos: los que retornan instancias de QuerySet y los que no.
A continuación se listan los métodos que retornan QuerySet:
filter( **argumentos ) [♦♦]
Sólo incluye las instancias que cumplen con el criterio definido en argumentos.
exclude( **argumentos )
Excluye elementos que cumplan con el criterio definido en argumentos.
order_by(*campos)
Ordena el resultado por el/los campos campos.
distinct()
Sólo admite una instancia de cada elemento.
values(*campos)
Retorna sólo los campos campos como un diccionario.
dates(campo, tipo, order='ASC')
Retorna fechas.
none()
Evalúa a QuerySet vacío.
select_related()
Preselecciona en la consulta los datos relacionados para evitar el sobrecarga de múltiples consultas sobre QuerySets grandes y con muchas relaciones.
extra(select=None, where=None, params=None, tables=None)
Permite realizar una consulta de bajo nivel.
| [♣♣] | El doble asterisco (**) representa argumentos del tipo clave = valor, mientras que el asterisco simple (*), define que son argumentos variables. |
El conjunto de métodos que no devuelven QuerySet es:
get(**campos)
Obtiene una única entidad identificada por campos.
create(**campos)
Crea una entidad basada con los valores campos.
get_or_create(**kwargs)
Crea una entidad basada con los valores campos o la modifica si existe.
count()
Retorna la cantidad de elementos del QuerySet.
in_bulk(lista_de_identificadores)
Retorna un diccionario donde la clave es cada identificador y el valor es la instancia.
latest(campo = None)
Retorna la instancia más reciente comparando por el campo campo. Debe ser del tipo DateField.
Los managers que retornan QuerySet, se pueden encadenar, por ejemplo:
>>> # Ejemplo para selección de libros impresos entre el 2000 y 2005 que
>>> # no sean de la editorial APress
>>> Book.objects.filter(publish__gte = '1/1/2000',
published__lte = '1/1/2006').exclude(publisher = 'APress')
El elemento de entrada tradicional de las aplicaciones web está constituido por los formularios. En un sistema de información, la forma de realizar las operaciones CRUD es a través de éstos.
Un formulario es una entidad que posee un conjunto de campos, cada uno de los cuales tiene la responsabilidad de validar los propios datos. A posteriori el formulario realiza una comprobación de la coherencia global, tras la cual se obtiene el resultado de la validación.
Mediante los atributos widget de los campos que componen el formulario, éste puede de renderizarse como HTML.
El ciclo de trabajo normal para la entrada de datos es:
El usuario:
- Carga una página en la cual existe un formulario.
- Completa los campos (pueden existir campos opcionales).
- Envía el formulario, utilizando típicamente un botón del navegador, el cual se encarga de codificar los datos y enviarlos a la URL indicada en el atributo action del tag de definición del formulario.
El servidor:
Recibe los datos y los valida. Si los datos son correctos, generalmente envía una respuesta HTTP 300 indicando que la carga ha sido exitosa. Si la validación es incorrecta devuelve al usuario el formulario con los datos que hayan sido válidos, y se muestran los mensajes pertinentes de los errores que hayan ocurrido, volviendo al paso 2 del usuario.
La validación de los formularios puede ser una tarea compleja. Django provee un mecanismo para generar formularios en el módulo django.forms que alivia la tarea.
En la siguiente figura se muestra el diagrama de clases del paquete django.forms:
Diagrama de clases de un formulario en Django