jueves, 22 de diciembre de 2022

5.- Django - Plantillas Incrustadas y Herencia de Plantillas.

Cuando se construye un sitio web, normalmente se suele hacer una plantilla base, que tendrá la estructura básica de la plantilla, es decir, recogerá todo lo que quieres que tengan en común todas tus plantillas. A la plantilla base se le suele llamar base.html. Esto es muy útil, ya que nos va a ahorrar mucho tiempo y escritura de código. 


Por ejemplo, en nuestro sitio web todas las páginas tendrán la misma cabecera, barra de navegación y pie de página que será común a todas, solo variará el contenido de la parte principal. Si no existiera la herencia tendríamos que escribir el código html de los elementos comunes en cada una de las páginas, pero con esto, lo que haremos será escribir el código de ellos en una plantilla base o padre y usaremos ese código en todas las páginas hijas que hagamos.


Plantillas Incrustadas.


Antes de afrontar el tema de la herencia de plantillas vamos a parar un momento para ver un tema relacionado, como es el de incrustar código html que este en otro archivo:

{% include "nombre_archivo_html" %}

donde "nombre_archivo_html" contendrá el código a insertar.

¿Por qué hacer esto? 

La respuesta es que si tenemos elementos individuales, como puede ser la barra de navegación, cuyo código está en un archivo a parte del principal, si algún día tenemos que modificarlos (por ejemplo añadir más páginas a la barra de navegación) es más fácil de hacer en un código breve que tenemos controlado, que no buscar dentro de un archivo, que en nuestra caso no, porque nuestra plantilla base es muuuuy sencilla, pero que en una página profesional podría tener miles de líneas de código.

Sigamos trabajando y de paso repasamos algunos conceptos ya vistos.

Empezamos creando la plantila "base.html" dentro del directorio de "plantillas" que ya tenemos creado. Su contenido podría ser algo similar a esto:


plantillas/base.html: Plantilla base desde que se generarán las demás.

<!DOCTYPE html>
<html lang="es">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Barra de Navegación</title>
    
</head>
<body>
    <header>
        <h1>Tutorial de Django</h1>
        <!-- Aqui ira la barra de navegación cargada desde una plantilla
        externa, es decir desde otro documento html-->
        {% include "barra_navegacion.html" %}
        <hr/>
    </header>
    <section>
        Aquí ira la parte cambiante de las páginas.
    </section>
    <footer>
        <hr/>
        <small>Aquí ira el pie de página.</small>
    </footer>
    
</body>
</html>

Como ves en el ejemplo superior se ha insertado una barra de navegación, que estará en el archivo "barra_navegación.html" dentro de una plantilla genérica.


Una vez creada la plantilla base, crearemos la barra de navegación dentro del mismo directorio "barra_navegacion.html".

    En un proyecto más extenso, para que este todo ordenado, seguramente querrás tener subcarpetas con los diferentes elementos a insertar en la plantilla base. Por ejemplo esta barra de navegación que vamos a crear podría estar en una subcarpeta llamada "superior" donde estarían todos los elementos que ocupan la parte superior de la página web. Solo tenemos que tener en cuenta que al cargarla tendremos que especificar la ruta:

{% include "superior/barra_navegacion.html" %}

Pero bueno, en nuestro ejemplo, la crearé en la misma carpeta principal llamada "plantillas".

Su estructura será:

plantillas/barra_navegacion.html: barra de navegación que se insertará en las plantillas.

<!DOCTYPE html>
<html lang="es">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
    <style>
        #barra {
            margin: 0;
            padding: 0;
            list-style: none;
            text-transform: uppercase;
        }
        #barra li{
            display: inline;
            margin: 0 23px;
        }
    </style>
</head>
<body>
    <nav>
        <ul id="barra">
            <li><a href="#">Inicio</a></li>
            <li><a href="#">Página Uno</a></li>
            <li><a href="#">Sobre Nosotros</a></li>
        </ul>
    </nav>
</body>
</html>

El siguiente paso es crear la vista principal usando la plantilla "base.html", dentro del archivo views.py. Es muy sencillo utilizando el método que vimos en el último punto del capitulo anterior:


views.py.

...
from django.shortcuts import render

# Vistas

# Vista principal a la que se accederá si no ponemos nada.
def principal(request):
    return render(request, "base.html")


def saludo(request):
....
y ya solo nos queda registrar la vista en el archivo urls.py. Esta vista no tendrá un nombre dentro de la misma para que sea lo primero que nos salga al cargar la aplicación. Como siempre que registremos una vista, importamos la misma desde el archivo views.py y luego la registramos:

views.py.

...
from Proyecto1.views import saludo, fecha_actual, suma_numeros, principal

urlpatterns = [
    path('admin/', admin.site.urls),
    path('saludo/', saludo),
    path("time_server/", fecha_actual),
    path("suma_numeros/<int:numero_1>/<int:numero_2>/", suma_numeros),
    path("", principal),
]
...

