Entorno virtual
Para este tutorial usaremos un nuevo directorio djangogirls en tu directorio home:
Haremos un virtualenv llamado myvenv. El comando general estará en el formato:
Trabajar con virtualenv
El comando anterior creará un directorio llamado myvenv (o cualquier nombre que hayas elegido) que contiene nuestro entorno virtual (básicamente un montón de archivos y carpetas).
Instalar Django
Ahora que tienes tu virtualenv iniciado, puedes instalar Django.
Antes de hacer eso, debemos asegurarnos que tenemos la última versión de pip, el software que utilizamos para instalar Django:
command-line
(myvenv) ~$ python -m pip install --upgrade pipInstalar paquetes con un fichero de requisitos (requirements)
Un fichero de requisitos (requirements) tiene una lista de dependencias que se deben instalar mediante pip install:
Primero crea un archivo requirements.txt dentro de tu directorio djangogirls, usando el editor de código que instalaste previamente. Lo puedes hacer mediante abriendo un nuevo archivo en el editor de código y guardándolo como requirements.txt en el directorio djangogirls. Tu directorio se verá así:
djangogirls
└───requirements.txt
Dentro del fichero djangogirls/requirements.txt deberías tener el siguiente texto:
djangogirls/requirements.txt
Django~=2.2.4
Ahora, ejecuta pip install -r requirements.txt para instalar Django.
command-line
(myvenv) ~$ pip install -r requirements.txt
Collecting Django~=2.2.4 (from -r requirements.txt (line 1))
Downloading Django-2.2.4-py3-none-any.whl (7.1MB)
Installing collected packages: Django
Successfully installed Django-2.2.4¡Tu primer proyecto en Django!
En Windows debes ejecutar el siguiente comando. (No olvides incluir el punto . al final):
command-line
(myvenv) C:\Users\Name\djangogirls> django-admin.exe startproject mysite .
El punto
.es crucial porque le dice al script que instale Django en el directorio actual (para el cual el punto.sirve de abreviatura).Nota Cuando teclees los comandos de arriba, recuerda que sólo tienes que escribir la parte que empieza por
django-admin.exe. La parte de(myvenv) C:\Users\Name\djangogirls>que mostramos aquí es sólo un ejemplo del mensaje que aparecerá en tu línea de comandos.
Cambiar la configuración
En settings.py, encuentra la línea que contiene TIME_ZONE y modifícala para elegir tu zona horaria. Por ejemplo:
Si quieres un idioma diferente, cambia el código de idioma cambiando la siguiente línea:
También tenemos que añadir una ruta para archivos estáticos. (Veremos todo acerca de archivos estáticos y CSS más adelante.) Ve al final del archivo, y justo debajo de la entrada STATIC_URL, añade una nueva llamada STATIC_ROOT:
Cuando DEBUG es True y ALLOWED_HOST esta vacío, el host es validado contra ['localhost', '127,0.0.1', '[::1]']. Una vez despleguemos nuestra aplicación este no sera el mismo que nuestro nombre de host en PythonAnywhere así que cambiaremos la siguiente opción:
Configurar una base de datos
Hay una gran variedad de opciones de bases de datos para almacenar los datos de tu sitio. Utilizaremos la que viene por defecto, sqlite3.
Esta ya está configurado en esta parte de tu archivo mysite/settings.py:
python manage.py migrate (necesitamos estar en el directorio de djangogirls que contiene el archivo manage.py). Si eso va bien, deberías ver algo así:manage.py (en la carpeta djangogirls). En la consola, podemos iniciar el servidor web ejecutando python manage.py runserver:Crear una aplicación
Para mantener todo en orden, crearemos una aplicación separada dentro de nuestro proyecto. Es muy bueno tener todo organizado desde el principio. Para crear una aplicación, necesitamos ejecutar el siguiente comando en la consola (dentro de la carpeta de djangogirls donde está el archivo manage.py):
blog y ahora contiene una cantidad de archivos. Los directorios y archivos en nuestro proyecto deberían verse así:mysite/settings.py -- ábrelo en el editor. Tenemos que encontrar INSTALLED_APPS y agregar una línea que contiene 'blog.apps.BlogConfig', justo por encima de ]. El producto final debe tener este aspecto:Crear el modelo del Post
En el archivo blog/models.py definimos todos los objetos llamados Models. Este es un lugar en el cual definiremos nuestra entrada del blog.
Abre blog/models.py en el editor, borra todo, y escribe código como este:
Crear tablas para los modelos en tu base de datos
El último paso aquí es agregar nuestro nuevo modelo a la base de datos. Primero tenemos que hacer saber a Django que hemos hecho cambios en nuestro modelo. (Lo acabamos de crear!) Ve a tu terminal y escribe python manage.py makemigrations blog. Se verá así:
Nota: Recuerda guardar los archivos que edites. De otro modo, tu computador ejecutará las versiones anteriores lo que puede ocasionar errores inesperados.
Django preparó un archivo de migración que ahora tenemos que aplicar a nuestra base de datos. Escribe python manage.py migrate blog y el resultado debería ser:
Administrador de Django
Para agregar, editar y borrar los posts que hemos modelado, usaremos el administrador (admin) de Django.
Abre el fichero blog/admin.py en el editor y reemplaza su contenido con esto:
Como puedes ver, importamos (incluimos) el modelo Post definido en el capítulo anterior. Para hacer nuestro modelo visible en la página del administrador, tenemos que registrar el modelo con admin.site.register(Post).
Ok, es hora de ver nuestro modelo Post. Recuerda ejecutar python manage.py runserver en la consola para correr el servidor web. Ve a tu navegador y escribe la dirección http://127.0.0.1:8000/admin/. Verás una página de inicio de sesión como esta:
python manage.py createsuperuser y pulsa enter.intro para continuar. Luego, verás algo así (donde username y email serán los que escribiste anteriormente):Ve a 'Posts' y curiosea un poco. Añade cinco o seis publicaciones en tu blog. No te preocupes por el contenido -- solo será visible para ti en tu ordenador -- puedes copiar y pegar texto de este tutorial para ir más rápido. :)
Asegúrate de que al menos dos o tres posts (pero no todos) tengan la fecha de publicación definida. Esto será muy poderoso después.
Instalar Git
Puedes descargar Git desde git-scm.com. Puedes hacer click en "Next" en todos los pasos excepto en dos: cuando se te pregunte que selecciones tu editor, selecciona Nano, y en el paso "adjusting your PATH environment", selecciona "Use Git and optional Unix tools from the Windows Command Prompt" (la última opción). Aparte de eso, los valores por defecto son correctos. "Checkout Windows-style, commit Unix-style line endings" tampoco necesita corrección.
No olvides reiniciar el Símbolo del Sistema o el PowerShell una vez que la instalación se complete con éxito.
Crear nuestro repositorio Git
Git sigue los cambios realizados a un grupo determinado de archivos en lo que llamamos un repositorio de código (abreviado "repo"). Vamos a crear uno para nuestro proyecto. Abre la consola y ejecuta los siguientes comandos en el directorio de djangogirls:
Inicializar el repositorio de git es algo que sólo tenemos que hacer una vez por proyecto (y no tendrás que volver a teclear tu nombre de usuario y correo electrónico nunca más).
Git llevará un seguimiento de los cambios realizados en todos los archivos y carpetas en este directorio, pero hay algunos archivos que queremos que ignore. Esto lo hacemos creando un archivo llamado .gitignore en el directorio base. Abre tu editor y crea un nuevo archivo con el siguiente contenido:
.gitignore en la carpeta "djangogirls".git status antes de git add o en cualquier momento en que no sepas muy bien lo que ha cambiado. Esto te ayudará a evitar sorpresas, como subir cambios o archivos que no queríamos subir. El comando git status muestra información sobre cualquier archivo no seguido ("untracked"), modificado ("modified"), preparado ("staged"), el estado de la rama y muchas cosas más. La salida debería ser parecida a esto:Subiendo tu código a Github
Vete a GitHub.com y regístrate para obtener una cuenta de usuario nueva y gratuita. (Si ya lo hiciste en la preparación del taller, ¡eso es genial!) Asegúrate de recordar tu contraseña (agrégala a tu administrador de contraseñas, si usas uno).
A continuación, crea un nuevo repositorio con el nombre "my-first-blog". Deja el checkbox "initialize with a README" sin marcar, deja la opción de .gitignore vacía (ya lo hemos hecho manualmente) y deja la licencia como None.
Ahora necesitas enlazar el repositorio Git en tu ordenador con el repositorio de GitHub.
Escribe lo siguiente en la consola (cambia <your-github-username> por tu nombre de usuario de GitHub, pero sin los símbolos < y > -- fíjate en que la URL debería coincidir con la URL para clonar el repo que acabas de ver):
Configurar nuestro blog en PythonAnywhere
Crea una cuenta en PythonAnywhere
PythonAnywhere es un servicio para ejecutar código Python en servidores "en la nube". Lo vamos a usar para alojar nuestro sitio para que esté disponible en Internet.
Almacenaremos del blog que estamos construyendo sobre Python Anywhere. Crea una cuenta como "Principiante/Beginner" en Python Anywhere (el modo gratuito está bien, no necesitas una tarjeta de crédito).
Crear un token para la API de PythonAnywhere
Este paso solo necesita ser completado una vez. Una vez que te hayas registrado en PythonAnywhere, serás llevado a tu tablón principal. Encontrarás el enlace a la página de tu "Cuenta" en la esquina superior derecha:
Configurar nuestro sitio en PythonAnywhere
Vuelve al dashboard de PythonAnywhere haciendo click en el logo, y escoge la opción de iniciar una consola "Bash" – esta terminal es como la que hay en tu ordenador, pero en el servidor de PythonAnywhere.
Para desplegar una aplicación web en PythonAnywhere necesitas descargar tu código de GitHub y configurar PythonAnywhere para que lo reconozca y lo sirva como una aplicación web. Hay formas de hacerlo manualmente, pero PythonAnywhere tiene una herramienta automática que lo hará todo por nosotros. Lo primero, vamos a instalar la herramienta:Eso debería mostrar en pantalla algunos mensajes como Collecting pythonanywhere, y finalmente una linea diciendo que ha terminado bien: Successfully installed (...) pythonanywhere- (...).
Ahora ejecutaremos el asistente para configurar automáticamente nuestra aplicación desde GitHub. Teclea lo siguiente en la consola de PythonAnywhere (no te olvides de usar tu propio nombre de usuario de GitHub en lugar de <your-github-username>, para que la URL sea como la URL de clonar el repo de GitHub):
En PythonAnywhere todos estos pasos están automatizados, pero son los mismos que tendrías que seguir en cualquier otro proveedor de servidores.
Lo más importante que debes notar en este momento es que tu base de datos en PythonAnywhere está totalmente separada de tu base de datos en tu propia computadora, por lo que puedes tener diferentes publicaciones y cuentas de administrador. Como consecuencia, igual que lo hicimos en tu ordenador, tenemos que crear la cuenta de administrador con el comando createsuperuser. PythonAnywhere ya ha activado el virtualenv automáticamente, así que lo único que tienes que hacer es ejecutar:
Teclea las credenciales para tu usuario admin. Para evitar confusiones, te recomendamos usar el mismo nombre de usuario que usaste en tu ordenador local; aunque a lo mejor prefieres que la contraseña en PythonAnywhere sea más segura.
Ahora, si quieres, también puedes ver tu código en PythonAnywhere con el comando ls:
¡Tu primera URL de Django!
¡Es hora de crear nuestra primera URL! Queremos que 'http://127.0.0.1:8000/' sea la página de inicio del blog y que muestre una lista de post.
También queremos mantener limpio el archivo mysite/urls.py, así que vamos a importar las urls de nuestra aplicación blog en el archivo principal mysite/urls.py.
Vamos, añade la línea para importar blog.urls. Tú también necesitarás cambiar la línea desde django.urls... porque estaremos usando la función include aquí, así que se necesitará añadir ese import a la línea.
El archivo mysite/urls.py debería verse ahora así:
blog.urls
Crea un nuevo fichero vacío llamado urls.py en el directorio blog, y ábrelo en el editor de código. ¡Vale! Añade las dos primeras líneas.
Aquí estamos importando la función de Django path y todos nuestras views desde la aplicación blog (no tenemos una aun, pero veremos eso en un minuto!)
Luego de esto, podemos agregar nuestro primer patrón URL:
Como puedes ver, estamos asociando una vista (view) llamada post_list a la URL raíz. Este patrón de URL coincidirá con una cadena vacía y el solucionador de URL de Django ignorará el nombre de dominio (es decir, http://127.0.0.1:8000/) que prefija la ruta de URL completa. Este patrón le dirá a Django que views.post_list es el lugar correcto al que ir si alguien entra a tu sitio web con la dirección 'http://127.0.0.1:8000/'.
La última parte name='post_list' es el nombre de la URL que se utilizará para identificar a la vista. Puede coincidir con el nombre de la vista pero también puede ser algo completamente distinto. Utilizaremos las URL con nombre más delante en el proyecto así que es importante darle un nombre a cada URL de la aplicación. También deberíamos intentar mantener los nombres de las URL únicos y fáciles de recordar.
Si tratas de visitar http://127.0.0.1:8000/ ahora, encontrarás un mensaje de error 'web page not available' a algo así. Esto es porque el servidor (¿recuerdas que escribimos runserver?) ya no está funcionando. Mira la ventana de la consola del servidor para saber por qué.
blog/views.py
Vale, abre este fichero en el editor y mira lo que hay en él.
No hay demasiadas cosas aquí todavía.
Recuerda que las líneas que comienzan con # son comentarios - significa que Python no las ejecutará.
Creemos una vista (view) como sugiere el comentario. Añade la siguiente mini-vista por debajo:
Como puedes ver, hemos creado una función (def) llamada post_list que acepta request y return una función render que reproduce (construye) nuestra plantilla blog/post_list.html.
Guarda el archivo, ve a http://127.0.0.1:8000/ y mira lo que hemos hecho.
¡Otro error! Leamos lo que está pasando ahora:
¡Tu primera plantilla!
Crear una plantilla significa crear un archivo de plantilla. Todo es un archivo, ¿verdad? Probablemente hayas notado esto ya.
Las plantillas se guardan en el directorio de blog/templates/blog. Así que primero crea un directorio llamado templates dentro de tu directorio blog. Luego crea otro directorio llamado blog dentro de tu directorio de templates:
blog
└───templates
└───blog
(Tal vez te estés preguntando por qué necesitamos dos directorios llamados blog – como verás más adelante, es una convención de nombres que nos facilitará la vida cuando las cosas se pongan más complicadas.)
Y ahora crea un archivo post_list.html (déjalo en blanco por ahora) dentro de la carpeta blog/templates/blog.
Mira cómo se ve su sitio web ahora: http://127.0.0.1:8000/
¡Ningún error más! Felicidades :) Sin embargo, por ahora, tu sitio web no está publicando nada excepto una página en blanco, porque la plantilla también está vacía. Tenemos que arreglarlo.
Abre un fichero nuevo en el editor y escribe lo siguiente:
Ahora, ¿cómo luce tu sitio web? Haz clic para verlo: http://127.0.0.1:8000/Cabeza y cuerpo
Cada página HTML también se divide en dos elementos: head y body.
head es un elemento que contiene información sobre el documento que no se muestra en la pantalla.
body es un elemento que contiene todo lo que se muestra como parte de la página web.
Usamos <head> para decirle el navegador acerca de la configuración de la página y <body> para decir lo que realmente está en la página.
Por ejemplo, puedes ponerle un título a la página web dentro de la <head>, así:
blog/templates/blog/post_list.html
<html>
<head>
<title>Ola's blog</title>
</head>
<body>
<p>Hi there!</p>
<p>It works!</p>
</body>
</html>
Guarda el archivo y actualiza tu página.
¿Observas cómo el navegador ha comprendido que "Ola's blog" es el título de tu página? Ha interpretado <title>Ola's blog</title> y colocó el texto en la barra de título de tu navegador (también se utilizará para marcadores y así sucesivamente).
Probablemente también hayas notado que cada etiqueta de apertura coincide con una etiqueta de cierre, con un /, y que los elementos son anidados (es decir, no puedes cerrar una etiqueta particular hasta que todos los que estaban en su interior se hayan cerrado también).
Es como poner cosas en cajas. Tienes una caja grande, <html></html>; en su interior hay <body></body>, y que contiene las cajas aún más pequeñas: <p></p>.
Tienes que seguir estas reglas de etiquetas de cierre y de anidación de elementos - si no lo haces, el navegador puede no ser capaz de interpretarlos apropiadamente y tu página se mostrará incorrectamente.
Personaliza tu plantilla
¡Ahora puedes divertirte un poco y tratar de personalizar tu plantilla! Aquí hay algunas etiquetas útiles para eso:
<h1>Un título</h1>- para tu título más importante<h2>Un subtítulo</h2>- para el título del siguiente nivel<h3>Un subsubtítulo</h3>- ... y así hasta<h6><p>Un párrafo de texto</p><em>texto</em>- pone en cursiva tu texto<strong>texto</strong>- pone en negrita tu texto<br>va en otra línea (no puedes poner nada dentro de br y no hay etiqueta de cierre)<a href="https://djangogirls.org">link</a>- crea un vínculo<ul><li>primer elemento</li><li>segundo elemento</li></ul>- crea una lista, ¡igual que esta!<div></div>- define una sección de la página
Aquí va un ejemplo de una plantilla completa, cópialo y pégalo en blog/templates/blog/post_list.html:
Aquí hemos creado tres secciones div.
- El primer elemento
divcontiene el título de nuestro blog - es un encabezado y un enlace - Otros dos elementos
divcontienen nuestras publicaciones (posts) del blog con su fecha de publicación,h2con el título del post que es clickable y dosps (párrafos) de texto, uno para la fecha y otro para el contenido del post.
Nos da este efecto:
Haz commit, y sube tu código a GitHub
En primer lugar, vamos a ver qué archivos han cambiado desde la última puesta en marcha (ejecute estos comandos localmente, no en PythonAnywhere):
$ git status
Asegúrate de que estás en el directorio djangogirls y vamos a decirle a git que incluya todos los cambios en este directorio:
$ git add --all .
Nota
--allsignifica quegittambien reconocerá si has borrado archivos (por defecto, solo reconoce archivos nuevos o modificados). También recuerda (del capítulo 3) que.significa el directorio actual.
Antes de que subamos todos los archivos, vamos a ver qué es lo que git subirá (todos los archivos que git cargará deberían aparecer en verde):
$ git status
Ya casi estamos, ahora es tiempo de decirle que guarde este cambio en su historial. Vamos a darle un "mensaje de commit" donde describimos lo que hemos cambiado. Puedes escribir cualquier cosa que te gustaría en esta etapa, pero es útil escribir algo descriptivo para que puedes recordar lo que has hecho en el futuro.
$ git commit -m "Cambie el HTML para la página."
Nota Asegúrate de usar comillas dobles alrededor del mensaje de commit.
Una vez hecho esto, subimos (push) los cambios a Github:
$ git pushDescarga tu nuevo código a PythonAnywhere y actualiza tu aplicación web
- Abre la página de consolas de PythonAnywhere y ve a tu consola Bash (o comienza una nueva). Luego, ejecuta:
PythonAnywhere command-line
$ cd ~/<your-pythonanywhere-domain>.pythonanywhere.com
$ git pull
[...]
Necesitarás sustituir <your-pythonanywhere-domain> con tu actual nombre de subdominio PythonAnywhere, sin los paréntesis angulares o corchetes. Tu nombre de subdominio es normalmente tu nombre de usuario PythonAnywhere, pero en algunos casos puede ser un poco diferente (por ejemplo, si tu nombre de usuario contiene letras mayúsculas). Así, si este comando no funciona, usa el comando ls(listar archivos) para encontrar tu actual subdominio/nombre-carpeta, y muévete allí con cd.
Ahora mira cómo se descarga tu código. Si quieres comprobar que efectivamente ha llegado bien, puedes ir a la página "Files" y ver tu código en PythonAnywhere (puedes ir a otras páginas de PythonAnywhere desde el botón de la esquina superior derecha de la página de la consola).
¡Tu nueva versión ya debería estar publicada! Ve al navegador y refresca tu sitio web. Deberías ver los cambios. :)
Django shell
Abre tu consola local (no la de PythonAnywhere) y escribe este comando:
(myvenv) ~/djangogirls$ python manage.py shell
El resultado debería ser:
(InteractiveConsole)
Todos los objetos
Vamos a mostrar todos nuestros posts primero. Puedes hacerlo con el siguiente comando:
>>> Post.objects.all()
Traceback (most recent call last):
File "<console>", line 1, in <module>
NameError: name 'Post' is not defined¡Uy! Apareció un error. Nos dice que Post no existe. Esto es correcto, ¡olvidamos importarlo!
>>> from blog.models import PostVamos a importar el modelo Post de blog.models. Y probamos de nuevo a mostrar todas las publicaciones (posts):
>>> Post.objects.all()
<QuerySet [<Post: my post title>, <Post: another post title>]>Crear objetos
Esta es la forma de crear un nuevo objeto Post en la base de datos:
>>> Post.objects.create(author=me, title='Sample title', text='Test')Pero nos falta un ingrediente aquí: me. Tenemos que pasar una instancia del modelo User como autor. ¿Eso cómo se hace?
Primero importemos el modelo User:
>>> from django.contrib.auth.models import User¿Qué usuarios tenemos en nuestra base de datos? Prueba esto:
>>> User.objects.all()
<QuerySet [<User: ola>]>¡Este es el superusuario que hemos creado antes! Ahora, vamos a obtener una instancia de este usuario (cambia el código para usar tu propio nombre de usuario):
>>> me = User.objects.get(username='ola')Como ves, ya hemos obtenido (get) un usuario (User) cuyo username es igual a 'ola'. ¡Mola!
Ahora, finalmente, podemos crear nuestra entrada:
>>> Post.objects.create(author=me, title='Sample title', text='Test')
<Post: Sample title>¡Hurra! ¿Quieres probar si funcionó?
>>> Post.objects.all()
<QuerySet [<Post: my post title>, <Post: another post title>, <Post: Sample title>]>
¡Ahí está, una entrada de blog más en la lista!
Filtrar objetos
Una parte importante de los QuerySets es la habilidad para filtrar los resultados. Digamos que queremos encontrar todos los post del usuario ola. Usaremos filter en vez de all en Post.objects.all(). Entre paréntesis estableceremos qué condición (o condiciones) debe cumplir un post del blog para aparecer como resultado en nuestro queryset. En nuestro caso sería author es igual a me. La forma de escribirlo en Django es: author=me. Ahora nuestro bloque de código tiene este aspecto:
>>> Post.objects.filter(author=me)
<QuerySet [<Post: Sample title>, <Post: Post number 2>, <Post: My 3rd post!>, <Post: 4th title of post>]¿O quizá queremos ver todas las entradas que contengan la palabra 'title' en el campo title?
>>> Post.objects.filter(title__contains='title')
<QuerySet [<Post: Sample title>, <Post: 4th title of post>]>También puedes obtener una lista de todos los post publicados. Lo hacemos filtrando los post que tienen la fecha de publicación, published_date, en el pasado:
>>> from django.utils import timezone
>>> Post.objects.filter(published_date__lte=timezone.now())
<QuerySet []>Por desgracia, el post que hemos añadido desde la consola de Python aún no está publicado. ¡Pero lo podemos cambiar! Primero obtén una instancia de la entrada que queremos publicar:
>>> post = Post.objects.get(title="Sample title")Y luego publícala con nuestro método publish:
>>> post.publish()Ahora vuelve a intentar obtener la lista de posts publicados (pulsa la tecla de "flecha arriba" tres veces y pulsa enter):
>>> Post.objects.filter(published_date__lte=timezone.now())
<QuerySet [<Post: Sample title>]>Ordenar objetos
Los QuerySets también te permiten ordenar la lista de objetos. Intentemos ordenarlos por el campo created_date:
>>> Post.objects.order_by('created_date')
<QuerySet [<Post: Sample title>, <Post: Post number 2>, <Post: My 3rd post!>, <Post: 4th title of post>]>También podemos invertir el orden agregando - al principio:
>>> Post.objects.order_by('-created_date')
<QuerySet [<Post: 4th title of post>, <Post: My 3rd post!>, <Post: Post number 2>, <Post: Sample title>]>Consultas complejas a través de encadenamiento de métodos
Como ves, algunos métodos en Post.objects devuelven un QuerySet. Los mismos métodos pueden ser llamados también en un QuerySet, y entonces devolverán un nuevo QuerySet. También puedes combinar QuerySets encadenando uno con otro:
>>> Post.objects.filter(published_date__lte=timezone.now()).order_by('published_date')
<QuerySet [<Post: Post number 2>, <Post: My 3rd post!>, <Post: 4th title of post>, <Post: Sample title>]>Es muy potente y te permite escribir consultas bastante complejas.
¡Genial! ¡Ahora estás lista para la siguiente parte! Para cerrar la consola, escribe esto:
>>> exit()
$Datos dinámicos en plantillas
Tenemos diferentes piezas en su lugar: el modelo Post está definido en models.py, tenemos a post_list en views.py y la plantilla agregada. ¿Pero cómo haremos realmente para que nuestros posts aparezcan en nuestra plantilla HTML? Porque eso es lo que queremos, tomar algún contenido (modelos guardados en la base de datos) y mostrarlo adecuadamente en nuestra plantilla, ¿no?
Esto es exactamente lo que las views se supone que hacen: conectar modelos con plantillas. En nuestra view post_list necesitaremos tomar los modelos que deseamos mostrar y pasarlos a una plantilla. En una vista decidimos qué (modelo) se mostrará en una plantilla.
Muy bien, entonces ¿cómo lo logramos?
Tenemos que abrir blog/views.py en el editor. De momento post_list view tiene esto:
blog/views.py
from django.shortcuts import render
def post_list(request):
return render(request, 'blog/post_list.html', {})
QuerySet
Ya debes estar familiarizada con la forma en que funcionan los QuerySets. Hablamos de ellos en el capítulo Django ORM (QuerySets).
Así que ahora nos interesa tener una lista de post publicados ordenados por published_date (fecha de publicación), ¿no? ¡Ya lo hicimos en el capítulo QuerySets!
blog/views.py
Post.objects.filter(published_date__lte=timezone.now()).order_by('published_date')
Abre blog/views.py en el editor, y añade este trozo de código a la función def post_list(request) -- pero no te olvides de añadir from django.utils import timezone antes:
blog/views.py
from django.shortcuts import render
from django.utils import timezone
from .models import Post
def post_list(request):
posts = Post.objects.filter(published_date__lte=timezone.now()).order_by('published_date')
return render(request, 'blog/post_list.html', {})Una cosa más
Sería bueno ver si tu sitio web seguirá funcionando en la Internet pública, ¿no? Vamos a intentar desplegar de nuevo en PythonAnywhere. Aquí va un resumen de los pasos…
- Lo primero, sube tu código a GitHub
command-line
$ git status
[...]
$ git add --all .
$ git status
[...]
$ git commit -m "Templates modificados para mostrar post desde base de datos."
[...]
$ git push- Luego, vuelve a entrar en PythonAnywhere y ve a tu consola Bash (o inicia una nueva), y ejecuta:
PythonAnywhere command-line
$ cd <your-pythonanywhere-domain>.pythonanywhere.com
$ git pull
[...](Recuerda sustituir <your-pythonanywhere-domain> con tu subdominio de PythonAnywhere real, sin los paréntesis angulares.)
- Y finalmente, dirígete a la página "Web" y haz clic en Reload en tu aplicación web. (Para ir a otras páginas de PythonAnywhere desde la consola, haz clic en el botón de la esquina superior derecha.) Los cambios deberían estar visibles en https://subdomain.pythonanywhere.com -- ¡compruébalo en el navegador! Si ves distintas publicaciones en el sitio en PythonAnywhere de las que tienes en tu servidor local, es lo normal. Tienes dos bases de datos, una en tu ordenador local y otra en PythonAnywhere y no tienen por qué tener el mismo contenido.
Instalar Bootstrap
Para instalar Bootstrap, abre tu fichero .html en el editor de código y añade esto a la sección <head>:
blog/templates/blog/post_list.html
<link rel="stylesheet" href="//maxcdn.bootstrapcdn.com/bootstrap/3.2.0/css/bootstrap.min.css">
<link rel="stylesheet" href="//maxcdn.bootstrapcdn.com/bootstrap/3.2.0/css/bootstrap-theme.min.css">Dónde poner los archivos estáticos en Django
Django ya sabe dónde encontrar los archivos estáticos de la app "admin". Ahora necesitamos añadir los archivos estáticos de nuestra aplicación, blog.
Crearemos una carpeta llamada static dentro de la app blog:
djangogirls
├── blog
│ ├── migrations
│ ├── static
│ └── templates
└── mysite¡Tu primer archivo CSS!
Vamos a crear un archivo CSS, para añadir tu propio estilo a la página. Crea un nuevo directorio llamado css dentro de la carpeta static. A continuación, crea un nuevo archivo llamado blog.css dentro de la carpeta css. ¿Listos?
djangogirls
└─── blog
└─── static
└─── css
└─── blog.css
¡Vamos a escribir algo de CSS! Abre el archivo blog/static/css/blog.css en el editor de código
El navegador lee los archivos en el orden que le son dados, por lo que debemos asegurarnos de que está en el lugar correcto. De lo contrario, el código en nuestro archivo podría ser reemplazado por código en nuestros archivos Bootstrap. Le acabamos de decir a nuestra plantilla dónde se encuentra nuestro archivo CSS.
Ahora tu archivo debe tener este aspecto:
blog/templates/blog/post_list.html
{% load static %}
<html>
<head>
<title>Django Girls blog</title>
<link rel="stylesheet" href="//maxcdn.bootstrapcdn.com/bootstrap/3.2.0/css/bootstrap.min.css">
<link rel="stylesheet" href="//maxcdn.bootstrapcdn.com/bootstrap/3.2.0/css/bootstrap-theme.min.css">
<link rel="stylesheet" href="{% static 'css/blog.css' %}">
</head>
<body>
<div>
<h1><a href="/">Django Girls Blog</a></h1>
</div>
{% for post in posts %}
<div>
<p>published: {{ post.published_date }}</p>
<h2><a href="">{{ post.title }}</a></h2>
<p>{{ post.text|linebreaksbr }}</p>
</div>
{% endfor %}
</body>
</html>De acuerdo, ¡guarda el archivo y actualiza el sitio!En el archivo blog/static/css/blog.css deberías añadir el siguiente código:
blog/static/css/blog.css
h1 a, h2 a {
color: #C25100;
}Añade esto a tu CSS, guarda el archivo y ¡mira cómo funciona!
¿Quizá podríamos personalizar la tipografía del título? Pega esto en la sección <head> del archivo blog/templates/blog/post_list.html:
blog/templates/blog/post_list.html
<link href="//fonts.googleapis.com/css?family=Lobster&subset=latin,latin-ext" rel="stylesheet" type="text/css">Como antes, revisa el orden y pon antes del enlace a blog/static/css/blog.css. Esta línea importará un estilo de letra llamada Lobster de Google Fonts (https://www.google.com/fonts).
Encuentra el bloque de declaración (el código entre las llaves { y }) h1 a en el archivo CSS blog/static/css/blog.css. Ahora añade la línea font-family: 'Lobster'; entre las llaves y actualiza la página:
blog/static/css/blog.css
h1 a, h2 a {
color: #FCA205;
font-family: 'Lobster';
}¡Genial!
Como ya hemos dicho, CSS tiene un concepto de clases. Las clases te permiten dar un nombre a una parte del código HTML para aplicar estilos solo a esta parte, sin afectar a otras. ¡Esto puede ser súper útil! Quizá tienes dos divs haciendo algo diferente (como el encabezado y el texto de tu publicación). Las clases pueden ayudarte a asignarles estilos distintos.
Adelante, nombra algunas partes del código HTML. Añade una clase llamada page-header a tu div que contiene el encabezado, así:
blog/templates/blog/post_list.html
<div class="page-header">
<h1><a href="/">Django Girls Blog</a></h1>
</div>Adelante, nombra algunas partes del código HTML. Añade una clase llamada page-header a tu div que contiene el encabezado, así:
blog/templates/blog/post_list.html
<div class="page-header">
<h1><a href="/">Django Girls Blog</a></h1>
</div>Y ahora añade una clase post a tu div que contiene una publicación del blog.
blog/templates/blog/post_list.html
<div class="post">
<p>publicado: {{ post.published_date }}</p>
<h2><a href="">{{ post.title }}</a></h2>
<p>{{ post.text|linebreaksbr }}</p>
</div>Ahora añadiremos bloques de declaración a varios selectores. Los selectores que comienzan con . hacen referencia a clases. Hay muchos tutoriales y explicaciones excelentes sobre CSS en la Web que te pueden ayudar a entender el código que sigue a continuación. Por ahora, copia y pega lo siguiente en tu archivo blog/static/css/blog.css:
blog/static/css/blog.css
.page-header {
background-color: #C25100;
margin-top: 0;
padding: 20px 20px 20px 40px;
}
.page-header h1, .page-header h1 a, .page-header h1 a:visited, .page-header h1 a:active {
color: #ffffff;
font-size: 36pt;
text-decoration: none;
}
.content {
margin-left: 40px;
}
h1, h2, h3, h4 {
font-family: 'Lobster', cursive;
}
.date {
color: #828282;
}
.save {
float: right;
}
.post-form textarea, .post-form input {
width: 100%;
}
.top-menu, .top-menu:hover, .top-menu:visited {
color: #ffffff;
float: right;
font-size: 26pt;
margin-right: 20px;
}
.post {
margin-bottom: 70px;
}
.post h2 a, .post h2 a:visited {
color: #000000;
}Crea una plantilla base
Una plantilla base es la plantilla más básica que extiendes en cada página de tu sitio web.
Vamos a crear un archivo base.html en blog/templates/blog/:
blog
└───templates
└───blog
base.html
post_list.htmlAhora, ábrelo en el editor de código y copia todo el contenido de post_list.html en base.html, así:
blog/templates/blog/base.html
{% load static %}
<html>
<head>
<title>Django Girls blog</title>
<link rel="stylesheet" href="//maxcdn.bootstrapcdn.com/bootstrap/3.2.0/css/bootstrap.min.css">
<link rel="stylesheet" href="//maxcdn.bootstrapcdn.com/bootstrap/3.2.0/css/bootstrap-theme.min.css">
<link href='//fonts.googleapis.com/css?family=Lobster&subset=latin,latin-ext' rel='stylesheet' type='text/css'>
<link rel="stylesheet" href="{% static 'css/blog.css' %}">
</head>
<body>
<div class="page-header">
<h1><a href="/">Django Girls Blog</a></h1>
</div>
<div class="content container">
<div class="row">
<div class="col-md-8">
{% for post in posts %}
<div class="post">
<div class="date">
{{ post.published_date }}
</div>
<h2><a href="">{{ post.title }}</a></h2>
<p>{{ post.text|linebreaksbr }}</p>
</div>
{% endfor %}
</div>
</div>
</div>
</body>
</html>Luego, en base.html reemplaza por completo tu <body> (todo lo que haya entre <body> and </body>) con esto:
blog/templates/blog/base.html
<body>
<div class="page-header">
<h1><a href="/">Django Girls Blog</a></h1>
</div>
<div class="content container">
<div class="row">
<div class="col-md-8">
{% block content %}
{% endblock %}
</div>
</div>
</div>
</body>
Seguro que ya te has dado cuenta de que lo que hemos hecho ha sido cambiar todo lo que había entre {% for post in posts %} y {% endfor %} por
blog/templates/blog/base.html
{% block content %}
{% endblock %}¡Y ya está! Guarda el fichero y comprueba que el sitio web sigue funcionando como antes. :)Crea un enlace a la página de detalle de una publicación
Empezaremos añadiendo un enlace al fichero blog/templates/blog/post_list.html. Ábrelo en el editor; de momento debería tener este contenido:
blog/templates/blog/post_list.html
{% extends 'blog/base.html' %}
{% block content %}
{% for post in posts %}
<div class="post">
<div class="date">
{{ post.published_date }}
</div>
<h2><a href="">{{ post.title }}</a></h2>
<p>{{ post.text|linebreaksbr }}</p>
</div>
{% endfor %}
{% endblock %}Queremos tener un enlace del título de una publicación en la lista de publicaciones al detalle de la misma. Cambiemos <h2><a href="">{{ post.title }}</a></h2> para enlazarla a la página detalle del post:
blog/templates/blog/post_list.html
<h2><a href="{% url 'post_detail' pk=post.pk %}">{{ post.title }}</a></h2>Crea una URL al detalle de una publicación
Vamos a crear una URL en urls.py para nuestra view post_detail!
Queremos que el detalle de la primera entrada se visualice en esta URL: http://127.0.0.1:8000/post/1/
Vamos a crear una URL en el fichero blog/urls.py que dirija a Django hacia una vista llamada post_detail, que mostrará una entrada de blog completa. Abre el fichero blog/urls.py en el editor, y añade la línea path('post/<int:pk>/', views.post_detail, name='post_detail'), para que el fichero quede así:
blog/urls.py
from django.urls import path
from . import views
urlpatterns = [
path('', views.post_list, name='post_list'),
path('post/<int:pk>/', views.post_detail, name='post_detail'),
]Añade la vista de detalle de la publicación
Esta vez nuestra vista tomará un parámetro adicional pk. Nuestra vista necesita recibirlo, ¿verdad? Así que definiremos nuestra función como def post_detail (request, pk):. Tenga en cuenta que necesitamos usar exactamente el mismo nombre que el que especificamos en urls (pk). ¡Omitir esta variable es incorrecto y resultará en un error!
Ahora, queremos obtener solo un post. Para ello podemos usar querysets como este:
blog/views.py
Post.objects.get(pk=pk)Crear una plantilla para post detail
Vamos crear un fichero en blog/templates/blog llamado post_detail.html, y abrirlo en el editor de código.
Se verá así:
blog/templates/blog/post_detail.html
{% extends 'blog/base.html' %}
{% block content %}
<div class="post">
{% if post.published_date %}
<div class="date">
{{ post.published_date }}
</div>
{% endif %}
<h2>{{ post.title }}</h2>
<p>{{ post.text|linebreaksbr }}</p>
</div>
{% endblock %}Una vez más estamos extendiendo base.html. En el bloque content queremos mostrar la fecha de publicación (si existe), título y texto de nuestros posts. Pero deberíamos discutir algunas cosas importantes, ¿cierto?
{% if ... %} ... {% endif %} es un template tag que podemos usar cuando querramos ver algo. (Recuerdas if ... else .. del capítulo Intruducción a Python?) Ahora queremos mirar si la published_date de un post no esta vacía.
Bien, podemos actualizar nuestra página y ver si TemplateDoesNotExist se ha ido.
¡Hora de despliegue!
Sería bueno verificar que tu sitio web aún funcionará en PythonAnywhere, ¿cierto? Intentemos desplegar de nuevo.
command-line
$ git status
$ git add -A .
$ git status
$ git commit -m "Agregadas vistas y plantilla para el detalle del post del blog así como también CSS para el sitio."
$ git pushLuego, en una consola Bash de PythonAnywhere:
PythonAnywhere command-line
$ cd ~/<your-pythonanywhere-domain>.pythonanywhere.com
$ git pull
[...]Actualizar los ficheros estáticos (static files) en el servidor
Normalmente, los servidores como PythonAnywhere tratan los ficheros estáticos (como los ficheros CSS) de manera diferente a los ficheros de Python. Se llaman estáticos porque el servidor no debe ejecutarlos, sino servirlos tal cual. Y por ello se tratan por separado para servirlos más rápido. Como consecuencia, si cambiamos nuestros ficheros CSS, tenemos que ejecutar un comando extra en el servidor para decirle que los actualice. Este comando se llama collectstatic.
Activa el virtualenv si no estaba activado de antes (en PythonAnywhere se usa el comando workon, es igual que el comando source myenv/bin/activate que usamos en local):
PythonAnywhere command-line
$ workon <your-pythonanywhere-domain>.pythonanywhere.com
(ola.pythonanywhere.com)$ python manage.py collectstatic
[...]Formularios de Django
Lo último que haremos en nuestro sitio web será crear una forma agradable de agregar y editar posts en el blog. El admin de Django está bien, pero es bastante difícil de personalizar y hacerlo bonito. Con forms tendremos un poder absoluto sobre nuestra interfaz; ¡podemos hacer casi cualquier cosa que podamos imaginar!
Lo bueno de los formularios de Django es que podemos definirlos desde cero o crear un ModelForm, el cual guardará el resultado del formulario en el modelo.
Esto es exactamente lo que queremos hacer: crearemos un formulario para nuestro modelo Post.
Como cada parte importante de Django, los formularios tienen su propio archivo: forms.py.
Necesitamos crear un archivo con este nombre en el directorio blog.
blog
└── forms.py
Vale, ábrelo en el editor de código y teclea lo siguiente:
blog/forms.py
from django import forms
from .models import Post
class PostForm(forms.ModelForm):
class Meta:
model = Post
fields = ('title', 'text',)
Lo primero, necesitamos importar Django forms (from django import forms) y nuestro modelo Post (from .models import Post).
Enlace a una página con el formulario
Ahora toca abrir el fichero blog/templates/blog/base.html en el editor. Vamos a añadir un enlace en el div llamado page-header:
blog/templates/blog/base.html
<a href="{% url 'post_new' %}" class="top-menu"><span class="glyphicon glyphicon-plus"></span></a>
Ten en cuenta que queremos llamar a nuestra nueva vista post_new. La clase "glyphicon glyphicon-plus" es proporcionada por el tema de bootstrap que estamos utilizando, y nos mostrará un signo de suma.
Después de agregar la línea, tu archivo html debería lucir de esta forma:
blog/templates/blog/base.html
{% load static %}
<html>
<head>
<title>Django Girls blog</title>
<link rel="stylesheet" href="//maxcdn.bootstrapcdn.com/bootstrap/3.2.0/css/bootstrap.min.css">
<link rel="stylesheet" href="//maxcdn.bootstrapcdn.com/bootstrap/3.2.0/css/bootstrap-theme.min.css">
<link href='//fonts.googleapis.com/css?family=Lobster&subset=latin,latin-ext' rel='stylesheet' type='text/css'>
<link rel="stylesheet" href="{% static 'css/blog.css' %}">
</head>
<body>
<div class="page-header">
<a href="{% url 'post_new' %}" class="top-menu"><span class="glyphicon glyphicon-plus"></span></a>
<h1><a href="/">Django Girls Blog</a></h1>
</div>
<div class="content container">
<div class="row">
<div class="col-md-8">
{% block content %}
{% endblock %}
</div>
</div>
</div>
</body>
</html>
Después de guardar y refrescar la página http://127.0.0.1:8000 verás el - ya conocido - error NoReverseMatch. ¿Es así? ¡Vamos bien!
URL
Abrimos blog/urls.py en el editor para añadir una línea:
blog/urls.py
path('post/new', views.post_new, name='post_new'),
Y el código final tendrá este aspecto:
blog/urls.py
from django.urls import path
from . import views
urlpatterns = [
path('', views.post_list, name='post_list'),
path('post/<int:pk>/', views.post_detail, name='post_detail'),
path('post/new/', views.post_new, name='post_new'),
]
Después de actualizar el sitio, veremos un AttributeError, puesto que no tenemos la vista post_new implementada. Añadaáosla de una vez.
Vista post_new
Ahora abre el fichero blog/views.py en el editor y añade estas líneas con el resto de imports from:
blog/views.py
from .forms import PostForm
Y ahora nuestra vista:
blog/views.py
def post_new(request):
form = PostForm()
return render(request, 'blog/post_edit.html', {'form': form})
Para crear un nuevo formulario Post, tenemos que llamar a PostForm() y pasarlo a la plantilla. Volveremos a esta vista pero, por ahora, vamos a crear rápidamente una plantilla para el formulario.
Plantilla
Tenemos que crear un fichero post_edit.html el el directorio blog/templates/blog, y abrirlo en el editor de código. Para hacer que un formulario funcione necesitamos varias cosas:
- Tenemos que mostrar el formulario. Podemos hacerlo, por ejemplo, con un sencillo
{{ form.as_p }}. - La línea anterior tiene que estar dentro de una etiqueta de formuLario HTML:
<form method="POST">...</form>. - Necesitamos un botón
Guardar. Lo hacemos con un botón HTML:<button type='submit'>Save</button>. - Finalmente justo después de abrir la etiqueta
<form ...>tenemos que añadir{% csrf_token %}. ¡Esto es muy importante ya que hace que tus formularios sean seguros! Si olvidas este pedazo, Django se molestará cuando intentes guardar el formulario:

Bueno, miremos lo que se debería ver el HTML en post_edit.html:
blog/templates/blog/post_edit.html
{% extends 'blog/base.html' %}
{% block content %}
<h2>New post</h2>
<form method="POST" class="post-form">{% csrf_token %}
{{ form.as_p }}
<button type="submit" class="save btn btn-default">Save</button>
</form>
{% endblock %}Guardar el formulario
Abre blog/views.py de nuevo en el editor. De momento todo lo que tenemos en la vista post_new es lo siguiente:
blog/views.py
def post_new(request):
form = PostForm()
return render(request, 'blog/post_edit.html', {'form': form})Editar formulario
Ahora sabemos cómo agregar un nuevo formulario. Pero, ¿qué pasa si queremos editar uno existente? Es muy similar a lo que acabamos de hacer. Creemos rápidamente algunas cosas importantes. (si no entiendes algo, pregúntale a tu tutora o tutor, o revisa lo capítulos anteriores, son temas que ya hemos cubierto.)
Abre blog/templates/blog/post_detail.html en el editor de código y añade la línea
blog/templates/blog/post_detail.html
<a class="btn btn-default" href="{% url 'post_edit' pk=post.pk %}"><span class="glyphicon glyphicon-penUna cosa más: ¡Tiempo de despliegue!
Veamos si todo esto funciona en PythonAnywhere. ¡Tiempo de hacer otro despliegue!
- Lo primero, haz commit de tus últimos cambios y súbelo (push) a GitHub:
command-line
$ git status
$ git add --all .
$ git status
$ git commit -m "Added views to create/edit blog post inside the site."
$ git push
- Luego, en una consola Bash de PythonAnywhere
PythonAnywhere command-line
$ cd ~/<your-pythonanywhere-domain>.pythonanywhere.com
$ git pull
[...]- Para terminar ve a "Web" page (usa el botón del menú de la esquina superior derecha, encima de la consola) y haz click en Reload. Recarga tu https://subdomain.pythonanywhere.com blog para ver los cambios.
¡Y eso debería ser todo! Felicidades :)
Comentarios
Publicar un comentario