You are on page 1of 48

Tutorial de

Tutorial escrito por Jos Plana

Tutorial Django

ndice
Introduccin: Cmo iniciar una aplicacin con Django; lbum de fotos.
Modelos y BBDD: Donde guardar la informacin.
Vistas y Urls: Procesar los datos
Formularios y Templates: Guardar y ensear nuestras fotos.
Cmo usar Git y Bootstrap con Django

Tutorial Django

Introduccin: Cmo iniciar una aplicacin con


Django; lbum de fotos.
Si an no conoces Django, el framework de Python que nos permite crear
aplicaciones web de forma rpida, no te puedes perder este tutorial en el que iremos
creando un lbum de fotos, para ver de forma prctica los conceptos bsicos. Como
punto de inicio es una buena prctica trabajar sobre un entorno virtual, que nos
permita tener instalaciones especficas del proyecto en el que estamos y para
hacerlo vamos a usar virtualenvwrapper. Comprobamos que lo tenemos instalado.
puedes ver como instalarlo)
(
Aqu

openwebinars@~/aplicaciones: pip
showvirtualenvwrapper
--Name: virtualenvwrapper
Version
:
4.2
Location: /usr/
local
/lib/python2
.7
/dist-packages
Requires: virtualenv, virtualenv-clone, stevedore
openwebinars@~/aplicaciones:

Ahora podemos crear el entorno virtual en el que vamos a trabajar, que llamaremos
tutorial
openwebinars
@~/aplicaciones: mkvirtualenv tutorial
New python executable
intutorial/bin/python
Installing setuptools, pip...done.
(tutorial)openwebinars
@~/aplicaciones:

Podemos ver que ahora tenemos delante del prompt el nombre de nuestro entorno
entre parntesis, esto nos indica que estamos dentro. Otros comandos tiles son:
deactivate: Para salir del entorno. workon: Para ver los entornos disponibles. workon
[entorno]: Para activar un entorno; workon tutorial en nuestro caso. rmvirtualenv
[entorno]: Para borrar un entorno. Recuerda! antes tienes que haber salido de l. Con
nuestro entorno listo, podemos empezar a prepararlo, para ello vamos a instalar
Django y, como nuestra aplicacin va a trabajar con imgenes, tambin necesitamos
Pillow.

Tutorial Django

(tutorial)openwebinars
@~/aplicaciones: pip install Django==1.7, Pillow
Downloading/unpacking Django==
1.7
Downloading Django-
1.7
-py2.py3-none-any.whl (
7.4
MB):
7.4
MB downloaded
Downloading/unpacking Pillow
Downloading Pillow-
2.6.1
.tar.gz (
7.3
MB):
7.3
MB downloaded
--Successfully installed Django Pillow
Cleaning up...
(tutorial)openwebinars
@~/aplicaciones:
(tutorial)openwebinars
@~/aplicaciones: pip list
argparse (
1.2.1
)
Django (
1.7
)
ipython (
2.3.0
)
Pillow (
2.6.1
)
pip (
1.5.6
)
setuptools (
3.6
)
wsgiref (
0.1.2
)
(tutorial)openwebinars
@~/aplicaciones:

Con el proceso de instalacin terminado podemos crear nuestro proyecto que


llamamos
myapps
, usa el comando:
(tutorial)openwebinars
@~/aplicaciones: django-admin.py startproject myapps
(tutorial)openwebinars
@~/aplicaciones:

Ya tenemos el proyecto creado y sobre este podremos ir aadiendo aplicaciones a


parte de las que ya vienen con Django, pero esto lo veremos ms adelante, antes
vamos a ver qu aspecto tiene; El c
omando tree nos muestra la estructura
.
(tutorial)openwebinars
@~/aplicaciones: tree myapps
myapps
manage.py
myapps
__init__.py
settings.py
urls.py
wsgi.py
1directory,
5files
(tutorial)openwebinars
@~/aplicaciones:

En este primer post vamos a usar solo dos de estos ficheros, ./manage.py
y./myapps/settings.py. El primero nos va a permitir realizar acciones sobre el

Tutorial Django

proyecto desde la lnea de comandos, el segundo es el fichero de configuracin


propio del proyecto. En este punto ya podemos crear la aplicacin, vamos al
directorio ./myapps y ejecutamos el siguiente comando:
(tutorial)openwebinars@~
/aplicaciones/my
apps: python manage.py startapp album
(tutorial)openwebinars@~
/aplicaciones/my
apps:
(tutorial)openwebinars@~
/aplicaciones/my
apps: tree album
album
admin.py
__init__.py
migrations

__init__.py
models.py
tests.py
views.py
1directory,
6files
(tutorial)openwebinars@~
/aplicaciones/my
apps:

Aqu podemos ver como es el directorio de nuestra aplicacin lbum. Vamos ahora
al directorio ./myapps y editamos el fichero de configuracin settings.py para aadir
nuestra aplicacin en la tupla INSTALLED_APPS; Debera tener este aspecto.
INSTALLED_APPS= (
'django.contrib.admin'

,
'django.contrib.auth'

,
'django.contrib.contenttypes'

,
'django.contrib.sessions'

,
'django.contrib.messages'

,
'django.contrib.staticfiles'

,
'album'

,
)

Antes de guardar los cambios en el fichero de configuracin, buscamos el


diccionario DATABASES, en este tutorial usaremos SQLite, la base de datos que nos
ofrece Django por defecto.

DATABASES = {
'default'

:{
'ENGINE'

:
'django.db.backends.sqlite3'
,
'NAME'

: os.path.
join
(BASE_DIR,
'db.sqlite3'
),
}
}

Tutorial Django

aqu puedes
Y por ltimo establecemos la zona horaria en la que nos encontramos,
verlas todas, Europa/Madrid en nuestro caso.
TIME_ZONE=
'Europe/Madrid'

Ya podemos guardar los cambios en settings.py. Una de las aplicaciones que nos
ofrece Django por defecto es un administrador desde el que podremos aadir o
editar contenidos en nuestra aplicacin, esto nos va a facilitar mucho las cosas.
Para iniciarlo necesitamos crear las tablas necesarias y posteriormente podremos
crear el usuario con el que tendremos acceso. Para el primer paso ejecutamos el
siguiente comando.
(tutorial)openwebinars@~
/aplicaciones/my
apps: python manage.py migrate
Operations to perform:
Apply all migrations: admin, contenttypes, auth, sessions
Running migrations:
Applying contenttypes
.0001
_initial... OK
Applying auth
.0001
_initial... OK
Applying admin
.0001
_initial... OK
Applying sessions
.0001
_initial... OK
(tutorial)openwebinars@~
/aplicaciones/my
apps:

Con esto lo que hacemos es recorrer la lista de aplicaciones que tenemos en


nuestro proyecto, en INSTALLED_APPS, dentro del fichero settings.py e ir creando
las tablas necesarias en la base de datos. Tambin podemos ver que se ha creado
un fichero db.sqlite3 que ser nuestra base de datos.

(tutorial)openwebinars@~/aplicaciones/myapps: ll -rt
total
56
-rwxrwxr-
x
1jose jose
249
oct
21
20
:
24manage.py*
drwxrwxrwx
4jose jose
4096
oct
22
19
:
43../
drwxrwxr-
x
2jose jose
4096
oct
22
20
:
22myapps/
drwxrwxr-
x
3jose jose
4096
oct
22
20
:
22album/
-rw-r--r--
1jose jose
36864
oct
22
20
:
22db.sqlite3
drwxrwxr-
x
4jose jose
4096
oct
22
20
:
22./
(tutorial)openwebinars@~/aplicaciones/myapps:

Tutorial Django

Con la base de datos lista, vamos a crear el usuario.

(tutorial)openwebinars
@~/aplicaciones/myapps: python manage.py createsuperuser
Username (leave blank to use
'administrador'
): admin
Email address: admin
@admin.com
Password: *****
Password (again): *****
Superuser created successfully.
(tutorial)openwebinars
@~/aplicaciones/myapps:

Ya tenemos nuestro proyecto listo para empezar a desarrollar nuestra aplicacin,


para probarlo iniciamos el servidor de desarrollo.
(tutorial)openwebinars@~/aplicaciones/myapps: python manage.py runserver
Performing system checks...
System
check
identified
noissues (
0silenced).
October
22
,
2014-
20
:
32
:
42
Django
version
1.7
,
usingsettings
'myapps.settings'
Startingdevelopment
server
athttp://
127.0.0.1
:
8000
/
Quit the
server
withCONTROL-C.

En un navegador buscamos la direccin http://127.0.0.1:8000/

Tutorial Django
Para visitar el administrador vamos a http://127.0.0.1:8000/admin/

Django nos ofrece mucho trabajo hecho para que solo tengamos que pensar en
nuestra aplicacin.

Tutorial Django

Modelos y BBDD: Donde guardar la informacin.


En el captulo anterior vimos cmo poner en marcha nuestro proyecto Django y
cmo arrancar una aplicacin. Estamos creando un lbum de fotos, hemos
preparado un entorno virtual en el que estamos trabajando y tenemos instalado
Django y Pillow. Tambin creamos nuestra base de datos con SQLite y un usuario de
administracin para la aplicacin.
En este nuevo post vamos a crear el modelo de datos y lo incluiremos en el
administrador que nos ofrece Django para poder gestionarlo sin tener que recurrir a
la consola.
Si recordamos la estructura de carpetas que creaba Django para nuestra aplicacin,
tenemos lo siguiente.
(tutorial)openwebinars@~
/aplicaciones/my
apps: tree album
album
admin.py
__init__.py
migrations

__init__.py
models.py
tests.py
views.py
1directory,
6files
(tutorial)openwebinars@~
/aplicaciones/my
apps:

Es en el fichero models.py donde vamos a definir el modelo de datos para nuestro


lbum, lo editamos para dejarlo as:

fromdjango.db
importmodels
classCategory(models.Model):
""" Categorias para clasificar las fotos """

name = models.CharField(max_length=
50
)
def

__unicode__
(self)
:
returnself.name

Tutorial Django

classPhoto(models.Model):
""" Fotos del album """

category = models.ForeignKey(Category, null=


True
, blank=
True
)
title = models.CharField(max_length=
50
, default=
'No title'
)
photo = models.ImageField(upload_to=
'photos/'
)
pub_date = models.DateField(auto_now_add=
True
)
favorite = models.BooleanField(default=
False
)
comment = models.CharField(max_length=
200
, blank=
True
)
def

__unicode__
(self)
:
returnself.title

Lo primero que hacemos es importar el mdulo models ya que cada una de nuestras
clases ser una subclase de models.Model.
Para nuestra aplicacin hemos definido dos clases, Category que representar las
distintas categoras en las que podemos clasificar nuestras fotos y Photo que
representa las fotos de nuestro lbum. Si las clases que definimos en models.py
representan las tablas en nuestra base de datos, las columnas estn representadas
por las instancias de la clase Field. Nosotros hemos usado los siguientes campos:
CharField: para campos de caracteres. Tiene un argumento obligatorio, max_length.
ImageField: para ficheros con extensiones de imagen. Aqu usamos el argumento
upload_to para especificarle en que directorio queremos que se guarden las
imgenes de nuestra aplicacin.
DateField:

para fechas.

Al indicarle auto_now_add como True, estamos

almacenando la fecha en la que la foto se ha guardado por primera vez.


BooleanField: para valores booleanos, indicndole que el valor por defecto es false,
en caso de no definirlo estaramos usando None como valor por defecto.
ForeignKey: para establecer relaciones entre clases. null y blank para especificar que
este campo puede estar vaco.
Adems de esto, definimos en cada una de las clases el mtodo __unicode__() de
esta forma le podemos indicar como queremos que quede representado cada uno

Tutorial Django

de los objetos, en nuestro caso usaremos el nombre para las categoras y el ttulo
para las fotos.
Con el modelo de datos definido podemos trasladarlo a nuestra base de datos,
primero ejecutamos el siguiente comando.
(tutorial)openwebinars@~
/aplicaciones/my
apps: python manage.py makemigrations
Migrations
for
'album'
:
0001

_initial.py:
- Create model Category
- Create model Photo
(tutorial)openwebinars@~
/aplicaciones/my
apps:

Veremos una salida como esta, que nos indica que se ha creado un fichero,
0001_initial.py en el que estn las modificaciones que se harn en la base de datos
al ejecutar el siguiente comando.
(tutorial)openwebinars@~
/aplicaciones/my
apps: python manage.py migrate
Operations to perform:
Apply all migrations: admin, album, contenttypes, auth, sessions
Running migrations:
Applying album
.0001
_initial... OK
(tutorial)openwebinars@~
/aplicaciones/my
apps:

Con esto tendremos en la base de datos el modelo que acabamos de definir. Pero
antes de probarlo tenemos que editar el fichero settings.py para definir
MEDIA_ROOT como:
MEDIA_ROOT = os.path.
join
(BASE_DIR,
'media/'
)

Lo hacemos porque Django no guarda en la base de datos el fichero si no la ruta


donde est el fichero, de forma relativa a esta configuracin; As, nuestras fotos se
guardarn en./media/photos/.
Ahora ya podemos abrir el shell y probar a grabar la primera imagen.

(
tutorial
)
openwebinars
@
~/aplicaciones/myapps:python manage.py shell
from album.models import Category, Photo
from django.core.files import File

10

Tutorial Django

c=
Category
(name=
'Lagos'
)
c.
save
()
p=
Photo
(category=c, title=
'Maly'
)
f=
open
(
'/home/jose/Escritorio/maly.jpg'
)
p.photo.
save
(
'maly.jpg'
,
File
(f))
c.name
Out:
'Lagos'
p.title
Out:
'Maly'
p.photo.url
Out:
'photos/maly.jpg'
exit

Podemos ver que se ha creado un nuevo directorio ./media/photos/ que contiene


nuestra imagen.
(
tutorial
)
openwebinars
@
~/aplicaciones/myapps:tree media
media
photos
maly.jpg
1directory,
1file

Aunque vemos que funciona correctamente, esta forma de trabajar es lenta si se


trata de alimentar la base de datos con ms de un registro, para hacerlo ms
llevadero vimos en el captulo anterior que Django nos ofreca un administrador con
el que poder gestionar el contenido de nuestra app. Vamos a prepararlo.
Lo primero es decirle que queremos que nuestra aplicacin, lbum, aparezca en l.
Editamos el fichero ./album/admin.py para dejarlo as.
fromdjango.contrib
importadmin
fromalbum.models
importCategory, Photo
admin.site.register(Category)
admin.site.register(Photo)

11

Tutorial Django

Iniciamos el servidor de pruebas y vamos a la direccin http://127.0.0.1:8000/admin,


entramos con el usuario que creamos en el captulo anterior y vemos la pgina
principal del administrador.

Ahora es ms fcil crear una categora nueva y mucho ms fcil aadir imgenes. A
la derecha de Photos tenemos el botn Add.

En Category podemos elegir entre las que tenemos creadas o directamente crear
una nueva, Montaas en nuestro caso. Abajo a la derecha podemos decidir si
guardar y crear otra, guardar y continuar editando o guardar.
Tambin podemos borrar las fotos que tenemos cargadas, con el botn que aparece
abajo a la izquierda.

12

Tutorial Django

Al hacerlo detectamos un problema, aunque el registro se ha borrado de nuestra


base de datos, vemos que el fichero que contiene la imagen sigue estando en
nuestro directorio./media/photos/; Como dijimos antes lo que almacena Django en
la base de datos solo es el directorio donde est la imagen.
Para solucionarlo podemos usar las seales. Estas nos informan de cundo est
ocurriendo una determinada accin. A nosotros nos interesa saber cuando se est
llamando al mtodo delete() de nuestro objeto Photo, de esta forma una vez borrado
el registro de la base de datos, podremos borrar la imagen a la que hace referencia.
Volvemos a nuestro fichero models.py y lo editamos.
fromdjango.db.models.signals
importpost_delete
fromdjango.dispatch
importreceiver
@receiver(post_delete, sender=Photo)
def
photo_delete
(sender, instance, **kwargs)
:
""" Borra los ficheros de las fotos que se eliminan. """

instance.photo.delete(
False
)

Con esto queda solucionado nuestro problema.

Vistas y Urls: Procesar los datos


En los post anteriores vimos cmo se iniciaba una aplicacin, creamos la base de
datos y los modelos; y vimos como podamos introducir las imgenes de nuestro
lbum de fotos de dos formas, usando el shell y a travs del administrador que nos
ofrece Django por defecto. Hoy usaremos las vistas para mostrar nuestra galera.

13

Tutorial Django

Para ver como gestiona Django las vistas, las relaciona con el modelo de datos y
enva esa informacin al navegador vamos a empezar creando una vista sencilla,
simplemente vamos a mostrar un mensaje por pantalla.
Editamos el fichero ./album/views.py
fromdjango.http
importHttpResponse
def
first_view
(request)
:
returnHttpResponse(

'Esta es mi primera vista!'


)

Una vista es una funcin o un mtodo que bsicamente hace dos cosas, toma como
argumento un objeto HttpRequest, en el que va la informacin referente a la solicitud
que estamos haciendo, como por ejemplo si el mtodo empleado es POST o GET o
el directorio donde est la pgina solicitada; y devuelve un objeto HttpResponse con
la informacin de la pgina que va a mostrar o una excepcin si algo ha ido mal.
Ahora tenemos que asociar la vista que acabamos de definir con una direccin, para
hacerlo vamos a manejar dos ficheros, uno lo crea Django al iniciar el proyecto y
est en ./myapps/urls.py, el otro lo tendremos que crear nosotros dentro del
directorio de nuestra aplicacin,./album/urls.py.
Vamos a editar el primero ./myapps/urls.py.
fromdjango.conf.urls
importpatterns, include, url
fromdjango.contrib
importadmin
urlpatterns = patterns(
''
,
url(
r'^admin/'
, include(admin.site.urls)),
)

Si nos olvidamos de las lneas que tiene comentadas vemos que tiene una relacin
entre la direccin /admin y el mdulo admin.site.urls. Esta es la forma que tiene
Django de enlazar ladireccin con el fichero urls.py propio de la aplicacin admin
que ya hemos usado en los captulos anteriores. De esta forma al dirigirnos a
http://127.0.0.1:8000/admin/ lo que hacemos es empezar a usar dicho fichero para

14

Tutorial Django

los

siguientes

enlaces

que

usemos,

como

por

ejemplo

http://127.0.0.1:8000/admin/album/photo/.
Vamos a hacer lo mismo con nuestra aplicacin lbum. Aadimos la siguiente lnea
como nuevo argumento de la funcin patterns() para dejarla de la siguiente forma.
fromdjango.conf.urls
importpatterns, include, url
fromdjango.contrib
importadmin
urlpatterns = patterns(
''
,
url(
r'^album/'
, include(
'album.urls'
)),
url(
r'^admin/'
, include(admin.site.urls)),
)

Ahora ya sabemos que al incluir en el navegador una direccin que comience por
/albumenviaremos el resto de la cadena como argumento a la funcin patterns que
tenemos en el fichero./album/urls.py.
Creamos y editamos este fichero para dejarlo as.
fromdjango.conf.urls
importpatterns, url
fromalbum
importviews
urlpatterns = patterns(
''
,
url(
r'^$'
, views.first_view, name=
'first-view'
),
)

De esta forma, la direccin /album estar apuntando a la vista first_view().


La funcin url() necesita dos argumentos, el primero ser una expresin regular
(regex) que utiliza para identificar la direccin que escribimos en el navegador, el
segundo es la funcin a la que llamar, first_view() para nosotros, pasndole como
primer argumento el objeto HttpRequest que mencionbamos antes. Adems se
pueden pasar tres argumentos opcionales, name lo usaremos para identificar
nuestra url de forma nica y nos servir para poder cambiar la url sin tener que
modificar las partes del proyecto donde hacemos referencia a ella. Los otros dos
argumentos son kwargs y prefix que no usaremos en nuestra aplicacin.

15

Tutorial Django

Si arrancamos el servidor de pruebas y buscamos en el navegador la direccin


http://127.0.0.1:8000/album/ veremos lo siguiente.

Con esto hemos visto cmo unir la direccin del navegador con nuestras vistas,
veamos ahora cmo relacionar la vista con el modelo de datos y devolver esta
informacin al navegador. Para hacerlo vamos a crear un nuevo directorio dentro de
nuestra aplicacin./myapps/album/templates/album/ de modo que tengamos esto.
(tutorial)openwebinars@~/aplicaciones/myapps: tree album
album
admin.py

__init__
.py
migrations

0001_initial.py

0001_initial.pyc


__init__
.py
models.py
templates

album
tests.py
urls.py
views.py

En l incluiremos los ficheros html propios de nuestra aplicacin. Creamos el fichero


category.html de la siguiente forma.

{{ object_list }}

16

Tutorial Django

Ahora veremos que es object_list.


Editamos de nuevo el fichero ./album/views.py
fromdjango.http
importHttpResponse
fromdjango.shortcuts
importrender
fromalbum.models
importCategory, Photo
def
first_view
(request)
:
returnHttpResponse(

'Esta es mi primera vista'


)
def
category
(request)
:
category_list = Category.objects.all()
context = {
'object_list'
: category_list}
returnrender(request,

'album/category.html'
, context)

category_list contiene todas las categoras disponibles y las pasamos como


argumento a la funcin render(). Esta funcin es un atajo que nos proporciona
Django para devolver el objeto HttpResponse a partir de una plantilla (category.html)
y la informacin necesaria para componerla (context).
Lo unimos con ./album/urls.py
fromdjango.conf.urls
importpatterns, url
fromalbum
importviews
urlpatterns = patterns(
''
,
url(
r'^$'
, views.first_view, name=
'first-view'
),
url(
r'^category/$'
, views.category, name=
'category-list'
),
)

buscamos

la

nueva

direccin

en

el

navegador,

http://127.0.0.1:8000/album/category/

17

Tutorial Django

Como vemos, con object_list hemos incluido la lista de objetos Category dentro de
nuestra plantilla category.html, por tanto ya tenemos toda la informacin relativa a
las categoras.
Vamos a incluir una nueva vista que nos muestre el detalle de una de las categoras;
editamos de nuevo el fichero ./album/views.py
def
category_detail
(request, category_id)
:
category = Category.objects.get(id=category_id)
context = {
'object'
: category}
returnrender(request,

'album/category_detail.html'
, context)

Ahora la funcin category_detail() recibe dos argumentos, request y el identificador


de la categora que queremos mostrar.
En el directorio de ./album/templates/album creamos una nueva plantilla
category_detail.html
{{
object
.id }}, {{
object
.name }}, {{
object
.photo_set.all }}

De forma similar a la vista anterior, en la variable context que pasamos a la funcin


render() tenemos un objeto Category del que podemos extraer su id, name y las
fotos que pertenecen a dicha categora entre otras cosas.
Por ltimo conectamos la vista con una url, esta vez tenemos que indicar de qu
categora queremos extraer el detalle, para conseguirlo usaremos (?P\d+) siendo
category_id el segundo parmetro que hemos pasado a la funcin category_detail().

18

Tutorial Django

fromdjango.conf.urls
importpatterns, url
fromalbum
importviews
urlpatterns = patterns(
''
,
url(
r'^$'
, views.first_view, name=
'first-view'
),
url(
r'^category/$'
, views.category, name=
'category-list'
),
url(
r'^category/(?P\d+)/detail/$'
, views.category_detail,
name=
'category-detail'
),
)

Si abrimos el enlace http://127.0.0.1:8000/album/category/2/detail/

en el

navegador veremos el resultado.

Ya vimos en posts anteriores que Django nos ofrece soluciones para facilitarnos el
trabajo y en esta ocasin podemos aprovecharnos de las vistas genricas, como
ListView y DetailView para obtener una lista de todos los objetos pertenecientes a un
modelo y el detalle de uno de ellos. Vamos a hacerlo para la clase Photo.
Abrimos ./album/views.py e incluimos este cdigo.

fromdjango.views.generic
importListView, DetailView
classPhotoListView(ListView):
model = Photo
classPhotoDetailView(DetailView):
model = Photo

Como podemos ver solamente tenemos que decirle sobre que modelo queremos
trabajar.

19

Tutorial Django

Creamos dos nuevos templates, photo_list.html para el listado de fotos y


photo_detail.html para el detalle. Las vistas genricas usan por defecto el siguiente
sistema a la hora de buscar la plantilla html, <app_name>/<model_name>_list.html y
<app_name>/<model_name>_detail.html,

paraListView

DetailView

respectivamente. Si queremos especificar un nombre de plantilla distinto podemos


usar el atributo template_name.
Creamos ./album/templates/album/photo_list.html
{{ object_list }}

y ./album/templates/album/photo_detail.html
{{
object
.id }}, {{
object
.title }}

Por ltimo, unimos la vista con urls editando ./album/urls.py para dejarlo as.
fromdjango.conf.urls
importpatterns, url
fromalbum
importviews
urlpatterns = patterns(
''
,
url(
r'^$'
, views.first_view, name=
'first-view'
),
url(
r'^category/$'
, views.category, name=
'category-list'
),
url(
r'^category/(?P\d+)/detail/$'
, views.category_detail,
name=
'category-detail'
),
url(
r'^photo/$'
, views.PhotoListView.as_view(),
name=
'photo-list'
),
url(
r'^photo/(?P\d+)/detail/$'
,
views.PhotoDetailView.as_view(),
name=
'photo-detail'
),
)

Aqu tenemos que tener en cuenta dos cosas, al usar el sistema de vistas genricas
estamos trabajando con clases no con funciones por lo que tenemos que llamar al
mtodo as_view(), tambin hemos dejado de usar (?P\d+) y lo hemos sustituido por
(?P\d+).
Ya podemos ver en el navegador que el resultado es el mismo aunque esta vez
nuestras vistas son mucho ms sencillas.

20

Tutorial Django

http://127.0.0.1:8000/album/photo/

http://127.0.0.1:8000/album/photo/1/detail/

Esto es solo una parte ya que el sistema de vistas genricas es mucho ms potente
y lo veremos con ms detalle en el siguiente captulo.
Como ejercicio podemos cambiar las funciones category() y category_detail() para
usar generic views, teniendo en cuenta que estamos trabajando con la plantilla
category.html y no es lo que usar por defecto la vista ListView.

Formularios y Templates: Guardar y ensear


nuestras fotos.
En este nuevo captulo vamos trabajar con las plantillas que utilizamos en el post
anterior para mostrar la informacin de forma ms visual. Continuaremos viendo las

21

Tutorial Django

vistas genricas para crear formularios de forma que podamos modificar o aadir
registros a nuestra base de datos.
Como punto de partida vamos a modificar la plantilla category_list.html que
tenamos en el captulo anterior para dejarla as:

<
h1
>ALBUM</
h1
>
<
h2
>Category</
h2
>
<
div
>
{% for c in object_list %}
<
a
href
=
"{% url 'category-detail' c.pk %}"
>
{{ c.name }}
</
a
>
{% endfor %}
</
div
>

De nuestra nueva plantilla podemos destacar dos elementos, las variables que estn
identificadas por {{ }} y ya usamos en el captulo anterior y los tags que estn
representados por {% %}.
Las variables son evaluadas y sustituidas por su valor en la representacin de la
plantilla. La forma en que se muestran las variables puede ser modificada mediante
el uso de filtros que veremos ms adelante.
Los tags nos ofrecen la posibilidad de incluir lgica o control de flujo dentro de las
plantillas entre otras cosas.
En nuestra plantilla estamos usando {% for %} para recorrer las distintas categorias
que tenemos y poder mostrar su nombre con la variable {{ c.name }}, el valor name
del objeto c extraido de la lista object_list.
{% url %} es otro tag que usaremos muy a menudo, en este caso le pasamos el
nombre de la url a la que queremos apuntar y el parmetro que necesita.
Si vemos el resultado en el navegador http://127.0.0.1:8000/album/category/,
tenemos lo siguiente:

22

Tutorial Django
Si lo pensamos bien, vemos que el ttulo Album y la cabecera que indica si estamos
en la seccin Category o Photo se van a repetir para el resto de plantillas. Para evitar
tener que duplicar cdigo y seguir el principio DRY de Django, tenemos a nuestra
disposicin dos tags que nos van a permitir usar una nica plantilla como esqueleto
de nuestra aplicacin e ir insertando en esta las partes propias del resto de
plantillas, esto se conoce como Template inheritance. Vamos a usarlo.
Lo primero que haremos es modificar la plantilla category_list.html para dejarla as:
{% extends 'base.html' %}
{% block section %}
<
h2
>Category</
h2
>
<
hr
>
{% endblock section %}
{% block maincontent %}
<
div
>
{% for c in object_list %}
<
a
href
=
"{% url 'category-detail' c.pk %}"
>
{{ c.name }}
</
a
>
{% endfor %}
</
div
>
{% endblock maincontent %}

Aqu tenemos los dos nuevos tags, {% extends %} y {% block %}. El primero va a
cargar nuestra plantilla base.html, que ser la plantilla esqueleto e incluir las
secciones contenidas en los tags block donde se lo indiquemos.

23

Tutorial Django

Ahora vamos a crear un nuevo directorio ./templates en el que incluiremos la


plantilla base.html
(tutorial)openwebinars@~
/aplicaciones/my
apps: tree templates
templates
base.html
0directories,
1file
(tutorial)openwebinars@~
/aplicaciones/my
apps:

base.html quedar de la siguiente forma:

<!DOCTYPE html>
<
html
>
<
head
lang
=
"en"
>
<
meta
charset
=
"UTF-8"
>
<
title
>Album</
title
>
</
head
>
<
body
>
<
h1
>Album</
h1
>
<
ul
>
<
li
>
<
a
href
=
"{% url 'category-list' %}"
>Category</
a
>
</
li
>
<
li
>
<
a
href
=
"{% url 'photo-list' %}"
>Photo</
a
>
</
li
>
</
ul
>
<
hr
>
<
div
>
{% block section %}
{% endblock section %}
</
div
>
<
div
>
{% block maincontent %}
{% endblock maincontent %}
</
div
>
</
body
>
</
html
>

Hemos incluido dos nuevos links para poder navegar entre categoras y fotos.
Vamos a modificar la que era nuestra url principal, para que deje de mostrar la
primera vista que hicimos y la usaremos como pgina de inicio. Editamos
./album/views.py y ./album/urls.py
En la ./album/views.py aadimos:

24

Tutorial Django

EnOpenWebinars.nettenemostodosestos
cursosatuenteradisposicin

LinuxLPIC-1
Examen101

LinuxLPIC-1
Examen102

NodeJS,
ExpressJSy
MongoDB

AngularJSy
TypeScript

Appsmvilescon
PhoneGap

ServidoresVoIP
conAsterisk

Desarrollo
Frontend
Profesional

Virtualizacinde
Servidorescon
Promox

AppsMvilescon
TitaniumAlloy

Desarrollo
Backendcon
Django

Tutorial Django

fromdjango.shortcuts
importrender
def
base
(request)
:
returnrender(request,

'base.html'
)

y en ./album/urls.py:
fromdjango.conf.urls
importpatterns, url
fromalbum
importviews
urlpatterns = patterns(
''
,
url(
r'^$'
, views.base, name=
'base'
),
url(
r'^category/$'
, views.CategoryListView.as_view(),
name=
'category-list'
),
url(
r'^category/(?P<pk>\d+)/detail/$'
,
views.CategoryDetailView.as_view(),
name=
'category-detail'
),
url(
r'^photo/$'
, views.PhotoListView.as_view(),
name=
'photo-list'
),
url(
r'^photo/(?P<pk>\d+)/detail/$'
,
views.PhotoDetailView.as_view(),
name=
'photo-detail'
),
)

Adems, vamos a indicar la nueva ruta donde estar la plantilla base.html aadiendo
en./myapps/settings.py
TEMPLATE_DIRS = (
os.path.
join
(BASE_DIR,
'templates/'
),
)

Comprobamos el resultado en el navegador, http://127.0.0.1:8000/album/.

25

Tutorial Django
y http://127.0.0.1:8000/album/category/

Ahora ya podemos practicar con category_detail.html para que herede de base.html


y experimentar con las distintas etiquetas html. Nosotros lo hemos dejado as,
http://127.0.0.1:8000/album/category/1/detail/

26

Tutorial Django
{% extends 'base.html' %}
{% load static %}
{% block section %}
<
h2
>{{ object.name }}</
h2
>
<
hr
>
{% endblock section %}
{% block maincontent %}
<
div
>
<
table
border
=
"1px"
>
<
tr
>
{% for p in object.photo_set.all %}
<
td
>
<
div
>{{ p.title }}</
div
>
<
div
>
<
a
href
=
"{% url "photo-detail"
p.id%}">
<
img
src
=
"{% static p.photo.url %}"
style
=
"width:200px;height:200px"
/>
</
a
>
</
div
>
</
td
>
{% endfor %}
</
tr
>