Si ejecutamos el servidor de Django (python manage.py runserver) este debería ser el resultado:


plantilla insertada en base.html

Herencia de Plantillas.

La plantilla base.html va a ser nuestra plantilla padre. La vamos a utilizar para construir tres plantillas hijas, que heredarán los elementos comunes de su padre (header, barra de navegación y footer), y cuya estructura se utilizará, será la base, de cada una de las páginas de nuestro sitio web. (Inicio, Pagina Uno, Sobre Nosotros). Sin embargo, ten en cuenta que si una plantilla hija tiene algún elemento que también este definido en la plantilla padre, los elementos de esta última prevalecerán sobre las de la plantilla padre. 

Por ejemplo, en la plantilla padre has definido un determinado footer y en la hija otro distinto, el footer de la plantilla hija prevalece sobre la de la padre. No habrá herencia.

Empecemos modificando la plantilla base.html que ya tenemos para indicar las partes o bloques que las otras plantillas pueden escribir. Si tuviéramos hoja de estilos tendríamos que definirlas aquí. En este fichero incluiremos los bloques:

{% block title %} {% endblock %}
{% block content %} {% endblock %}

para definir el titulo y el contenido de las páginas hijas.

Lo que hacemos es crear una plantilla cuyo código html haremos que se repita en las demás, pero dejaremos los bloques que hemos comentado anteriormente para que cada una de las páginas hijas escriba ahí su propio código. Puedes usar tantos bloques como necesites, no tienes que limitarte a estos dos. 

base.html (Será la plantilla base utilizada para crear las demás)

<!DOCTYPE html>
<html lang="es">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>{% block title %}{% endblock %}</title>
    
</head>
<body>
    <header>
        <h1 style="background-color: darkolivegreen; color: white; text-align:center">Tutorial de Django</h1>
        <!-- Aqui ira la barra de navegación cargada desde una plantilla
        externa, es decir desde otro documento html-->
        {% include "barra_navegacion.html" %}
        <hr/>
    </header>
    <section>
        <!--Aqui ira la parte cambiante de las páginas.-->
        {% block content %}{% endblock %}
    </section>
    <footer style="background-color: darkolivegreen; color: white; text-align:center">
        <hr/>
        <small>Aqui ira el pie de página.</small>
    </footer>
    
</body>
</html>

¿Cómo conseguimos que otra plantilla html herede de esta base que hemos creado? Pues con la etiqueta:

{% extends "[nombre_plantilla_padre]" %}

Cada plantilla que lleve esta etiqueta como primera etiqueta de código va a heredar de la plantilla padre.
Es importante que para que esto funcione esta etiqueta sea la primera que aparezca en las plantillas hijas.  La ventaja no es solo el ahorro de tiempo para crear la plantilla hija, ya que casi no hay que utilizar código, el ahorro viene si queremos modificar la estructura general, por ejemplo la cabecera, que modificando la estructura de la padre automáticamente estará modificado en todas.

Vamos a crear la plantilla "inicio.htm" que se usará cuando entremos directamente en nuestro proyecto.

Lo primero importamos la plantilla base con la etiqueta {% extends "nombre_plantilla" %} y definimos su contenido para que se modifiquen los bloques que ya hemos definido.

inicio.html

{% extends "base.html" %}

{% block title %}Pagina de Inicio{% endblock %}

{% block content %}
    <p>¡Bienvenido! </p>
    <p>Esta es la página principla del proyecto.</p>
{% endblock %}
Esta primera plantilla hija la comenzamos diciendo que use la plantilla padre con la instrucción:

{% extends "base.html" %}

Para continuar reescribiendo los dos bloques, con lo que estamos diciendo al programa que busque estos bloques en la plantilla padre y que los sustituya por los que hemos puesto en esta plantilla.

El primero {% block title %} {% endblock %} modifica el título de la vista de inicio.

El segundo {% block content %} {% endblock %} mostrará el contenido propio de la página de inicio.

Y como ahora esta plantilla va a ser la que se llame cuando entremos en el proyecto tenemos que modificar la vista que teníamos asociada:

views.py

...
# Vistas

# Vista principal a la que se accederá si no ponemos nada.
def principal(request):
    return render(request, "inicio.html")


def saludo(request):
...
Salida:

plantilla incrustada



Vamos a crear otras dos vistas y paginas para mostrar en el menú creado.

pagina1.html

{% extends "base.html" %}
{% block title %}Pagina Uno{% endblock %}
{% block content %}
    <h2>Esta es la primera página.</h2>
    <p>Sigue siendo muy sencilla.</p>
{% endblock %}
Exactamente como antes. Heredamos el código de la plantilla "base.html" 

{% extends "base.html" %}

