En el capítulo 3 vimos los fundamentos de la creación de páginas Web dinámicas usando Django: creación de vistas y URLconf. Como explicamos entonces, una vista es responsable de computar cosas arbitrarias para luego devolver una respuesta. En el ejemplo, nuestro objetivo era calcular la fecha y hora actuales.
En las aplicaciones Web modernas, la lógica de negocio suele implicar la interacción con una base de datos. Entre bastidores, un sitio Web apoyado en una base de datos conecta a un servidor, recupera algunos datos de él y los muestra, con un bonito formato, en una página Web. De forma similar, un sitio web podría proporcionar la funcionalidad que permita a los visitantes poblar la base de datos ellos mismos.
Muchos sitios Web complejos proporcionan alguna combinación de ambas. Amazon.com, por nombrar uno, es un gran ejemplo de sitio web apoyado en una base de datos. Cada página de productos es esencialmente un extracto de la base de datos de productos de Amazon al que se le ha dado formato HTML, y cuando un cliente envía una reseña, se inserta en la base de datos de reseñas.
Django está muy buen preparado para hacer sitios Web que trabajen con bases de datos, ya que incorpora maneras sencillas pero potentes de realizar consultas a base de datos usando Python. Este capítulo explica esa funcionalidad (la capa de base de datos de Django)
(Nota: aunque no es estrictamente necesario conocer la teoría básica de bases de datos y SQL para poder usar la capa de base de datos de Django, sí es muy recomendable. Realizar una introducción a estos conceptos escaparía al ámbito de este libro, pero siga leyendo incluso si es novato en esto de las bases de datos. Probablemente será capaz de seguirnos y entender conceptos basándose en el contexto).
Así como en el capítulo anterior detallábamos una manera "simple" de producir HTML desde una vista (escribiendo HTML directamente dentro de la vista), hay una manera "simple" de recabar información de una base de datos desde una vista. Es sencillo: limítese a usar un módulo cualquiera de entre los que existe en Python para ejecutar una consulta SQL y hacer algo con el resultado.
En esta vista de ejemplo, usamos la biblioteca MySQLdb (disponible en http://sourceforge.net/projects/mysql-python) para conectarse a una base de datos MySQL, recuperar algunos registros y pasárselos a una plantilla para mostrarlos en una página Web:
from django.shortcuts import render_to_response
import MySQLdb
def book_list(request):
db = MySQLdb.connect(user='me', db='mydb', passwd='secret', host='localhost')
cursor = db.cursor()
cursor.execute('SELECT name FROM libros ORDER BY name')
names = [row[0] for row in cursor.fetchall()]
db.close()
return render_to_response('book_list.html', {'names': names})
Este enfoque funciona, pero podrían asaltarle inmediatamente algunos problemas:
Como podría esperarse, la capa de base de datos de Django pretende resolver estos problemas. Aquí tiene un avance de la manera en que reescribiríamos la vista anterior usando el API de bases de datos de Django:
from django.shortcuts import render_to_response
from misitio.libros.models import Book
def book_list(request):
libros = Book.objects.order_by('name')
return render_to_response('book_list.html', {'libros': libros})
Explicaremos este código un poco más adelante. Por ahora, basta con que se haga una idea de cómo sería.
Antes de meternos en más código, paremos un momento para tomar en consideración el diseño general de una aplicación de Django diseñada alrededor de una base de datos.
Como hemos mencionado en en capítulos anteriores, Django está diseñado para fomentar un bajo acoplamiento y una separación estricta entre las partes de una aplicación. Si sigue esta filosofía es sencillo realizar cambios a una parte en particular sin afectar al resto de la aplicación. Por ejemplo, sobre las funciones de vista resaltamos la importancia de separar la lógica de negocio de la de presentación usando un sistema de plantillas. Con la capa de base de datos estamos aplicando la misma filosofía a la lógica de acceso a datos.
Estas tres partes juntas (lógica de acceso a datos, de negocio y de presentación) comprenden un concepto que a veces se denomina patrón "Modelo Vista Controlador" (MVC) de arquitectura de software. En este patrón el "Modelo" se refiere a la capa de acceso a datos, la "Vista" se refiere a la parte del sistema que escoge qué mostrar y cómo hacerlo y el "Controlador" sería la parte del sistema que decide qué vista usar, dependiendo de la entrada del usuario y accediendo al modelo según sea necesario.
¿Por qué el acrónimo?
¿MVC? ¿MTV? ¿Qué sentido tienen estos términos?
El objetivo de definir patrones específicos tales como MVC es principalmente hacer más fluida la comunicación entre desarrolladores. En lugar de tener que decirle a sus compañeros de trabajo, "hagamos una abstracción del acceso a datos, hagamos luego una capa separada que se encargue de mostrar los datos y pongamos una capa en el medio que lo regule", podemos sacar provecho de un vocabulario común y decir "usemos aquí el patrón MVC".
Django sigue este patrón MVC tan de cerca que podría llamársele un framework MVC. A grandes rasgos, ésta es la manera en que se dividen la M, la V y la C en Django:
Ya que la "C" la maneja el propio framework y la mayoría de lo excitante de Django sucede en los modelos, plantillas y vistas, a Django se le ha definido como framework MTV. En el patrón de desarrollo MTV,
Si está familiarizado con otros framework MVC para desarrollo Web, como por ejemplo Ruby on Rails, puede considerar las vistas de Django como los "ccontroladores" y las plantillas como las "vistas". Esta desafortunada confusión surge de interpretaciones diferentes sobre MVC. Según la interpretación de Django del MVC, la "vista" describe los datos que serán presentados al usuario; no necesariamente el aspecto de los datos, sino qué datos se presentan. Por contraste, Ruby on Rails y otros framework similares sugieren que el trabajo del controlador incluye decidir sobre los datos ques serán presentados al usuario, mientras que la vista es estrictamente la manera en que se ven los datos, y no cuáles de ellos se han de presentar.
Ninguna de estas interpretaciones es más "correcta" que la otra. Lo importante es entender el concepto subyacente.
Con toda esta filosofía en mente, empecemos a explorar la capa de base de datos de Django. Primero necesitamos ocuparnos de cierta configuración inicial: debemos decirle a Django qué servidor de base de datos ha de usar y cómo conectarse a él.
Asumiremos que ya ha puesto en marcha, activado y creado una base de datos en el servidor correspondiente (p.ej. usando una sentencia CREATE DATABASE). SQLite es especial; en su caso no hay que crear una base de datos ya que SQLite almacena sus datos en ficheros a los que se accede directamente.
Al igual que TEMPLATE_DIRS en el capítulo anterior, la configuración de la base de datos reside en el fichero de configuración de Django, llamado settings.py por omisión. Edite este fichero y busque las opciones de base de datos:
DATABASE_ENGINE = '' DATABASE_NAME = '' DATABASE_USER = '' DATABASE_PASSWORD = '' DATABASE_HOST = '' DATABASE_PORT = ''
Descripción de cada opción:
DATABASE_ENGINE le dice a Django qué motor de base de datos ha de usar. DATABASE_ENGINE debe ser igual a una de las siguientes cadenas:
Opción |
Motor |
Adaptador necesario |
|---|---|---|
postgresql |
PostgreSQL |
psycopg versión 1.x, http://initd.org/projects/psycopg1 |
postgresql_psycopg2 |
PostgreSQL |
psycopg versión 2.x, http://initd.org/projects/psycopg2 |
mysql |
MySQL |
|
sqlite3 |
SQLite |
No hace falta adaptador para Python 2.5+. En caso contrario, pysqlite, http://initd.org/tracker/pysqlite |
ado_mssql |
Microsoft SQL Server |
adodbapi versión 2.0.1+, http://adodbapi.sourceforge.net/ |
oracle |
Oracle |
Observe que sea cual sea el motor de bases de datos que vaya a usar, necesitará descargar e instalar el adaptador apropiado. Todos están disponibles de forma gratuita en la Web.
DATABASE_NAME le indica a Django el nombre de la base de datos. Si está usando SQLite ha de especificar la ruta completa hasta el fichero con la base de datos, p.ej. '/home/django/mydata.db'
DATABASE_USER le dice a Django el nombre de usuario que ha de usar al conectar a la base de datos. Si usa SQLite, deje esto en blanco.
DATABASE_PASSWORD le dice a Django la clave a usar cuando se conecte a la base de datos. Si usa SQLite o no necesita clave, déjelo en blanco.
DATABASE_HOST le indica a Django la máquina donde conectar con la base de datos. Si se encuentra en el mismo computador donde está instalado el proyecto (o sea, localhost), o está usando SQLite, déjelo en blanco.
MySQL es un caso especial. Si el valor empieza con una barra ('/') y estamos usando MySQL, conectaremos al servidor usando el socket tipo Unix especificado. Por ejemplo:
DATABASE_HOST = '/var/run/mysql'
Si usa MySQL y este valor no comienza con una barra, entonces se asume que es la dirección de una máquina.
DATABASE_PORT le dice a Django el puerto donde escucha la base de datos. Si usa SQLite, déjelo en blanco. En caso contrario, si lo deja en blanco el adaptador usará el puerto por omisión para el tipo concreto de servidor. En la mayoría de los casos basta con el puerto por omisión, así que podemos dejarlo en blanco.
Una vez hayamos introducido todos estos datos, probaremos la configuración. Primero, ejecute python manage.py shell desde el directorio de proyecto misitio que creamos en el Capítulo 2.
Verá que esto inicia el intérprete interactivo de Python. Sin embargo, ¡las apariencias engañan! Hay una diferencia importante entre ejecutar python manage.py shell desde dentro de un directorio de proyecto de Django y el python a secas. Lo segundo es el intérprete básico de Python, mientras que el primero le dice a Django qué fichero de configuración tiene que usar antes de iniciar la sesión. Esto es un requisito clave para realizar consultas a bases de datos: Django necesita saber qué fichero de configuración usar para obtener la información de conexión a la base de datos.
python manage.py ajusta la variable de entorno DJANGO_SETTINGS_MODULE. Más adelante comentaremos las sutilezas de esto, pero por ahora basta que sepa que debería usar python manage.py shell siempre que quiera usar el intérprete de Python para hacer cosas con Django.
Una vez iniciada la sesión, escriba estas órdenes para probar la configuración de la base de datos:
>>> from django.db import connection >>> cursor = connection.cursor()
Si no pasa nada, significa que la base de datos está configurada correctamente. En caso contrario, examine el error en busca de pistas sobre el problema. Aquí tiene algunos de los habituales:
Mensaje de error Solución You haven’t set the DATABASE_ENGINE setting yet. Ponga en DATABASE_ENGINE algo diferente a una cadena vacía. Environment variable DJANGO_SETTINGS_MODULE is undefined. Ejecute python manage.py shell en lugar de python. Error loading _____ module: No module named _____. No ha instalado el adaptador apropiado (p. ej. psycopg o MySQLdb). _____ isn’t an available database backend. Ponga en DATABASE_ENGINE uno de las configuraciones válidas descritas antes. ¿Quizá se equivocó al escribir? database _____ does not exist Cambie DATABASE_NAME para que contenga el nombre de una base de datos que exista, o ejecute la sentencia CREATE DATABASE apropiada. role _____ does not exist Cambie DATABASE_USER para que contenta uno que exista o créelo en la base de datos. could not connect to server Asegúrese de que DATABASE_HOST y DATABASE_PORT contiene valores correctos, y de que el servidor de bases de datos está en marcha.
Ahora que hemos comprobado que la conexión funciona, es el momento de crear una aplicación de Django: código de Django, incluyendo modelos y vistas, que reside en un único paquete de Python y representa una aplicación completa.
Merece la pena explicar aquí la terminología, porque tiende a confundir a los principiantes. En el Capítulo 2 ya creamos un proyecto, así que, ¿cual es la diferencia entre un proyecto y una aplicación? Es una diferencia de configuración frente a código:
Un proyecto es una instancia de un cierto conjunto de aplicaciones de Django, más la configuración de esas aplicaciones.
Técnicamente, el único requisito de un proyecto es proporcionar un fichero de configuración, que defina la información de conexión a la base de datos, la lista de aplicaciones instaladas, los TEMPLATE_DIRS, etc.
Una aplicación es un conjunto portable de funcionalidad de Django, que suele incluir modelos y vistas, que están juntos en un único paquete de Python.
Por ejemplo, Django incorpora una serie de aplicaciones, tales como un sistema de comentarios y una interfaz automática de administración. Una cosa importante sobre estas aplicaciones es que son portables y reutilizables entre varios proyectos.
Hay muy pocas reglas estrictas sobre cómo amoldar su código en este esquema, ya que es flexible. Si estamos creando un sitio Web sencillo puede que usemos una única aplicación. Si estamos creando un sitio Web complejo con varias partes poco relacionadas entre sí como un sistema de comercio electrónico y un tablón de mensajes, probablemene querramos dividirlos en aplicaciones separadas de manera que podamos reutilizarlos individualmente en el futuro.
De hecho, no tiene por qué crear aplicaciones necesariamente, como evidencian las funciones de vista de ejemplo que hemos creado hasta ahora. En esos casos, sencillamente creamos un fichero llamado views.py, lo llenamos con funciones de vista y enlazamos nuestra URLconf a esas funciones. No hicieron falta "aplicaciones2.
Sin embargo, hay un requisito que concierne a la convención de las aplicaciones: si estamos usando la capa de acceso a base de datos de Django (modelos), debemos crear una aplicación. Los modelos deben residir dentro de aplicaciones. Por tanto, para poder empezar a escribir nuestros modelos tendremos que crear una nueva aplicación.
Para crear una nueva aplicación, escriba esta orden dentro del directorio de proyecto misitio que creamos en el Capítulo 2:
python manage.py startapp libros
(¿Por qué libros? Ésta es la aplicación de ejemplo para libros que vamos a construir juntos.)
Esta orden no genera ninguna salida, pero habrá creado un directorio libros dentro del directorio misitio. Veamos el contenido de ese directorio:
libros/
__init__.py
models.py
views.py
Estos ficheros contendrán los modelos y vistas de esta aplicación.
Eche un vistazo a models.py y views.py en su editor de texto favorito. Ambos ficheros están vacíos, excepto por un import en models.py. Ésta es la página en blanco para su aplicación.
Como dijimos anteriormente, la "M" de "MTV" viene de "Modelo". En Django un modelo es una descripción del contenido de la base de datos, representado como código en Python. Refleja la estructura de sus datos (el equivalente a las sentencias SQL CREATE TABLE), excepto que está escrito en Python en lugar de en SQL, e incluye más cosas que la mera definición de la base de datos. Django usa un modelo para ejecutar código SQL entre bastidores y devolver estructuras de datos de Python que representan las filas de las tablas en la base de datos. Django usa los modelos también para representar conceptos de nivel más alto de los que SQL no tiene por qué ocuparse.
Si está familiarizado con las bases de datos, su primera idea podría ser: "¿no es redundante definir los modelos de datos en Python y SQL?" Django funciona de esta manera por varias razones:
La introspección tiene un coste y es imperfecta.
Para poder proporcionar API convenientes para el acceso a los datos, Django necesita conocer de alguna manera la estructura de la base de datos, y hay dos maneras de conseguirlo. La primera forma sería describir explícitamente los datos usando Python y la segunda manera sería usar las capacidades de introspección de la base de datos para determinar el modelo.
La segunda manera parece más limpia, debido a que los metadatos sobre las tablas estarán en un solo lugar, pero introduce una serie de problemas. Primero, realizar la introspección sobre la base de datos durante la ejecución resulta en retardos. Si el framework tuviese que realizar esta operación cada vez que procesase una consulta o incluso al reiniciar el servidor Web, incurriría en retrasos inaceptables. (Hay gente que considera aceptable ese nivel de retardo, pero los desarrolladores de Django intentan eliminar la mayor cantidad posible de sobrecarga en el framework, y este enfoque ha conseguido hacer a Django más rápido que el resto de su competencia en frameworks de alto nivel.) Segundo, hay bases de datos, en especial las versiones antiguas de MySQL, que no almacenan metadatos suficientes para obtener un modelo completo y preciso.
Escribir Python es divertido y mantenernos dentro de los límites de Python reduce el número de veces que nuestro cerebro tiene que hacer un "cambio de contexto". Mantenerse en un único entorno/mentalidad de programación el mayor tiempo posible ayuda en la productividad. Tener que escribir SQl, luego Python y SQL de nuevo es molesto.
Almacenar los modelos de datos como código en lugar de en la base de datos hace más sencillo mantenerlos bajo control. De esta manera, se puede llevar el rastro de los cambios en la estructura de datos fácilmente.
SQL sólo permite introducir metadatos sobre la estructura de datos hasta un cierto nivel. Por ejemplo, la mayoría de los sistemas de bases de datos no proporciona un tipo especialiado para representar direcciones de correo electrónico o URL. Los modelos de Django sí. La ventaja de los tipos de datos de alto nivel está en un incremento de la productividad y en un código más reutilizable.
SQL es inconsistente entre plataformas. Si distribuimos una aplicación Web, por ejemplo, es mucho más pragmático distribuir un módulo de Python que describe la estructura de nuestros datos en lugar de sentencias CREATE TABLE para MySQL, PostgreSQL y SQLite.
Sin emgargo, una desventaja de este enfoque es que posibilita que el código en Python se desincronice con lo que hay realmente en la base de datos. Si hace cambios a un modelo de Django, necesitará hacer los mismos cambios en la base de datos para mantenerla consistente con el modelo. Más adelante entraremos en detalle de estrategias para tratar este problema.
Por último, debemos indicar que Django incluye una utilidad que puede generar modelos mediante introspección de una base de datos existente. Esto es útil para ponerse a trabajar rápidamente con datos ya existentes.
Para el ejemplo en el que vamos a trabajar en éste capítulo y el que sigue, nos centraremos en una estructura de datos básica de libro/autor/editorial. Lo usaremos como ejemplo porque las relaciones conceptuales entre libros, autores y editoriales son bien conocidas y es un ejemplo común en libros de introducción a SQL. Además, ¡usted está leyendo un libro, escrito por autores y publicado por una editorial!
Supondremos los siguientes conceptos, campos y relaciones:
El primer paso para usar esta estructura de base de datos con Django es expresarlo como código en Python. Escriba lo siguiente en el fichero models.py que creó la orden startapp:
from django.db import models
class Editorial(models.Model):
nombre = models.CharField(maxlength=30)
direccion = models.CharField(maxlength=50)
ciudad = models.CharField(maxlength=60)
estado_provincia = models.CharField(maxlength=30)
pais = models.CharField(maxlength=50)
sitioweb = models.URLField()
class Autor(models.Model):
tratamiento = models.CharField(maxlength=10)
nombre = models.CharField(maxlength=30)
apellidos = models.CharField(maxlength=40)
email = models.EmailField()
foto = models.ImageField(upload_to='/tmp')
class Libro(models.Model):
titulo = models.CharField(maxlength=100)
autores = models.ManyToManyField(Autor)
editorial = models.ForeignKey(Editorial)
fecha_publicacion = models.DateField()
Nos ocuparemos de la sintaxis y opciones de los modelos a lo largo de este capítulo, pero examinemos rápidamente este código para cubrir lo básico. La primer cosa que observará es que cada modelo lo representamos con una clase de Python que es subclase de django.db.models.Model. La clase de la que desciende, Model, contiene toda la maquinaria necesaria para hacer que estos objetos sean capaces de interactuar con una base de datos (y es hace que nuestros modelos sólo sean responsables de definir sus campos, usando una sintaxis compacta y agradable). Lo crea o no, éste es todo el código que necesitamos escribir para tener un acceso básico a datos con Django.
Cada modelo se corresponde por lo general con una sola tabla en la base de datos, y cada atributo de un modelo se suele corresponder con una columna en la tabla. El nombre del atributo corresponde al de la columna, y el tipo del campo (p.ej. CharField) corresponde al tipo de la columna (p.ej. varchar). Por ejemplo, el modelo Editorial es equivalente a la siguiente tabla (asumiendo la sintaxis de CREATE TABLE de PostgreSQL):
CREATE TABLE "libros_editorial" (
"id" serial NOT NULL PRIMARY KEY,
"nombre" varchar(30) NOT NULL,
"direccion" varchar(50) NOT NULL,
"ciudad" varchar(60) NOT NULL,
"estado_provincia" varchar(30) NOT NULL,
"pais" varchar(50) NOT NULL,
"sitioweb" varchar(200) NOT NULL
);
De hecho, Django puede generar esa sentencia CREATE TABLE por sí mismo, como veremos en un momento.
La excepción a la regla "una clase por tabla" es el caso de las relaciones muchos a muchos. En nuestros modelos de ejemplo, Libro tiene un ManyToManyField llamado autores. Esto indica que un libro tiene uno o más autores, pero la tabla en la base de datos para Libro no tiene una columna autores. En su lugar, Django crea una tabla adicional (una "tabla de unión" muchos a muchos) que se encarga de relacionar los libros con los autores.
Por último, observe que no hemos definido de forma explícita una clave primaria en ninguno de estos modelos. A menos que le indiquemos lo contrario, Django le da automáticamente a cada modelo como clave primaria un campo entero llamado id. Cada modelo de Django ha de tener una clave primaria formada por una única columna.
Hemos escrito el código. Ahora, vamos a crear las tablas en la base de datos. Para poder hacer esto, el primer paso es activar esos modelos en nuestro proyecto de Django. Lo hacemos añadiendo esta aplicación libros en la lista de aplicaciones instaladas del fichero de configuración.
Edite de nuevo settings.py y busque la opción INSTALLED_APPS. Ésta le dice a Django qué aplicaciones están activas para un proyecto dado. Verá algo así:
INSTALLED_APPS = (
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.sites',
)
Comente temporalmente esas cuatro cadenas poniendo un caracter sostenido (#) delante de ellas. (Están incluidas por conveniencia dado que se usan a menudo, pero las activaremos y hablaremos luego de ellas.) Ahora, añada 'misitio.libros' a la lista INSTALLED_APPS, de manera que termine con un aspecto parecido a éste:
INSTALLED_APPS = (
#'django.contrib.auth',
#'django.contrib.contenttypes',
#'django.contrib.sessions',
#'django.contrib.sites',
'misitio.libros',
)
(Como estamos tratando con una tupla de un único elemento, no se olvide de la coma al final de la cadena. Por cierto, los autores de este libro prefieren poner una coma al final de cada elemento de una tupla, sea de un único elemento o no. Esto evita el problema de olvidar las comas, y no hay problema por usar esta coma extra.)
'misitio.libros' se refiere a la aplicación libros en la que estamos trabajando. Cada aplicación de INSTALLED_APPS queda representada por su ruta de Python completa (o sea, la ruta de paquetes, separados por puntos, que lleva hasta el de la aplicación).
Ahora que hemos activiado la aplicación en el fichero de configuración, podemos crear las tablas en la base de datos. Primero validaremos los modelos ejecutando esta orden:
python manage.py validate
La orden validate comprueba si la sintaxis y la lógica de los modelos es correcta. Si todo está bien, verá el mensaje 0 errors found. Si no, asegúrese de haber escrito correctamente el código del modelo. La salida de errores debería proporcionarle información útil sobre lo que va mal en el código.
Siempre que piense que pueda tener problemas con sus modelos, ejecute python manage.py validate. Suele encontrar todos los problemas comunes en los modelos.
Si los modelos son válidos, ejecute la siguiente orden para que Django genere las sentencias CREATE TABLE para los modelos de la aplicación libros (mostrando la sintaxis en colores si está usando Unix):
python manage.py sqlall libros
En esta orden, libros es el nombre de la aplicación. Es el que especificamos cuando ejecutamos manage.py startapp. Al ejecutarse podremos ver algo como lo que sigue:
BEGIN;
CREATE TABLE "libros_editorial" (
"id" serial NOT NULL PRIMARY KEY,
"nombre" varchar(30) NOT NULL,
"direccion" varchar(50) NOT NULL,
"ciudad" varchar(60) NOT NULL,
"estado_provincia" varchar(30) NOT NULL,
"pais" varchar(50) NOT NULL,
"sitioweb" varchar(200) NOT NULL
);
CREATE TABLE "libros_libro" (
"id" serial NOT NULL PRIMARY KEY,
"titulo" varchar(100) NOT NULL,
"editorial_id" integer NOT NULL REFERENCES "libros_editorial" ("id"),
"fecha_publicacion" date NOT NULL
);
CREATE TABLE "libros_autor" (
"id" serial NOT NULL PRIMARY KEY,
"tratamiento" varchar(10) NOT NULL,
"nombre" varchar(30) NOT NULL,
"apellidos" varchar(40) NOT NULL,
"email" varchar(75) NOT NULL,
"foto" varchar(100) NOT NULL
);
CREATE TABLE "libros_libro_autores" (
"id" serial NOT NULL PRIMARY KEY,
"libro_id" integer NOT NULL REFERENCES "libros_libro" ("id"),
"autor_id" integer NOT NULL REFERENCES "libros_autor" ("id"),
UNIQUE ("libro_id", "autor_id")
);
CREATE INDEX libros_libro_editorial_id ON "libros_libro" ("editorial_id");
COMMIT;
Observe lo siguiente:
La orden sqlall no llega realmente a crear las tablas o a tocar la base de datos de ninguna otra manera; se limita a imprimir la salida por pantalla de manera que pueda ver exactamente lo que Django ejecutaría si se lo pidiese. Si lo desea, puede copiar y pegar este SQL en el cliente que esté usando para su base de datos, o utilizar tuberías de Unix para pasarlo directamente. Sin embargo, Django proporciona una manera más sencilla de enviar el SQL a la base de datos. Ejecute la orden syncdb, de esta manera:
python manage.py syncdb
Verá algo así:
Creating table libros_editorial Creating table libros_libro Creating table libros-autor Installing index for libros.Libro model
La orden syncdb realiza una "sincronización" sencilla entre sus modelos y la base de datos. Comprueba todos los modelos de cada aplicación en la opción INSTALLED_APPS, comprueba en la base de datos si existen las tablas apropiadas, y crea las tablas si aún no existen. Tenga en cuenta que syncdb no sincroniza cambios o borrados en los modelos ya existentes; si hace un cambio en un modelo o lo borra, y quiere actualizar la base de datos, syncdb no lo va a hacer por usted (luego hablaremos más sobre esto).
Si ejecuta python manage.py syncdb no ocurrirá nada, dado que no ha añadido ningún modelo a la aplicación libros, ni ninguna aplicación a INSTALLED_APPS. Por tanto, no hay peligro en ejecutar python manage.py syncdb (no romperá nada).
Si tiene interés, permítase perder un momento en el cliente de línea de órdenes de su base de datos y vea las tablas que ha creado Django para usted. Puede ejecutar el cliente directamente (p. ej. psql para PostgreSQL) o la orden python manage.py dbshell, que adivinará la orden adecuada dependiendo del contenido de DATABASE_SERVER. Lo último suele ser lo más cómodo.
Una vez haya creado un modelo, Django le proporciona automáticamente un API de alto nivel para poder trabajar con los modelos desde Python. Pruébela ejecutando python manage.py shell y escribiendo lo siguiente:
>>> from libros.models import Editorial >>> p = Editorial(nombre='Apress', direccion='2560 Ninth St.', ... ciudad='Berkeley', estado_provincia='CA', pais='U.S.A.', ... sitioweb='http://www.apress.com/') >>> p.save() >>> p = Editorial(nombre="O'Reilly", direccion='10 Fawcett St.', ... ciudad='Cambridge', estado_provincia='MA', pais='U.S.A.', ... sitioweb='http://www.oreilly.com/') >>> p.save() >>> lista_editoriales = Editorial.objects.all() >>> lista_editoriales [<Editorial: Editorial object>, <Editorial: Editorial object>]
En unas pocas líneas de código hemos conseguido bastante. Lo más importante:
Naturalmente, se pueden hacer muchas más cosas con el API de bases de datos de Django, pero antes, ocupémonos de una pequeña molestia.
Antes, cuando imprimimos la lista de editoriales, todo lo que obtuvimos fue este resultado de poca utilidad que nos hace difícil distinguir un objeto Editorial de otro:
[<Editorial: Editorial object>, <Editorial: Editorial object>]
Podemos corregir esto fácilmente añadiendo a nuestro objeto Editorial un método llamado __str__(). Un método __str__() le dice a Python cómo debe mostrar la representación "textual" de un objeto. Podrá ver esto en acción añadiendo un método __str__() a los tres modelos:
class Editorial(models.Model):
nombre = models.CharField(maxlength=30)
direccion = models.CharField(maxlength=50)
ciudad = models.CharField(maxlength=60)
estado_provincia = models.CharField(maxlength=30)
pais = models.CharField(maxlength=50)
sitioweb = models.URLField()
def __str__(self):
return self.nombre
class Autor(models.Model):
tratamiento = models.CharField(maxlength=10)
nombre = models.CharField(maxlength=30)
apellidos = models.CharField(maxlength=40)
email = models.EmailField()
foto = models.ImageField(upload_to='/tmp')
def __str__(self):
return '%s %s' % (self.nombre, self.apellidos)
class Libro(models.Model):
titulo = models.CharField(maxlength=100)
autores = models.ManyToManyField(Autor)
editorial = models.ForeignKey(Editorial)
fecha_publicacion = models.DateField()
def __str__(self):
return self.titulo
Como puede ver, un método __str__() hace aquello que haga falta para devolver una cadena representativa. Aquí, los métodos __str__() de Editorial y Libro se limitan a devolver el nombre nombre y título, respectivamente, pero la el __str__() de Autor es ligeramente más complejo (une los campos nombre y apellidos). El único requisito para __str__() es que debe devolver una cadena de texto. Si no fuera así (si devolviese, digamos, un entero), entonces Python lanzará una TypeError con un mensaje del tipo "__str__ returned non-string".
Para que tengan efecto los cambios, salga de la sesión de Python y entre de nuevo usando python manage.py shell. (Ésta es la manera más sencilla de hacer que los cambios tengan efecto.) Ahora es mucho más sencillo entender la lista de objetos Editorial:
>>> from books.models import Editorial >>> editorial_list = Editorial.objects.all() >>> editorial_list [<Editorial: Apress>, <Editorial: O'Reilly>]
Asegúrese de que cualquier modelo que defina tenga un método __str__() (no sólo por su propia conveniencia al usar el intérprete interactivo, sino porque Django utiliza el resultado de __str__() en varios lugares donde necesite mostrar objetos).
Por último, observe que __str__() es un buen ejemplo de adición de comportamiento a los modelos. Un modelo de Django describe un objeto más de lo que puede hacerlo la estructura de una tabla en una base de datos; también describe cualquier funcionalidad del propio objeto. __str__() es un ejemplo de tal funcionalidad (un modelo sabe cómo representarse a sí mismo).