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.


miércoles, 21 de diciembre de 2022

4.- Django - Cargadores de Plantillas.

Hasta ahora para cargar una plantilla creábamos una variable y usábamos el método "open", le especificamos la ruta de la plantilla. Después utilizábamos el objeto "Template" para pasarle por parámetro el objeto que acabamos de cargar y lo leíamos con el metodo "read".  Y más tarde le decíamos que renderizará ese objeto Template utilizando un contexto


views.py

...
# Vistas

def saludo(request):
    doc_externo = open(
        "/home/chema/Cursos/DJANGO/Proyecto1/plantillas/plantilla.html")
    plantilla = Template(doc_externo.read())
    doc_externo.close()

    nombre = "Antonio"
    fecha_actual = datetime.datetime.now()

    ciudades = ["Barcelona", "Madrid", "León", "Caceres"]

    # Al contexto hay que pasarle un diccionario que se usara para transferir a el valor
    # a la plantilla
    contexto = Context(
        {"nombre_usuario": nombre, "date": fecha_actual, "lugares": ciudades})
    documento = plantilla.render(contexto)
    return HttpResponse(documento)
...


Hay una forma más eficiente de hacer todo esto que es utilizando un cargador o "loader". Consiste en decirle al proyecto de Django que las plantillas se encuentran en un directorio en concreto, al que normalmente se denomina templates (aunque puedes llamarlo como quieras, aquí seguiremos llamándole plantillas). Después cuando vayamos a utilizar una plantilla, todo lo anterior se va a hacer automáticamente, ahorrando recursos y varias líneas de código.

Primeramente debemos decirle a Django donde está la carpeta que contiene nuestras plantillas. Esto se hace en el archivo settings.py y buscamos una variable llamada TEMPLATES. Es una lista que contiene un diccionario una de cuyas claves es DIRS que por defecto aparece vacío.

Nota: Si utilizas como nombre del directorio "templates", que es el que se utiliza por defecto, y no un nombre personalizado como "plantillas" que he usado yo, no tendrás que registrar el directorio de las plantillas en settings.py


TEMPLATES dentro de settings.py


Es aquí donde pondremos la ruta del directorio que contiene nuestras plantillas. En mi caso se llama "plantillas" pero puedes usar el nombre que quieras. Lo modificamos para que incluya la ruta de las plantillas, en mi caso:


settings.py

...
TEMPLATES = [
    {
        'BACKEND': 'django.template.backends.django.DjangoTemplates',
        'DIRS': ["/home/chema/Cursos/DJANGO/Proyecto1/plantillas"],
        'APP_DIRS': True,
        'OPTIONS': {
            'context_processors': [
                'django.template.context_processors.debug',
                'django.template.context_processors.request',
                'django.contrib.auth.context_processors.auth',
                'django.contrib.messages.context_processors.messages',
            ],
        },
    },
]


Una vez hecho esto, importamos el cargador o "loader" en views.py:

from django.template.loader import get_template

views.py

from django.http import HttpResponse
import datetime
# Al utilizar un cargador la siguiente línea no hace falta.
# from django.template import Template, Context
from django.template.loader import get_template

# Vistas

def saludo(request):
    doc_externo = open(
        "/home/chema/Cursos/DJANGO/Proyecto1/plantillas/plantilla.html")
    plantilla = Template(doc_externo.read())
...

Después de importar el loader borramos (yo lo voy a comentar para ver la diferencia) la línea que cargaba el programa, creaba el objeto "Template" y cerraba el documento.

views.py

def saludo(request):
    # Al utilizar un cargador esto no hace falta ya.
    # doc_externo = open("/home/chema/Cursos/DJANGO/Proyecto1/plantillas/plantilla.html")
    # plantilla = Template(doc_externo.read())
    # doc_externo.close()

    nombre = "Antonio"
    fecha_actual = datetime.datetime.now()

    ciudades = ["Barcelona", "Madrid", "León", "Caceres"]

    doc_externo = get_template("plantilla.html")

    contexto = {"nombre_usuario": nombre, "date": fecha_actual, "lugares": ciudades}
    documento = doc_externo.render(contexto)
    return HttpResponse(documento)

Y todo va a quedar más sencillo. 