27

Tutorial Django

</
table
>
</
div
>
{% endblock maincontent %}

Vemos que en cada categora se muestra una miniatura con los enlaces a las
distintas fotos que pertenecen, para mostrar las imagenes necesitamos incluir
STATICFILES_DIRS en ./album/settings.py
STATICFILES_DIRS = (
os.path.
join
(BASE_DIR,
'media'
),
)

Ahora que tenemos los enlaces al detalle de las fotos, vamos a completar la
navegacin modificando photo_detail.html para que nos muestre la foto y los
detalles.
Editamos photo_detail.html.
{% extends 'base.html' %}
{% load static %}

{% block section %}
<
h2
>
{{ object.title }}
{% if object.favorite %}
&#9733;
{% else %}
&#9734;
{% endif %}
{% if object.category_id %}
( <
a
href
=
"{% url "category-detail"
object.category_id%}">{{
object.category }}</
a
>)
{% endif %}
</
h2
>
<
p
><
strong
>comments: </
strong
>{{ object.comment|default:"no comments" }}</
p
>
<
a
href
=
"{% url "photo-update"
object.id%}">edit</
a
>
<
a
href
=
"{% url "photo-delete"
object.id%}">delete</
a
>
<
hr
>
{% endblock section %}
{% block maincontent %}
<
div
>
<
div
>
<
img
src
=
"{% static object.photo.url %}"
style
=
"width:550px;height:420px"
/>
</
div
>

