Enrutamiento de URL y página de detalle del artículo

En este capítulo aprenderás a configurar el enrutamiento dinámico de Django, para implementar la página de detalle del artículo y los enlaces de navegación entre páginas.


¿Qué es el enrutamiento dinámico?

La página de inicio muestra todos los artículos; cada artículo necesita su propia página de detalle independiente.

La URL debería verse así:/post/1/、/post/2/, donde la parte numérica es la clave primaria (pk) del artículo.

Enrutamiento dinámicoPermite que la URL contenga variables; Django las extrae automáticamente y las pasa a la función de vista.


Configurar el enrutamiento dinámico

Ejemplo

# Ruta de archivo: blog/urls.py
from django.urls import path
from . import views

urlpatterns = [
    path('', views.index, name='index'),
    # int:pk — Parámetro entero en la URL, llamado pk (clave primaria)
    # name='post_detail' — Da nombre a la ruta; se usa {% url %} en las plantillas para referenciarla
    path('post/<int:pk>/', views.post_detail, name='post_detail'),
]

Explicación de los convertidores de ruta:

ConvertidorRuta de ejemploRegla de coincidencia
<int:pk>/post/3/Coincide con enteros positivos
<str:slug>/post/django-guide/Coincide con una cadena (sin /)
<slug:slug>/post/django-guide/Coincide con letras, números, - y _
<uuid:id>/post/uuid/Coincide con el formato UUID

Escribir la vista de la página de detalle

La página de detalle debe consultar el artículo correspondiente en la base de datos según el pk de la URL.

Ejemplo

# Ruta de archivo: blog/views.py
from django.shortcuts import render, get_object_or_404
from .models import Post

def post_detail(request, pk):
    """Página de detalle del artículo"""
    # get_object_or_404: consulta un único registro
    # Si se encuentra, devuelve el objeto; si no, devuelve automáticamente la página 404 (sin escribir try/except)
    post = get_object_or_404(Post, pk=pk)

    context = {
        'post': post,
        'title': f'{post.title} - Blog de EJEMPLO'
    }
    return render(request, 'blog/post_detail.html', context)

get_object_or_404Es la mejor práctica de Django. Equivale aPost.objects.get(pk=pk) + try/except Post.DoesNotExist, pero el código es más limpio. Cuando el usuario accede a un ID de artículo que no existe, verá una página 404 amigable en lugar de un error.


Crear la plantilla de la página de detalle

Ejemplo

<!-- Ruta de archivo: blog/templates/blog/post_detail.html -->
{% extends 'blog/base.html' %}

{% block title %}{{ post.title }} - Blog de EJEMPLO{% endblock %}

{% block content %}
<article class="post-view">
    <span class="category-tag">{{ post.category.name }}</span>
    <h1>{{ post.title }}</h1>
    <time>{{ post.created_at|date:"Y-m-d" }}</time>

    <!-- El cuerpo usa el filtro |safe para renderizar HTML (contenido HTML introducido en Admin) -->
    <div class="content">
        {{ post.content|safe }}
    </div>

    <a href="{% url 'index' %}" class="back-link">← Volver al inicio</a>
</article>
{% endblock %}

Estilos de la página de detalle

Ejemplo

/* Añadir al style de base.html */
.post-view {
    max-width: 720px;
    margin: 0 auto;
}

.category-tag {
    display: inline-block;
    padding: 4px 12px;
    background: #E8EEF5;
    color: #3D71A6;
    border-radius: 12px;
    font-size: 13px;
    margin-bottom: 12px;
}

.post-view h1 {
    font-size: 32px;
    margin-bottom: 12px;
    line-height: 1.4;
}

.post-view time {
    display: block;
    color: #999;
    font-size: 14px;
    margin-bottom: 30px;
}

.content {
    line-height: 1.8;
    font-size: 16px;
    color: #333;
}

.content h2 {
    margin: 24px 0 12px;
    font-size: 22px;
}

.content p {
    margin-bottom: 12px;
}

.back-link {
    display: inline-block;
    margin-top: 40px;
    color: #2c3e50;
    text-decoration: none;
}

|safeEl filtro le dice a Django: este contenido es HTML seguro, se renderiza directamente, no lo escapes. Al usar safe, asegúrate de que el contenido sea confiable (como el que tú mismo editas en Admin). Si es contenido enviado por el usuario, nunca debes añadir safe, de lo contrario hay riesgo de XSS.


Generar enlaces en la plantilla: {% url %}

Añade un enlace a la página de detalle en la tarjeta del artículo de la página de inicio.

Ejemplo

<!-- Modificar la tarjeta del artículo en blog/templates/blog/index.html -->
<div class="article-grid">
    {% for post in posts %}
    <a href="{% url 'post_detail' post.pk %}" class="card-link">
        <div class="article-card">
            <div class="card-content">
                <span class="card-category">{{ post.category.name }}</span>
                <h3>{{ post.title }}</h3>
                <p>{{ post.summary|truncatechars:80 }}</p>
                <span class="card-date">{{ post.created_at|date:"Y-m-d" }}</span>
            </div>
        </div>
    </a>
    {% endfor %}
</div>

{% url 'post_detail' post.pk %}Genera automáticamente la URL correcta según el nombre de la ruta y los parámetros.

Si más adelante la ruta cambia (por ejemplo,/post/se cambia a/article/), el enlace en la plantilla se actualizará automáticamente, sin necesidad de modificarlo manualmente.


Añadir get_absolute_url() al modelo

Definir este método en el modelo Post permite que el backend de Admin, las herramientas integradas de Django, etc., encuentren automáticamente el enlace a la página de detalle.

Ejemplo

# Ruta de archivo: añadir en la clase Post de blog/models.py
from django.urls import reverse

class Post(models.Model):
    # ... definición de campos ...
    def __str__(self):
        return self.title

    def get_absolute_url(self):
        # reverse resuelve la URL según el nombre de la ruta y los parámetros
        return reverse('post_detail', kwargs={'pk': self.pk})

Resumen del capítulo

En este capítulo has dominado el núcleo del enrutamiento de Django: el enrutamiento dinámico<int:pk>Pasar parámetros, consultar de forma segura con get_object_or_404, generar enlaces con la etiqueta de plantilla {% url %} y resolver URLs con reverse.

Ahora el blog tiene dos páginas: la lista de la portada y la página de detalle del artículo. Al hacer clic en la tarjeta, puedes saltar para leer el contenido completo.

Otras extensiones