Creamos una variable (doc_externo) que cargará la plantilla o plantilla que queramos usando "get_template".

 Date cuenta que ya no hay que especificar la ruta de la plantilla. Ya no necesitamos tampoco ni el objeto Template ni el objeto context. Ahora sencillamente al doc_externo que ha cargado la plantilla lo renderizamos con el método render al que tenemos que pasar un diccionario y no un objeto "context" como hacíamos antes.


Y LO MAS PRACTICO VIENE AHORA ->

Incluso podemos abreviar más el código si utilizamos atajos o "shortcut functions" que Django nos facilita. Aquí nos viene una función muy útil, llamada "render()" que hará que podamos prescindir de casi todo y nos quedará todo el proceso de carga y renderizado en una única línea. Para ello primero tenemos que importar en views.py lo siguiente:

from django.shorcuts import render 

y veréis en lo que se nos queda el código.

views.py (archivo final para cargar una plantilla externa usando el menor código)

from django.http import HttpResponse
import datetime

from django.shortcuts import render

# Vistas

def saludo(request):
    nombre = "Antonio"
    fecha_actual = datetime.datetime.now()

    ciudades = ["Barcelona", "Madrid", "León", "Caceres"]
    
    contexto = {"nombre_usuario": nombre, "date": fecha_actual, "lugares": ciudades}
    
    return render(request, "plantilla.html", contexto)


En vez de retornar un objeto HttpResponse como hicimos hasta ahora, retornamos un objeto render en el que ya especificamos como primer argumento el "request", como segundo el nombre de la plantilla entre comillas y como tercero podemos pasarle el contexto como una variable o pasarle directamente el diccionario. Así de fácil y con una sola línea de código podemos renderizar una plantilla con un determinado contexto.

lunes, 12 de diciembre de 2022

3.- Django - Plantillas II, variables y propiedades en la plantilla.

En el apartado anterior vimos como crear y cargar plantillas desde las vistas. En este apartado vamos a profundizar más y veremos como pasar información a las plantillas, para lo cual utilizaremos un tipo de dato de Python, como son los diccionarios dentro de los contextos.

Seguimos con el mismo ejemplo usado en el capitulo anterior donde una de la vistas se llamaba "saludo" y cargábamos un documento externo, la plantilla, desde un directorio creado al efecto.

Imaginemos que queremos pasarle un nombre de usuario a la plantilla desde la vista para que la muestre en la página web y le de la Bienvenida.

Por ejemplo que Ponga "Bienvenido Pedro" o lo que tu quieras.

Empezamos creando en las vistas, views.py, una variable "nombre" que recoja el nombre que le queramos pasar. Luego dentro del contexto y siempre a través de un diccionario usamos una clave "nombre_usuario" cuyo valor será renderizado posteriormente al mostrar la página web.

views.py: archivos de vistas.

...
# Vistas
def saludo(request):
    doc_externo = open("/home/chema/Cursos/DJANGO/Proyecto1/plantillas/plantilla.html")
    plantilla = Template(doc_externo.read())
    doc_externo.close()

    nombre = "Antonio"

    # Al contexto hay que pasarle un diccionario que se usara para transferir a el valor
    # a la plantilla
    contexto = Context({"nombre_usuario": nombre})
    documento = plantilla.render(contexto)
    return HttpResponse(documento)

...

Para finalizar en la plantilla, ponemos donde queramos que se renderice posteriormente esta información usando flechas dobles {{ }}. Tiene su lógica puesto que recuerda que en Python se usan las flechas sencillas para crear los diccionarios {}.

plantilla.html

<!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>Fecha Servidor</title>
</head>
<body>
      <h3>
         ¡Hola Mundo! He sido cargado desde una plantilla.         
      </h3> 
      <p>
            Bienvenido {{nombre_usuario}}
      </p>  
</body>
</html>


Salida:


variable renderizada en la plantilla

Podemos pasar tantas claves como queramos e incluso usar la metodología del punto de las clases para pasar valores. Por ejemplo aprovechando que habíamos importado la función "datetime" vamos a mostrar la fecha actual del siguiente modo:

views.py: archivos de vistas.

...
# Vistas
def saludo(request):
    doc_externo = open("/home/chema/Cursos/DJANGO/Proyecto1/plantillas/plantilla.html")
    plantilla = Template(doc_externo.read())
    doc_externo.close()

    nombre = "Antonio"
    fecha_actual = datetime.datetime.now()

    # Al contexto hay que pasarle un diccionario que se usara para transferir a el valor
    # a la plantilla
    contexto = Context({"nombre_usuario": nombre, "date": fecha_actual})
    documento = plantilla.render(contexto)
    return HttpResponse(documento)