28

Tutorial Django

</
div
>
{% endblock maincontent %}

En esta nueva plantilla hemos incluido varias cosas interesantes. Con la etiqueta
load estamos cargando el modulo static.py que necesitamos para mostrar las
imagenes. Para mostrar los comentarios usamos la variable {{ object.comment }}
con un filtro default para incluir un comentario por defecto cuando el valor es una
cadena vaca , adems tenemos dos nuevos enlaces para editar y borrar.
Como ya hemos visto, estos enlaces apuntan a una vista a travs de ./album/urls.py.
Con el primero modificaremos las fotos mientras que con el segundo vamos a poder
borrar un registro de nuestra base de datos.
Vamos a crear estas nuevas funcionalidades y aprovechamos para recordar como
se trabajaba con las vistas genricas.
Editamos ./album/views.py para aadir lo siguiente.
fromdjango.core.urlresolvers
importreverse_lazy
fromdjango.views.generic.edit
importUpdateView, CreateView, DeleteView
classPhotoUpdate(UpdateView):
model = Photo
classPhotoCreate(CreateView):
model = Photo
classPhotoDelete(DeleteView):
model = Photo
success_url = reverse_lazy(
'photo-list'
)

Tambin hemos incluido la vista PhotoCreate que usaremos enseguida.