y definimos los bloques se se insertarán y que se mostrarán en el diseño de la Página Uno (título y contenido)

También tenemos que importar y crear la nueva vista:

views.py

...
# Vistas

# Vista principal a la que se accederá si no ponemos nada.
def principal(request):
    return render(request, "inicio.html")

def pagina_uno(request):
    return render(request, "pagina1.html")
...
y definir la ruta:

urls.py

rom django.contrib import admin
from django.urls import path
from Proyecto1.views import saludo, fecha_actual, suma_numeros, principal,\
    pagina_uno

urlpatterns = [
    path('admin/', admin.site.urls),
    path('saludo/', saludo),
    path("time_server/", fecha_actual),
    path("suma_numeros/<int:numero_1>/<int:numero_2>/", suma_numeros),
    path("", principal),
    path("pagina_uno/", pagina_uno),
]
Salida:

pagina uno - inserción de plantillas

y la última plantilla:

sobre_nosotros.html

{% extends "base.html" %}
{% block title %}Sobre Nosotros{% endblock %}
{% block content %}
    <h2>Estamos empezando con DJANGO.</h2>
    <p>¡Luego juntaremos todo en una aplicación!.</p>
{% endblock %}

views.py

...
# Vista principal a la que se accederá si no ponemos nada.
def principal(request):
    return render(request, "inicio.html")

def pagina_uno(request):
    return render(request, "pagina1.html")

def sobre_nosotros(request):
    return render(request, "sobre_nosotros.html")
...

urls.py

from django.contrib import admin
from django.urls import path
from Proyecto1.views import saludo, fecha_actual, suma_numeros, principal,\
    pagina_uno, sobre_nosotros

urlpatterns = [
    path('admin/', admin.site.urls),
    path('saludo/', saludo),
    path("time_server/", fecha_actual),
    path("suma_numeros/<int:numero_1>/<int:numero_2>/", suma_numeros),
    path("", principal),
    path("pagina_uno", pagina_uno),
    path("sobre_nosotros/", sobre_nosotros)
]

Salida:

sobre nosotros -  inserción de plantillas

Para terminar de definir el menú solo nos queda hacer que la barra de navegación funcione. Aunque ya la tenemos definida vamos a construir los enlaces.

La forma menos eficaz es codificar a lo bruto el link de enlace. Por ejemplo el enlace sobre nosotros podemos construirlo como:

<li><a href="/sobre_nosotros/">Sobre Nosotros</a></li>

Pero entonces, si más adelante, cambiamos el patrón para la vista "sobre nosotros" (e.j. a /acerca_de/) la plantilla no podrá seguir enlazando correctamente. No obstante lo siguiente funcionaria para que al pulsar en el enlace nos lleve a la vista "sobre_nosotros".


barra_navegacion.html

...
<body>
    <nav>
        <ul id="barra">
            <li><a href="/">Inicio</a></li>
            <li><a href="#">Página Uno</a></li>
            <li><a href="/sobre_nosotros/">Sobre Nosotros</a></li>
        </ul>
    </nav>
</body>
...
Usar un mapeador de URL es más flexible y robusto. A pesar del nombre es muy fácil crearlo. Vamos a verlo con la vista de la pagina_uno. En el archivo "urls.py" vamos al path correspondiente en la ruta y utilizamos el parámetro "name=" con el nombre que queramos. Como hace referencia a la ruta pagina_uno utilizaré el mismo nombre. Esta parte del código quedaría de esta forma:

urls.py

from django.contrib import admin
from django.urls import path
from Proyecto1.views import saludo, fecha_actual, suma_numeros, principal,\
    pagina_uno, sobre_nosotros

urlpatterns = [
    path('admin/', admin.site.urls),
    path('saludo/', saludo),
    path("time_server/", fecha_actual),
    path("suma_numeros/<int:numero_1>/<int:numero_2>/", suma_numeros),
    path("", principal),
    path("pagina_uno/", pagina_uno, name="pagina_uno"),
    path("sobre_nosotros/", sobre_nosotros)
]
y ahora en el código de la barra de navegación utilizamos la siguiente etiqueta para crear el enlace. 

{% url 'nombre_usado_en_name' %}

Esto hace que Django cree automáticamente, mediante el mapeador de urls, la ruta para la vista que hemos definido. De forma que si mañana cambiamos el primer argumento, el nombre de la ruta en el archivo urls.py, la vista seguirá funcionado.

barra_navegacion.html

...
body>
    <nav>
        <ul id="barra">
            <li><a href="/">Inicio</a></li>
            <li><a href="{% url 'pagina_uno' %}">Página Uno</a></li>
            <li><a href="/sobre_nosotros/">Sobre Nosotros</a></li>
        </ul>
    </nav>
</body>
...
Con esto ya tenemos un proyecto plenamente funcional.


No hay comentarios:

Publicar un comentario