...
y modificamos la plantilla:

plantilla.html

<!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>Fecha Servidor</title>
</head>
<body>
      <h3>
         ¡Hola Mundo! He sido cargado desde una plantilla.         
      </h3> 
      <p>
            Bienvenido {{nombre_usuario}}
      </p>  
      <p>
            Hoy es {{date.day}} - {{date.month}} - {{date.year}}
      </p>
</body>
</html>

Salida:


inclusión de variable en plantillas


A continuación vamos a ver como pasar una lista a una plantilla. Seguimos con el ejemplo y vamos a pasarle una lista con ciudades.

El proceso es igual que en los casos anteriores. Creamos la lista y se la pasamos a través del contexto mediante un diccionario que contendrá la clave "lugares" y como valor la lista a pasar.

views.py: archivos de vistas.

...
# Vistas
def saludo(request):
    doc_externo = open("/home/chema/Cursos/DJANGO/Proyecto1/plantillas/plantilla.html")
    plantilla = Template(doc_externo.read())
    doc_externo.close()

    nombre = "Antonio"
    fecha_actual = datetime.datetime.now()

    ciudades = ["Barcelona", "Madrid", "León", "Caceres"]

    # Al contexto hay que pasarle un diccionario que se usara para transferir a el valor
    # a la plantilla
    contexto = Context({"nombre_usuario": nombre, "date": fecha_actual, "lugares":ciudades})
    documento = plantilla.render(contexto)
    return HttpResponse(documento)

...

y modificamos la plantilla para que sean mostrados. La lista se muestra tal cual.

plantilla.html

...
<body>
      <h3>
         ¡Hola Mundo! He sido cargado desde una plantilla.         
      </h3> 
      <p>
            Bienvenido {{nombre_usuario}}
      </p>  
      <p>
            Hoy es {{date.day}} - {{date.month}} - {{date.year}}
      </p>
      <p> En todas estas ciudades:
            <br/><br/>
            {{lugares}}
      </p>
</body>
...

Salida:

Insertar una lista en una plantilla

¿Y si quisieramos acceder a un solo elementos de la lista? Pues lo hariamos directamente en la plantilla con la nomenclatura del punto. En el ejemplo si solo quisiéramos mostrar "Caceres" (las listas en python comienzan en el elemento 0) el código sería:

plantilla.html

...
<body>
      <h3>
         ¡Hola Mundo! He sido cargado desde una plantilla.         
      </h3> 
      <p>
            Bienvenido {{nombre_usuario}}
      </p>  
      <p>
            Hoy es {{date.day}} - {{date.month}} - {{date.year}}
      </p>
      <p> En todas estas ciudades:
            <br/><br/>
            {{lugares.3}}
      </p>
</body>
...

También podríamos haberlo hecho mediante Python en el archivo de vistas y pasarle solo el elemento que nos interese.


Salida:

Seleccionando un elemento de una lista

En los ejemplos anteriores hemos utilizado varias veces la nomenclatura del punto. Vamos a hacer un inciso para ver el orden jerárquico que usa Django cuando se encuentra con un punto. Es decir que hace Django cuando encuentra un punto en un plantilla. En este orden:

- Lo primero que hace es buscar un Diccionario. Es decir a ver si ese punto corresponde con un elemento de un diccionario.

- Si no lo encuentra mira a ver si ese mismo punto corresponde a un Atributo o Propiedad correspondiente a una clase.

- En tercer lugar pasa a mirar si se trata de un Método perteneciente a un objeto o una clase.

- Y por último mira a ver si es un elemento de una Lista (como en el ejemplo previo)


Bucles en plantillas de Django.

¿y si quisiéramos que las ciudades aparezcan una debajo de otra? Pues se puede hacer de varias formas pero para avanzar un poco vamos a usar los bucles en plantillas con Django.

Los bucles en plantillas van, NO entre dos llaves, si no entre una llave simple y utilizando el signo de porcentaje. Hay que poner una etiqueta de apertura del bucle y otra de cierre. Al igual que en Python se usa la palabra reservada "for" de la siguiente forma:

plantilla.html