Enlazamos las vistas con urls editando ./album/urls.py y aadiendo lo siguiente
dentro de patterns.
# Update
url(
r'^photo/(?P<pk>\d+)/update/$'
, views.PhotoUpdate.as_view(),
name=
'photo-update'
),
#Create
url(
r'^photo/create/$'
, views.PhotoCreate.as_view(), name=
'photo-create'
),
#Delete
url(
r'^photo/(?P<pk>\d+)/delete/$'
, views.PhotoDelete.as_view(),

29

Tutorial Django

name=
'photo-delete'
),

Ahora bien, para las dos primeras vistas, update y create, necesitaremos un
formulario desde el que podamos interactuar bien modificando los valores de
nuestros registros o bien creando uno nuevo. Para crearla usaremos el nombre que
buscar Django por defecto, photo_form.html, esta plantilla es comn para las dos
vistas PhotoUpdate y PhotoCreate ya que muestra todos los campos definidos en el
modelo y dependiendo de la llamada que se haga, desde el modo Update o Create
mostrar los campos con los valores o vacos respectivamente.
Este es el aspecto que tendr photo_form.html
{% extends "base.html" %}
{% block maincontent %}
<
form
enctype
=
"multipart/form-data"
method
=
"post"
>{% csrf_token %}
{{ form.as_p }}
<
button
type
=
"submit"
>Save</
button
>
<
a
class
=
'btn'
href
=
"{% url 'photo-list' %}"
>Cancel</
a
>
</
form
>
{% endblock maincontent%}

