Extiende tu aplicación

Ya hemos completado todos los diferentes pasos necesarios para la creación de nuestro sitio web: sabemos cómo escribir un modelo, URL, vista y plantilla. También sabemos cómo hacer que nuestro sitio web sea bonito.

¡Hora de practicar!

Lo primero que necesitamos en nuestro blog es, obviamente, una página para mostrar un post, ¿cierto?

Ya tenemos un modelo Post, así que no necesitamos añadir nada a models.py.

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>

Es hora de explicar el misterioso{% url 'post_detail' pk=post.pk %}. Como probablemente sospeches, la notación {% %} significa que estamos utilizando Django template tags. ¡Esta vez usaremos uno que creará un URL para nosotros!

La parte depost_detail significa que Django estará esperando un URL en blog/urls.py con el nombre=post_detail

¿Y ahora qué pasa con pk=post.pk? pk se refiere a primary key (clave primaria), la cual es un nombre único por cada registro en una base de datos. Debido a que no especificamos una llave primaria en nuestro modelo Post, Django creará una por nosotros (por defecto, un número que incrementa una unidad por cada registro, por ejemplo, 1, 2, 3) y lo añadirá como un campo llamado pk a cada uno de nuestros posts. Accedemos a la clave primaria escribiendo post.pk, del mismo modo en que accedemos a otros campos (título, autor, etc.) en nuestro objeto Post!

Ahora cuando vayamos a: http://127.0.0.1:8000/ tendremos un error (como era de esperar, ya que no tenemos una URL o una vista para post_detail). Se verá así:

NoReverseMatch error

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'),
]

Esta parte post/<int:pk>/ especifica un patrón de URL – ahora lo explicamos:

  • post/ significa que la URL debería empezar con la palabra post seguida por una /. Hasta aquí bien.
  • <int:pk> – esta parte tiene más miga. Significa que Django buscará un número entero y se lo pasará a la vista en una variable llamada pk.
  • / – necesitamos otra / al final de la URL.

Esto quiere decir que si pones http://127.0.0.1:8000/post/5/ en tu navegador, Django entenderá que estás buscando una vista llamada post_detail y transferirá la información de que pk es igual a 5 a esa vista.

OK, ¡hemos añadido un nuevo patrón de URL a blog/urls.py! Actualizamos la página: http://127.0.0.1:8000/ y, ¡boom! El servidor vuelve a dejar de funcionar. Echa un vistazo a la consola – como era de esperar, ¡hay otro error!

AttributeError

¿Recuerdas cuál es el próximo paso? ¡Añadir una vista!

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)

Pero este código tiene un problema. Si no hay ningún Post con esa clave primaria (pk), ¡tendremos un error muy feo!

DoesNotExist error

¡No queremos eso! Por suerte, Django tiene una función que se encarga de eso: get_object_or_404. En caso de que no haya ningún Post con el pk dado se mostrará una página mucho más agradable, Page Not Found 404.

Page not found

La buena noticia es que puedes crear tu propia página Page Not Found y diseñarla como desees. Pero por ahora no es tan importante, así que lo omitiremos.

¡Es hora de agregar una view a nuestro archivo views.py!

En blog/urls.py creamos un regla de URL denominada post_detail que hace referencia a una vista llamada view.post_detail. Esto significa que Django va a estar esperando una función llamada post_detail de vista en blog/views.py.

Deberíamos abrir blog/views.py en el editor y añadir el siguiente código cerca de los otros import from:

blog/views.py

from django.shortcuts import render, get_object_or_404

Y al final del archivo agregamos nuestra view:

blog/views.py

def post_detail(request, pk):
    post = get_object_or_404(Post, pk=pk)
    return render(request, 'blog/post_detail.html', {'post': post})

Sí. Es hora de actualizar la página: http://127.0.0.1:8000/

Post list view

¡Funcionó! Pero, ¿qué pasa cuando haces click en un enlace en el título del post?

TemplateDoesNotExist error

¡Oh, no! ¡Otro error! Pero ya sabemos cómo lidiar con eso, ¿no? ¡Tenemos que añadir una plantilla!

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.

Post detail page

¡Yay! ¡Funciona!

¡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 push

Luego, en una consola Bash de PythonAnywhere:

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.)

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
[...]

El comando manage.py collectstatic es un poco como el comando manage.py migrate. Hacemos cambios en nuestro código y luego le decimos a Django que los aplique, bien a la colección de ficheros estáticos o bien a la base de datos.

En cualquier caso, ahora estaremos listos para saltar sobre la página "Web" (desde el botón del menú en el lado superior derecho de la consola), realizar la Recarga, y mirar en la página https://subdomain.pythonanywhere.com para ver el resultado.

¡Y eso debería ser todo! Felicidades :)

results matching ""

    No results matching ""