...
<body>
      <h3>
         ¡Hola Mundo! He sido cargado desde una plantilla.         
      </h3> 
      <p>
            Bienvenido {{nombre_usuario}}
      </p>  
      <p>
            Hoy es {{date.day}} - {{date.month}} - {{date.year}}
      </p>
      <p> En todas estas ciudades:</p>
      <p>
            <ul>
            {% for ciudad in lugares %} <!-- Llave de apertura -->
                  <li>{{ciudad}}</li>
            {% endfor %} <!-- LLave de cierre -->
            </ul>
      </p>
</body>
...

Salida:

bucle for en Django


Condicionales.

Al igual que cualquier lenguaje de programación también podemos usar condicionales. Se construye de la misma forma que hemos visto antes para los bucles, con una etiqueta de apertura y otra de cierre y usando una llave y la palabra reservada "if". Por ejemplo vamos a modificar la plantilla para que nos indique si estamos en la primera quincena del mes o en la segunda.

La sintaxis sería la siguiente:

plantilla.html

...
      <p>
            Bienvenido {{nombre_usuario}}
      </p>  
      <p>
            Hoy es {{date.day}} - {{date.month}} - {{date.year}}.
            {% if date.day < 15 %}
                  Estamos en la primera quincena del mes.
            {% else %}
                  Estamos en la segunda quincena del mes.
            {% endif %}
      </p>
      <p> En todas estas ciudades:</p>
      <p>
...

Es casi igual que en Python salvo porque no se ponen los dos puntos al final de la sentencia a evaluar.

Salida:

condicional en Django



Puedes encontrar más información sobre bucles, condicionales y otra multitud de etiquetas que puedes usar en Django ya construidos en la documentación en la sección "Built-in template tags and filters"

Otra diferencia con Python es a la hora de utilizar los métodos. Me explico. Imagina que en Python tenemos un string y queremos ponerlo en mayúsculas. Por ejemplo tengo la palabra "gacela" y quiero ponerla en mayúsculas:

"gacela".upper()

Sin embargo en Django para aplicar en la plantilla un método a este mismo string usaríamos el mismo método pero con una diferencia, prescindiríamos de los paréntesis finales ()

"gacela".upper

En el ejemplo en el que estamos trabajando si queremos poner el nombre del usuario en mayúsculas lo haríamos de la siguiente forma:

...
      <p>
            Bienvenido {{nombre_usuario.upper}}
      </p>  
      <p>
            Hoy es {{date.day}} - {{date.month}} - {{date.year}}.
...

Salída:

Condicionales en Django


Comentarios en Plantillas de Django:

Los comentarios de una sola línea tienen esta estructura:

{# Texto del comentario #}

Los de varias líneas esta otra:

{% comment %}
    texto del comentario
    texto del comentario
{% endcommnent %}


Filtros en Plantillas. (built-in filters reference)

Son elementos que se usan para filtrar la información o transformarla de alguna manera. Con los filtros puedes hacer muchas cosas en las plantillas y no solo modificar strings o texto, sino también sumar dos números, ordenar listas y muchas más. Lo mejor es ver la documentación pero vamos a ver un ejemplo.

El filtro se usa poniendo después de la variables que se quiere filtrar un "pipeline" que es el símbolo "|" y el filtro a utilizar.

Usaremos filtros para que todos los nombres de las ciudades se pongan en Mayúsculas y también para que nos diga si el día es un numero par o impar. 

plantilla.html

<!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>Fecha Servidor</title>
</head>
<body>
      {# COMENTARIO EN UNA PLANTILLA DE DJANGO de una sola línea #}
      {% comment %}
            COMENTARIO
            de varias lineas
      {% endcomment %}
      <h3>
         ¡Hola Mundo! He sido cargado desde una plantilla.         
      </h3> 
      <p>
            Bienvenido {{nombre_usuario}}
      </p>  
      <p>
            Hoy es {{date.day}} - {{date.month}} - {{date.year}}.
            {% if date.day < 15 %}
                  Estamos en la primera quincena del mes.
            {% else %}
                  Estamos en la segunda quincena del mes.
            {% endif %}

            {# Filtro para mostrar si el dia es un número par o impar. #}
            {% if date.day|divisibleby:"2" %}
                  <p>El día de hoy es un número par.</p>
            {% else %}
                  <p>El día de hoy es un número impar.</p>

            {% endif %}
      </p>
      <p> En todas estas ciudades:</p>
      <p>
            <ul>
            {% for ciudad in lugares %} <!-- Llave de apertura -->
                  <li>{{ciudad|upper}}</li>
            {% endfor %} <!-- LLave de cierre -->
            </ul>
      </p>
</body>
</html>

Salida:

uso de filtros en plantillas de Django


No obstante conviene no abusar del uso tanto de las estructuras de control, como de los filtros ya que la filosofía de Django es que la parte lógica esté en una parte "views.py" y el diseño en otro (en las plantillas).

miércoles, 7 de diciembre de 2022

2.- Django - Contenido dinámico e Introducción de Parámetros en URL.

En la primera página que mostramos en el post anterior, correspondiente al "Hola Mundo", se mostraba un contenido estático. Aunque cargaras la página web varias veces siempre se mostraba lo mismo. 

Vamos a diseñar ahora una página que nos muestre la hora y fecha del servidor, con lo que cada vez que actualices la página se mostrará una información diferente - Dinámico -. Además, podemos pasar esta información formateada, usando como parámetro del método HttpResponse con código HTML, en vez de un string.

Y ¿Cómo hacemos esto?

Pues partimos del ejemplo del capitulo anterior. En el archivo views.py,  creamos una vista más de la siguiente forma:

views.py

from django.http import HttpResponse
import datetime

# Vistas
...
# Nueva vista que mostrará la hora actual.
def fecha_actual(request):

    date = datetime.datetime.now()

    # Estructura de un documento tipo html5
    documento = f"""
    <!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>Fecha Servidor</title>
    </head>
    <body>
        <h2>
            Fecha y hora Actuales: {date}
        </h2>    
    </body>
    </html>
    """
    return HttpResponse(documento)
Para empezar importamos la biblioteca datatime ya que es la que utilizaremos para mostrar la fecha y hora actuales. 

Creamos la variable date que almacenará esa fecha y hora.

Creamos la nueva vista. La Función de esta vista la he llamado "fecha_actual". Para darle formato a la salida utilizamos la variable documento que recoge la estructura básica de un documento html5. Al final no es más que un string con lo que podemos introducir información, como lo haríamos normalmente en Python - f"texto {variable}" -

El siguiente paso es registrar esa vista en el archivo que guarda las urls, urls.py.

urls.py

from django.contrib import admin
from django.urls import path
from Proyecto1.views import saludo, fecha_actual

urlpatterns = [
    path('admin/', admin.site.urls),
    path('saludo/', saludo),
    path("time_server/", fecha_actual),
]
Importamos la función fecha_actual al principio. Luego para acceder a la vista hay que usar el nombre "time_server" que hace referencia a la vista o función "fecha_Actual". Como comentamos en el post anterior el nombre para acceder a la vista "time_server" no tiene que ser necesariamente el nombre de la función "fecha_actual" aunque es lo más recomendable.

Vamos a ver si funciona. Ejecutamos el servidor de pruebas con:

$ python manage.py runserver

Salida:

muestra fecha servidor en pantalla


Puedes ver como va cambiando refrescando el navegador con F5.


Paso de Parámetros a través de la URL.


Para ver como se hace, vamos a ver un ejemplo, en el que pasando dos números a través de una vista nos muestre su suma.

Como en los casos anteriores lo primero es crear la función de la vista.

IMPORTANTE: Por defecto los parámetros que se pasan a través de una URL son de tipo string. Por eso si necesitamos, como en este caso, que sean numéricos hay que ponerlos con este formato:

/<int: parámetro>

Comentar que aparte de <int:loquesea> también se pueden utilizar los siguientes comandos para modificar el tipo de parámetros que introducimos en la Url:

  • string: Acepta cualquier texto sin barras (por defecto). Si no ponemos nada en el parámetro como ya dijimos lo considera como un string, una cadena de texto.
  • int: para convertirlo en enteros
  • float: para  valores reales, con decimales.
  • path: Acepta cadena de caracteres con barras


Otra opción es sabiendo que al pasárselos a la función, estos datos que son de tipo string, los convirtamos luego dentro de la función al tipo de datos que necesitemos usando Python. (por ejemplo int(parametro) )

Dicho lo cual la forma general de pasar los parámetros es:

def nombre_vista(request, parametro1, parametro2,...,parametro_n):
        -----
        -----
        -----
        return HttpResponse(documento)


Creamos la función de la nueva vista.

views.py

# Suma dos números pasados como parámetros en la Url
def suma_numeros(request, numero_1, numero_2):
    documento = documento = f"""
    <!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>Fecha Servidor</title>
    </head>
    <body>
        <h3>
            La suma de {numero_1} más {numero_2} = {numero_1 + numero_2}
            <hr/>
        </h3>    
    </body>
    </html>
    """
    return HttpResponse(documento)

Para registrar la URL la forma general es:

path("Nombre_Url/<parametro_1>/<parametro_2>/..../<parametro_n>/", nombre_vista)

en nuestro ejemplo, como necesitamos que los parámetros se pasen como enteros para sumarlos:

urls.py

from django.contrib import admin
from django.urls import path
from Proyecto1.views import saludo, fecha_actual, suma_numeros

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

Como siempre, importamos la función y luego registramos la URL.

Salida:

paso de parametros a través de url
Plantillas - Son cadenas de texto que pueden tener código Html o ser texto plano simplemente. Sirven para separar la parte lógica de la parte visual del proyecto. Aunque hay muchas formas de utilizar las plantillas la más habitual es guardar todo el código HTML en un documento aparte y en otra carpeta distinta y luego la cargarla en nuestra vista.

Para crear una plantilla, básicamente seguimos tres pasos:

1.) Creación de un objeto tipo Template.
2.) Creación de un contexto (Contenido dinámico, variables, funciones etc)
3.) Renderizado del template.