Volvemos a usar como plantilla principal base.html. Dentro de la etiqueta form


usamos el atributo method como POST en lugar de GET ya que en update y create
estamos modificando la informacin contenida en la base de datos. Tambin es
importante destacar el uso del tag {% csrf_token %}; usaremos este tag dentro de las
etiquetas form con atributos POST para evitar ataques CSRF. Bsicamente estos
ataques usan los datos de conexin de un usuario conocido y de confianza para
realizar acciones no autorizadas sobre el servidor.
La variable form contiene los campos de nuestro modelo y para representarlos
podemos usar as_p para mostrarlos entre etiquetas <p> en html, as_table para usar
etiquetas <tr> o as_ul para usar etiquetas <li>
Tenemos que tener en cuenta una cosa ms, cuando queramos cancelar la edicin
de un registro estamos redirigiendo la navegacin hacia la vista nombrada como
photo-list, de igual modo tenemos que definir hacia dnde iremos cuando grabemos

30

Tutorial Django

los datos usando el botn save, para ello editamos ./album/models.py y aadimos el
siguiente mtodo dentro de la clase Photo.

def
get_absolute_url
(self)
:
returnreverse(

'photo-list'
)

Como

estamos

usando

reverse

es

necesario

importarlo

desde

django.core.urlresolvers
Si abrimos el detalle de una foto y pulsamos sobre el enlace edit veremos esto:

donde podemos modificar los valores que tenamos.


Con estos pasos que acabamos de hacer tenemos lista tambin la opcin de crear
un nuevo registro para aadir fotos, solo tenemos que incluir el enlace en la plantilla
que queramos. Nosotros lo hemos hecho en base.html como otro elemento ms de
la lista.

31

Tutorial Django
Vamos a ver como preparar la opcin de borrado. Aunque ya tenemos enlazados las
vistas con la url y aparentemente no es necesaria ninguna plantilla para borrar un
registro de la base de datos, si intentamos borrar una foto veremos el error
TemplateDoesNotExist, esto es as porque Django nos pide una plantilla para
confirmar la accin, justo debajo del error podemos ver el nombre que espera
encontrar Django.

Vamos a crearla, en nuestro directorio ./album/templates/album incluimos una


nueva plantillaphoto_confirm_delete.html

{% extends 'base.html' %}

32

Tutorial Django

{% block maincontent %}
<
form
action
=
""
method
=
"post"
>{% csrf_token %}
<
p
>Delete "{{ object.title }}"?</
p
>
<
input
type
=
"submit"
value
=
"Confirm"/>
<
a
class
=
'btn'
href
=
"{% url 'photo-list' %}"
>Cancel</
a
>
</
form
>
{% endblock maincontent %}

Igual que al crear o modificar un registro tenemos que decirle el enlace al que debe
dirigirse una vez terminada la accin, al borrar tambin tenemos que hacerlo, ya lo
incluimos en la propia clase con el atributo success_url.
Con esto tenemos las herramientas necesarias para cambiar la plantilla
photo_list.html y hacer que tenga mejor aspecto, tambin podemos generar nuevos
formularios para crear, editar o eliminar categoras as como jugar con todas las
opciones que nos ofrece html para personalizar nuestra aplicacin.
En el siguiente captulo veremos cmo usar repositorios y mejoraremos la
apariencia de nuestro lbum con Bootstrap.

Cmo usar Git y Bootstrap con Django


Continuando con nuestro lbum de fotos, y ahora que el proyecto va tomando forma,
es buena idea empezar a usar un control de versiones de forma que queden
reflejados los cambios que vamos haciendo, as podremos recuperar el proyecto en
un estado anterior y tendremos control sobre las fechas en que se hicieron
modificaciones, entre otras cosas. Adems, aunque en nuestro caso slo
trabajamos nosotros en el lbum, en caso de formar parte de un equipo de
desarrollo tendramos la posibilidad de trabajar de forma independiente sobre los
mismos ficheros sin pisar el trabajo de nuestros compaeros.
Tenemos a nuestra disposicin distintas herramientas que nos permiten trabajar de
esta forma, en nuestro caso usaremos
Git
, as podemos trabajar en un repositorio
propio y una vez tengamos los cambios hechos, hacer push de nuestro repositorio a
un repositorio compartido.

33

Tutorial Django

Lo primero que vamos a hacer es crear un repositorio local, en el que incluiremos


nuestro proyecto con el comando git init .
(tutorial)openwebinars@~
/aplicaciones/my
apps: git init
Initialized empty Git repository
in/home/jose/aplicaciones/myapps/.git/
(tutorial)openwebinars@~
/aplicaciones/my
apps:

Vemos que se ha creado un nuevo directorio .git.


Para ver el estado actual de nuestro repositorio usaremos git status; es conveniente
revisar el estado de nuestro proyecto para tener identificados qu ficheros se han
aadido para la siguiente subida al repositorio comn.
(tutorial)openwebinars@~/aplicaciones/myapps: git status
En la rama master
Commitinicial
Archivos
sinseguimiento:
(
usegit
add<archivo>... para incluir lo que se ha de ejecutar)
.idea/
album/
db.sqlite3
manage.py
media/
myapps/
templates/
nose ha agregado nada al
commitpero existen archivos
sinseguimiento (
usegit
add
para darle seguimiento)
(tutorial)openwebinars@~/aplicaciones/myapps:

Se ha creado una rama de trabajo llamada master, adems git ha identificado todos
los archivos que forman nuestro proyecto y nos informa de que no se han incluido
para el siguiente commit. Para incluir los archivos usaremos el comando git add
<nombre> o git add. Para incluir todo, pero antes vamos a ver si queremos compartir
todo lo que hay en el directorio, por ejemplo en nuestro proyecto tenemos muchos
ficheros del tipo *.pyc que no queremos subir, tampoco queremos subir las fotos ni
la base de datos, de esta forma al repositorio llegarn copias limpias de nuestro
proyecto. Para descartar este tipo de ficheros usamos un archivo .gitignore en el que
podemos decir qu debe dejar sin seguimiento.

34

Tutorial Django

(tutorial)openwebinars@~
/aplicaciones/my
apps: cat .gitignore
*.jpg
*.pyc
.idea
db.sqlite3
(tutorial)openwebinars@~
/aplicaciones/my
apps:

Si volvemos a consultar el estado


(tutorial)openwebinars@~/aplicaciones/myapps: git status
En la rama master
Commitinicial
Archivos
sinseguimiento:
(
usegit
add<archivo>... para incluir lo que se ha de ejecutar)
.gitignore
album/
manage.py
myapps/
templates/
nose ha agregado nada al
commitpero existen archivos
sinseguimiento (
usegit
add
para darle seguimiento)
(tutorial)openwebinars@~/aplicaciones/myapps:

Vemos que ya no est incluido el archivo db.sqlite3, tampoco estarn dentro de los
directorios los ficheros .pyc y .jpg.
Ahora ya podemos decir a Git que inicie el seguimiento de los ficheros de nuestro
proyecto.
(tutorial)openwebinars@~
/aplicaciones/my
apps: git add .
(tutorial)openwebinars@~
/aplicaciones/my
apps: git status
En la rama master
Commit inicial
Cambios para hacer commit:
(use git rm --cached <archivo>... para eliminar stage)
newfile:
newfile:
newfile:

.gitignore
album/__init__.py
album/admin.py

35

Tutorial Django

newfile:
newfile:
newfile:
newfile:
newfile:
newfile:
newfile:
newfile:
newfile:
newfile:
newfile:
newfile:
newfile:
newfile:
newfile:
newfile:
newfile:
newfile:

album/migrations/
0001
_initial.py
album/migrations/__init__.py
album/models.py
album/templates/album/category_detail.html
album/templates/album/category_list.html
album/templates/album/photo_confirm_delete.html
album/templates/album/photo_detail.html
album/templates/album/photo_form.html
album/templates/album/photo_list.html
album/tests.py
album/urls.py
album/views.py
manage.py
myapps/__init__.py
myapps/settings.py
myapps/urls.py
myapps/wsgi.py
templates/base.html

(tutorial)openwebinars@~
/aplicaciones/my
apps:

Aqu podemos ver qu ficheros se han incluido en el primer commit que podemos
ejecutar con el comando git commit -m "Initial commit".

(tutorial)openwebinars@~/aplicaciones/myapps: git
commit-m
"Initial commit"
[
master(root-
commit
) e300e4a] Initial
commit
21files

changed
,
439insertions(+)
create

mode
100644.gitignore
create

mode
100644album/__init__.py
create

mode
100644album/admin.py
create

mode
100644album/migrations/
0001
_initial.py
create

mode
100644album/migrations/__init__.py
create

mode
100644album/models.py
create

mode
100644album/templates/album/category_detail.html
create

mode
100644album/templates/album/category_list.html
create

mode
100644album/templates/album/photo_confirm_delete.html
create

mode
100644album/templates/album/photo_detail.html
create

mode
100644album/templates/album/photo_form.html
create

mode
100644album/templates/album/photo_list.html
create

mode
100644album/tests.py
create

mode
100644album/urls.py
create

mode
100644album/views.py
create

mode
100755manage.py
create

mode
100644myapps/__init__.py
create

mode
100644myapps/settings.py
create

mode
100644myapps/urls.py
create

mode
100644myapps/wsgi.py
create

mode
100644templates/base.html
(tutorial)openwebinars@~/aplicaciones/myapps:

36

Tutorial Django

Ahora que hemos hecho el primer commit en nuestro repositorio local podemos
pasar nuestro proyecto a un repositorio compartido. En este captulo usaremos
GitHub para gestionar nuestro repositorio remoto. Una vez hayamos creado nuestra
cuenta en GitHub veremos la opcin Create Repository, nosotros lo hemos
nombrado

como

my_album

por

lo

que

GitHub

asigna

la

direccin

https://github.com/usuario/my_album.git, donde usuario ser el nombre de usuario


de nuestra cuenta GitHub. Con el siguiente comando podemos crear un repositorio
remoto, que normalmente se llama origin, asociado a esta direccin.
(tutorial)openwebinars@~
/aplicaciones/my
apps: git remote add origin
https:
//github.com/usuario/my_album.git
(tutorial)openwebinars@~
/aplicaciones/my
apps:

Ya podemos enviar nuestro repositorio local al repositorio remoto, origin, que


acabamos de crear.
(tutorial)openwebinars@~/aplicaciones/myapps: git push -u origin master
Username
for
'https://github.com'
: usuario
Password
for
'https://usuario@github.com'
: *******
Counting objects:
27
, done.
Delta compression
usingup to
2threads.
Compressing objects:
100
% (
23
/
23
), done.
Writing objects:
100
% (
27
/
27
),
6.03KiB |
0bytes/s, done.
Total
27(delta
1
), reused
0(delta
0
)
To https:
//github.com/usuario/my_album.git
*[
newbranch]
master -> master
Branch master
setup to track remote branch master
fromorigin.
(tutorial)openwebinars@~/aplicaciones/myapps:

Con este comando estamos diciendo que pase el contenido de la rama local, que
por defecto Git llama mster al repositorio origin que hemos creado, el parmetro -u
le dice a Git que recuerde estos dos nombres, por lo que la prxima vez slo
tendremos que usar git push y ya sabr que tiene que pasar el contenido de master a
origin. Si volvemos a la direccin https://github.com/usuario/my_album.git veremos
el contenido de nuestro proyecto.

37

Tutorial Django

Ahora que ya tenemos nuestro repositorio remoto, volvamos a trabajar sobre la


copia local que tenemos.
Aunque en el captulo anterior vimos cmo mejorar el aspecto de nuestra aplicacin,
, que aparte
en esta ocasin vamos a ir un paso ms all y vamos a usar
Bootstrap
de ofrecernos un diseo preestablecido es web responsive, es decir veremos cmo
podemos

hacer

que

nuestra

aplicacin

mantenga

un

buen

aspecto

independientemente del dispositivo que estemos usando para verla. Lo primero que
tenemos que hacer es indicar a nuestra aplicacin que tiene que cargar Bootstrap,
para hacerlo podemos descargarlo e incluirlo en nuestro proyecto o simplemente
referenciar al CDN, esto ltimo es lo que haremos nosotros. Editamos el fichero
./templates/base.html para dejarlo de la siguiente forma.

<!DOCTYPE html>
<
html
>
<
head
lang
=
"en"
>
<
meta
charset
=
"UTF-8"
>
<
title
>Album</
title
>
<!-- CDN Bootstrap -->

<
link
rel
=
"stylesheet"
href
=
"https://maxcdn.bootstrapcdn.com/bootstrap/3.3.1/css/bootstrap.min.css"
>
<
link
rel
=
"stylesheet"
href
=
"https://maxcdn.bootstrapcdn.com/bootstrap/3.3.1/css/bootstrap-theme.min.css
"
>
<
script
src
=
"https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"
></
script
>
<
script
src
=
"https://maxcdn.bootstrapcdn.com/bootstrap/3.3.1/js/bootstrap.min.js"
></
scrip
t
>
</
head
>
<
body
>
<!-- NavBar -->

<
nav
class
=
"navbar navbar-default"
>
<
div
class
=
"container-fluid"
>
<
div
class
=
"navbar-header"
>
<
button
type
=
"button"
class
=
"navbar-toggle collapsed"
data-toggle
=
"collapse"
data-target
=
"#bs-example-navbar-collapse-1"
>
<
span
class
=
"sr-only"
>Toggle navigation</
span
>
<
span
class
=
"icon-bar"
></
span
>
<
span
class
=
"icon-bar"
></
span
>
<
span
class
=
"icon-bar"
></
span
>
</
button
>
<
a
class
=
"navbar-brand"
href
=
"{% url "base"%}">Album</
a
>
</
div
>

38

Tutorial Django

<
div
class
=
"collapse navbar-collapse"
id
=
"bs-example-navbar-collapse-1"
>
<
ul
class
=
"nav navbar-nav"
>
<
li
><
a
href
=
"{% url "category-list"%}">Category<
span
class
=
"sr-only"
>(current)</
span
></
a
></
li
>
<
li
><
a
href
=
"{% url "photo-list"%}">Photo</
a
></
li
>
</
ul
>
<
ul
class
=
"nav navbar-nav navbar-right"
>
<
li
class
=
"dropdown"
>
<
a
href
=
"#"
class
=
"dropdown-toggle"
data-toggle
=
"dropdown"
role
=
"button"
aria-expanded
=
"false"
>Action
<
span
class
=
"caret"
></
span
>
</
a
>
<
ul
class
=
"dropdown-menu"
role
=
"menu"
>
<
li
><
a
href
=
"{% url "photo-create"
%}">Upload</
a
></
li
>
<
li
class
=
"divider"
></
li
>
<
li
><
a
href
=
"#"
>Log In</
a
></
li
>
</
ul
>
</
li
>
</
ul
>
</
div
>
</
div
>
</
nav
>
<
div
class
=
"container"
>
{% block section %}
{% endblock section %}
</
div
>
<
div
class
=
"container"
>
{% block maincontent %}
{% endblock maincontent %}
</
div
>
</
body
>
</
html
>

