Patrón MVT: Modelo-Vista-Template¶
Si tenéis experiencia en el mundo de la programación seguro que habéis oído hablar del famoso patrón MVC: Modelo-Vista-Controlador. Django redefine este modelo como MVT: Modelo-Vista-Template.
Hasta ahora lo que hemos hecho no requería de interactuar con la base de datos. Podríamos decir que simplemente se recibe una petición del navegador, se ejecuta la vista correspondiente y se renderiza el Template para que el navegador muestre el HTML resultante:
Sin embargo en el momento en que aparecen las base de datos y los modelos, este proceso se extendiende. Ahora se recibirá la petición, se pasará a la vista, en la vista recuperaremos los datos del modelo correspondiente, y finalmente la renderizaremos el Template pero esta vez integrando los datos dinámicos recuperados del modelo, antes de enviar el HTML final al navegador:
Vamos a hacerlo, ya veréis como en la práctica es bastante fácil.
Como la lista de proyectos la enviaremos al template portofolio.html a través de su view porfolio, vamos a recuperar los datos ahí. Pero en lugar de definirla en la app Core, la vamos a trasladar al views de su propia app portfolio/views.py:
portfolio/views.py
from django.shortcuts import render
def portfolio(request):
return render(request, "core/portfolio.html")
También crearemos una carpeta templates/portfolio y pondremos el template de la página portfolio ahí:
Y actualizamos la ruta al template:
portfolio/views.py
from django.shortcuts import render
def portfolio(request):
return render(request, "portfolio/portfolio.html") # <=====
Ahora tenemos que readaptar las URL, pero como ahora tenemos que hacer referencia a dos apps, necesitamos importar de forma distintas las vistas:
webpersonal/urls.py
from django.contrib import admin
from django.urls import path
from core import views as core_views
from portfolio import views as portfolio_views
from django.conf import settings
urlpatterns = [
path('', core_views.home, name="home"),
path('about-me/', core_views.about, name="about"),
path('portfolio/', portfolio_views.portfolio, name="portfolio"),
path('contact/', core_views.contact, name="contact"),
path('admin/', admin.site.urls),
]
if settings.DEBUG:
from django.conf.urls.static import static
urlpatterns += static(settings.MEDIA_URL,
document_root=settings.MEDIA_ROOT)
Esto que hemos hecho parece una tontería, pero separar las apps siempre viene bien. Nos permite organizar mejor el código y prepararlo para escalarlo en el futuro.
De vuelta a nuestra vista portfolio, necesitamos hacer referencia a nuestro modelo Project para recuperar sus instancias y enviarlas al template, así que lo importamos arriba del todo:
portfolio/views.py
from django.shortcuts import render
from .models import Project # <=====
def portfolio(request):
return render(request, "portfolio/portfolio.html")
Ahora fijaros qué fácil es recuperar los registros de la tabla Projects que maneja nuestro modelo ORM a través de un su lista de objetos interna y un método .all() que hace referencia a todos sus objetos:
portfolio/views.py
from django.shortcuts import render
from .models import Project
def portfolio(request):
projects = Project.objects.all() # <=====
return render(request, "portfolio/portfolio.html")
Finalmente tenemos que inyectar estos proyectos en el template. Para hacerlo simplemente enviamos a la función render un tercer parámetro con un diccionario y los valores que queremos inyectar. De la siguiente forma:
portfolio/views.py
from django.shortcuts import render
from .models import Project
def portfolio(request):
projects = Project.objects.all()
return render(request, "portfolio/portfolio.html",
{'projects':projects}) # <=====
Teóricamente con esto habremos inyectado los proyectos, así que ahora vamos a la plantilla para debugearlos a ver si nos aparece algo:
portfolio/templates/portfolio/porfolio.html
{% extends 'core/base.html' %}
{% load static %}
{% block title %}Portafolio{% endblock %}
{% block background %}{% static 'core/img/portfolio-bg.jpg' %}{% endblock %}
{% block headers %}
<h1>Portafolio</h1>
<span class="subheading">Currículo</span>
{% endblock %}
{% block content %}
{% for project in projects %}
Proyecto: {{project.title}} creado {{project.created}} <br>
{% endfor %}
{% endblock %}
Probamos la página y fijaros que os mostrará algo llamado QuerySet, como una lista y dentro aparece nuestro proyecto.
Un QuerySet es la representación del resultado de una consulta a la base de datos, pero devuelta como una lista de instancias. Al ser una especie de lista, lo bueno que tiene es que podemos iterarla con otro templatetag llamado for, y dentro de cada iteración mostrar para cada Proyecto sus atributos.
Si esto lo adaptamos un poco, podemos transformar cada iteración en el proyecto que tenemos que mostrar:
portfolio/templates/portfolio/porfolio.html
{% extends 'core/base.html' %}
{% block title %}Portafolio{% endblock %}
{% load static %}
{% block background %}{% static 'core/img/portfolio-bg.jpg' %}{% endblock %}
{% block headers %}
<h1>Portafolio</h1>
<span class="subheading">Currículo</span>
{% endblock %}
{% block content %}
{% for project in projects %}
<!-- Proyecto -->
<div class="row project">
<div class="col-lg-3 col-md-4 offset-lg-1">
<img class="img-fluid" src="{{project.image}}" alt="">
</div>
<div class="col-lg-7 col-md-8">
<h2 class="section-heading title">{{project.title}}</h2>
<p>{{project.description}}</p>
{% if project.link %}
<p><a href="{{project.link}}">Más información</a></p>
{% endif %}
</div>
</div>
{% endfor %}
{% endblock %}
¿No es fantástico? Lo malo es que la imagen no se muestra. Si analizamos el código generado veremos la causa:
Como véis no añade la URL de los ficheros que se supone están en /media/, pero no os preocupéis. El campo ImageField tiene un atributo llamado url que nos generará su ruta correcta automáticamente teniendo en cuenta la variable MEDIA_URL que tenemos en settings.py:
portfolio/templates/portfolio/porfolio.html
<img class="img-fluid" src="{{project.image.url}}" alt="">
Con esto debería funcionar la imagen:
En este punto si queréis podemos volver al panel de administrador y añadir otro proyecto de prueba:
Una vez creado el proyecto nos aparecerá en la parte superior, recordad que se nos ordenan de más recientes amás antiguos:
Fijaros en muy poco tiempo hemos transformado un template estático en una web con una sección Portafolio dinámica, donde a través de un panel de administrador nuestros clientes pueden manejar proyectos de forma simple y cómoda, incluso a través de su teléfono móvil. Sólo nos faltaría crear un usuario para nuestro cliente desde el administrador, pero esto lo trabajaremos más a fondo en el siguiente proyecto.
Por ahora damos la web personal acabada, lo que resta de sección lo dedicaremos a algunos ejercicios prácticos.
Última edición: 25 de Octubre de 2018