Vamos a pasar todo esto a código con el proyecto que estamos usando desde el post inicial. En el proyecto que estamos usando ya teníamos una función en views.py llamada "saludo" que nos devolvía un texto plano. Vamos a hacer que nos devuelva el mismo texto pero usando una plantilla para que nos sirva de ejemplo.

Vamos a crear un nuevo archivo que será la plantilla que vamos a crear y la guardaremos también en una carpeta nueva, separada que por ejemplo podemos llamar "plantillas", aunque normalmente se suele llamar "templates".

directorio para plantillas


plantilla.html (guardada en directorio "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>Fecha Servidor</title>
</head>
<body>
      <h3>
         ¡Hola Mundo! He sido cargada desde una plantilla.
      </h3>    
</body>
</html>

Ahora volvemos a el archivo views.py y modificamos la función saludo para que cargue la plantilla. Aunque posteriormente lo haremos con cargadores vamos a empezar poco a poco y en este ejemplo lo haremos manualmente.

Lo primero que haremos es importar al comienzo del programa la clase Template y Context

views.py

from django.http import HttpResponse
import datetime
from django.template import Template, Context

# Vistas
def saludo(request):
    doc_externo = open("/home/chema/Cursos/DJANGO/Proyecto1/plantillas/plantilla.html")
    plantilla = Template(doc_externo.read())
    doc_externo.close()
    # Aunque no hay contenido dinámico, hay que crear el contexto.
    contexto = Context()
    # Renderizamos el contenido
    documento = plantilla.render(contexto)
    return HttpResponse(documento)

...
Si vamos al archivo de vistas, views.py, en vez de trabajar con texto incrustado lo vamos a hacer con una plantilla ya creada, para cargarla y renderizarla. 

Empezamos creando una variable, doc_externo, que utiliza el método open para cargar la plantilla. Tenemos que especificar la ruta donde encontrar el documento. (Ojo que para especificar la ruta hay que usar esta barra "/" sobretodo si estás en windows)

Una vez hecho esto creamos el objeto de tipo Template que asignare a una variable que puedes llamar como prefieras, plantilla en mi caso. Utilizamos .read() para leerla. Ya tenemos cargado el documento.

Y como lo tenemos cargado voy a cerrar el fichero para que no ocupe memoria con doc_externo.close().

Como el ejemplo es muy sencillo y no lleva contenido dinámico, ni información adicional la variable contexto es igual a la clase Context pero sin contenido. 

Para finalizar renderizamos el contenido.

Si ejecutamos el servidor y lo probamos con localhost/saludo/ nos debería funcionar.

Todo lo anterior es para tener la idea global de como funciona la carga de plantillas, ya que como veremos luego existen métodos más eficientes y sencillos de hacerlo, como veremos en el próximo post.