Tambin hemos aadido una


barra de navegacin como men principal, que est
bajo el comentario <!-- NavBar -->. Vemos que las etiquetas que estbamos usando
hasta ahora tienen clases del tipo collapse o navbar-header, stas son clases
propias de Bootstrap. Si refrescamos nuestra aplicacin en el navegador veremos
que ha cambiado la fuente, el color del texto y nuestro men principal.

39

Tutorial Django

Para ver con ms detalle lo que nos ofrece Bootstrap vamos a construir nuestra
plantilla de fotos ./album/templates/photo_list.html. Partimos de esta plantilla
{% extends 'base.html' %}
{% load static %}
{% block section %}
<
h2
>Photo</
h2
>
<
hr
>
{% endblock section %}
{% block maincontent %}
{{ object_list }}
{% endblock maincontent %}

rid
y sobre ella vamos a incluir el sistema de g
de Bootstrap para dejarla as.

{% extends 'base.html' %}
{% load static %}
{% block section %}
<
h2
>Photo</
h2
>
<
hr
>
{% endblock section %}
{% block maincontent %}
<
div
class
=
"row well"
>
<
div
class
=
"col-md-6 well"
>
{{ object_list }}
</
div
>
</
div
>
{% endblock maincontent %}

40

Tutorial Django

Aqu lo importante est en las etiquetas div, vemos tres tipos de clases, row,
col-md-6 y well.
row: Siempre tiene que estar incluida en una clase container, en nuestro caso en
./templates/base.html. Se usa para organizar grupos de columnas.
col-md-6: Con esta clase creamos una columna (col-), adaptada a terminales
medianos (col-md-), de tamao 6 (col-md-6); debemos saber que Bootstrap divide el
terminal en doce unidades, por tanto nuestra columna ocupar la mitad del terminal
siempre que este sea mediano o grande, en terminales pequeos como tablets o
mviles ocupar el total del terminal. De igual forma podramos incluir una columna
de dos unidades, adaptada a terminales pequeos y separada una unidad del
margen izquierdo agrupando las clases .col-xs-2 y .col-xs-offset-1 o usar columnas
de tamao 3 para terminales medianos y tamao 7 para terminales pequeos
agrupando .col-sm-7 .col-md-3
well: Simplemente pone el fondo de nuestras etiquetas div en un tono ms oscuro.
Para verlo en accin solo tenemos que refrescar la pantalla y veremos esto en
nuestro porttil.

Vemos que la columna ocupa la mitad del espacio de la fila que la contiene, y
nuestro men aparece tal y como lo dejamos.
Esto sera lo que veramos en un mvil

41

Tutorial Django
Ahora vemos que la columna ocupa todo el espacio dentro de la fila, adems el
men ha cambiado, como no hay espacio suficiente para mostrar todas las
opciones con claridad ahora aparece un desplegable a la derecha.
Con esto podemos dejar la lista de fotos de nuestro lbum de la siguiente forma.

{% extends 'base.html' %}
{% load static %}
{% block section %}
<
h2
>Photo</
h2
>

42

Tutorial Django

<
hr
>
{% endblock section %}
{% block maincontent %}
<
div
class
=
"row well"
>
{% for p in object_list.all %}
<
div
class
=
"col-md-4 well"
>
<
div
class
=
"panel panel-primary"
>
<
div
class
=
"panel-heading"
>
<
h3
class
=
"panel-title"
>{{ p.title }}</
h3
>
</
div
>
<
div
class
=
"panel-body"
>
<
a
href
=
"{% url "photo-detail"
p.id%}">
<
img
src
=
"{% static p.photo.url %}"
style
=
"width:200px;height:200px;"
/>
</
a
>
</
div
>
<
div
class
=
"panel-footer"
>
{% if p.comment %}
{{ p.comment }}
{% else %}
<
a
href
=
"{% url "photo-update"
p.id%}">edit
comments</
a
>
{% endif %}
</
div
>
</
div
>
</
div
>
{% endfor %}
</
div
>
{% endblock maincontent %}

Donde tambin hemos usado P


anels
, uno de los componentes de Bootstrap.

43

Tutorial Django

Como habis podido ver, este framework nos est facilitando mucho las cosas y hay
que tener en cuenta que lo que hemos visto es una mnima parte de lo que nos
ofrece, os animo a que lo exploris a fondo.
Con esto podemos ver los cambios que tiene Git identificados respecto a la ltima
versin que tenemos en el repositorio local.

(tutorial)openwebinars@~/aplicaciones/myapps: git status


En la rama master
Your branch is up-to-date with 'origin/master'.
Cambios no preparados para el
commit
:
(
usegit
add<archivo>... para actualizar lo que se ejecutar)
(
usegit checkout
-- <archivo>... para descartar cambios en le directorio de
trabajo)
modificado: album/templates/album/photo_list.html
modificado: templates/base.html
nohay cambios agregados al
commit(
usegit
add
o git
commit-a)
(tutorial)openwebinars@~/aplicaciones/myapps:

Los aadimos para el siguiente commit, hacemos commit y lo volcamos al


repositorio remoto.
(tutorial)openwebinars@~/aplicaciones/myapps: git add .
(tutorial)openwebinars@~/aplicaciones/myapps: git
commit-m
"Bootstrap"
[
master
77
dadc0] Bootstrap
2files

changed
,
75insertions(+),
44deletions(-)
rewrite album/templates/album/photo_list.html (
79
%)
(tutorial)openwebinars@~/aplicaciones/myapps: git push -u origin
master
Username
for
'https://github.com'
: usuario
Password
for
'https://usuario@github.com'
:
Counting objects:
15
, done.
Delta compression
usingup
to
2threads.
Compressing objects:
100
% (
6
/
6
), done.
Writing objects:
100
% (
8
/
8
),
1.45KiB |
0bytes/s, done.
Total
8(delta
4
), reused
0(delta
0
)
Tohttps://github.com/usuario/my_album.git
e300e4a.
.77
dadc0
master->
master
Branch
master
setup
totrack remote branch
master
fromorigin.
(tutorial)openwebinars@~/aplicaciones/myapps:

y podemos ver los cambios incluidos en el repositorio remoto.

44

Tutorial Django
Hasta aqu hemos dado una vuelta por lo ms bsico de Bootstrap y Git, pero queda
mucho para investigar y mejorar vuestro lbum experimentando.
En el siguiente post veremos cmo mejorarlo an ms usando el trabajo de la
comunidad Django.

45

Tutorial Django

EnOpenWebinars.nettenemostodosestos
cursosatuenteradisposicin

LinuxLPIC-1
Examen101

LinuxLPIC-1
Examen102

NodeJS,
ExpressJSy
MongoDB

AngularJSy
TypeScript

Appsmvilescon
PhoneGap

ServidoresVoIP
conAsterisk

Desarrollo
Frontend
Profesional

Virtualizacinde
Servidorescon
Promox

AppsMvilescon
TitaniumAlloy

Desarrollo
Backendcon
Django

You might also like