Este tutorial comienza donde Tutorial 2 dejó. Continuamos con la aplicación de encuesta web y nos enfocaremos en crear la interfaz pública – «vistas.»
¿Dónde obtener ayuda:
Si tienes problemas al seguir este tutorial, por favor dirígete a la sección Ayuda del FAQ.
Una vista es un «tipo» de página web en tu aplicación Django que generalmente sirve una función específica y tiene un template específico. Por ejemplo, en una aplicación de blog, podrías tener las siguientes vistas:
Página de inicio del blog – muestra las últimas pocas entradas.
Página de detalles de entrada – página de enlace permanente para una sola entrada.
Página de archivo por año – muestra todos los meses con entradas en el año dado.
Página de archivo por mes – muestra todos los días con entradas en el mes dado.
Página de archivo por día – muestra todas las entradas en el día dado.
Acción de comentario – maneja la publicación de comentarios a una entrada dada.
En nuestra aplicación de encuesta, tendremos las siguientes cuatro vistas:
Página «índice» de pregunta – muestra las últimas pocas preguntas.
Página «detalles» de pregunta – muestra el texto de la pregunta, sin resultados pero con un formulario para votar.
Página «resultados» de pregunta – muestra los resultados para una pregunta particular.
La acción de votación – maneja el voto para una elección particular en una pregunta particular.
En Django, las páginas web y otros contenidos se entregan mediante vistas. Cada vista está representada por una función de Python (o método, en caso de vistas basadas en clases). Django elegirá una vista examinando la URL solicitada (para ser preciso, la parte de la URL después del nombre de dominio).
Ahora que has estado en línea durante un tiempo, es posible que hayas encontrado cosas como ME2/Sites/dirmod.htm?sid=&type=gen&mod=Core+Pages&gid=A6CD4967199A42D9B65B1B. Te alegrará saber que Django nos permite mucho más elegantes patrones de URL que eso.
Un patrón de URL es la forma general de una URL - por ejemplo: /newsarchive/<year>/<month>/.
Para pasar de una URL a una vista, Django utiliza lo que se conoce como “URLconfs”. Un URLconf mapea los patrones de URL a vistas.
Este tutorial proporciona instrucciones básicas sobre el uso de URLconfs y puedes referirte a Dispatcher de URL para obtener más información.
Ahora vamos a agregar algunas vistas adicionales a polls/views.py. Estas vistas son ligeramente diferentes, porque toman un argumento:
polls/views.py¶def detail(request, question_id):
return HttpResponse("You're looking at question %s." % question_id)
def results(request, question_id):
response = "You're looking at the results of question %s."
return HttpResponse(response % question_id)
def vote(request, question_id):
return HttpResponse("You're voting on question %s." % question_id)
Conecta estas nuevas vistas al módulo polls.urls agregando las siguientes llamadas a path():
polls/urls.py¶from django.urls import path
from . import views
urlpatterns = [
# ex: /polls/
path("", views.index, name="index"),
# ex: /polls/5/
path("<int:question_id>/", views.detail, name="detail"),
# ex: /polls/5/results/
path("<int:question_id>/results/", views.results, name="results"),
# ex: /polls/5/vote/
path("<int:question_id>/vote/", views.vote, name="vote"),
]
Toma una mirada en tu navegador, en «/polls/34/». Correrá la función detail() y mostrará lo que proporcionas como ID en la URL. Intenta «/polls/34/results/» y «/polls/34/vote/» también – estas mostrarán las páginas de resultados y votación con contenido de reemplazo.
When somebody requests a page from your website – say, «/polls/34/», Django
will load the mysite.urls Python module because it’s pointed to by the
ROOT_URLCONF setting. It finds the variable named urlpatterns
and traverses the patterns in order. After finding the match at 'polls/',
it strips off the matching text ("polls/") and sends the remaining text –
"34/" – to the “polls.urls” URLconf for further processing. There it
matches '<int:question_id>/', resulting in a call to the detail() view
like so:
detail(request=<HttpRequest object>, question_id=34)
La parte question_id=34 proviene de <int:question_id>. Utilizando los ángulos brackets «captura» parte de la URL y envía como argumento de palabra clave al función del visor. La parte question_id de la cadena define el nombre que se utilizará para identificar el patrón coincidente, y la parte int es un conversor que determina qué patrones deben coincidir con esta parte de la ruta de URL. El signo de dos puntos (:) separa el conversor y el nombre del patrón.
Cada visor es responsable de hacer una de las dos cosas: devolver un objeto HttpResponse que contiene el contenido para la página solicitada, o levantar una excepción como Http404. El resto está a tu cargo.
Tu visor puede leer registros de una base de datos, o no. Puede utilizar un sistema de plantillas como Django – o un sistema de plantillas Python de terceros – o no. Puede generar un archivo PDF, salida XML, crear un archivo ZIP en vivo, cualquier cosa que desees, utilizando las bibliotecas Python que deseeis.
Todo lo que quiere Django es ese HttpResponse. O una excepción.
Porque es conveniente, vamos a utilizar la API de base de datos propia de Django, que cubrimos en Tutorial 2. Aquí tienes un intento de crear una nueva vista index() , que muestra las últimas 5 preguntas de encuesta del sistema, separadas por comas, según la fecha de publicación:
polls/views.py¶from django.http import HttpResponse
from .models import Question
def index(request):
latest_question_list = Question.objects.order_by("-pub_date")[:5]
output = ", ".join([q.question_text for q in latest_question_list])
return HttpResponse(output)
# Leave the rest of the views (detail, results, vote) unchanged
Hay un problema aquí: el diseño de la página está hardcodeado en el visor. Si deseas cambiar la forma en que se ve la página, tendrás que editar este código Python. Así que vamos a utilizar el sistema de plantillas de Django para separar el diseño del Python creando una plantilla que el visor pueda usar.
Primero, crea un directorio llamado templates en tu directorio polls . Django buscará las plantillas allí.
La configuración de la TEMPLATES de tu proyecto describe cómo Django cargará y renderizará las plantillas. La configuración predeterminada configura un backend DjangoTemplates cuya opción APP_DIRS está establecida en True . Por convención, DjangoTemplates busca una subcarpeta «templates» en cada uno de los INSTALLED_APPS.
Within el directorio templates que has creado, crea otro directorio llamado polls, y dentro de ese crear un archivo llamado index.html. En otras palabras, tu plantilla debería estar en polls/templates/polls/index.html. Debido a cómo funciona el cargador de plantillas app_directories, puedes referirte a esta plantilla dentro de Django como polls/index.html.
Nombrespaciado de plantillas
Ahora podríamos intentar poner nuestras plantillas directamente en polls/templates (en lugar de crear otro subdirectorio llamado polls), pero sería una mala idea. Django elegirá la primera plantilla que encuentre cuyo nombre coincida, y si tenías una plantilla con el mismo nombre en otra aplicación, Django no podría distinguir entre ellas. Necesitamos poder apuntar a la correcta, y la mejor manera de asegurarlo es mediante el nombrespaciado, es decir, poniéndolas dentro de otro directorio llamado por la propia aplicación.
Coloca el siguiente código en esa plantilla:
polls/templates/polls/index.html¶{% if latest_question_list %}
<ul>
{% for question in latest_question_list %}
<li><a href="/polls/{{ question.id }}/">{{ question.question_text }}</a></li>
{% endfor %}
</ul>
{% else %}
<p>No polls are available.</p>
{% endif %}
Nota
Para hacer que el tutorial sea más corto, todos los ejemplos de plantillas utilizan HTML incompleto. En tus propios proyectos deberías utilizar documentos HTML completos.
Ahora actualicemos nuestra vista index en polls/views.py para usar la plantilla:
polls/views.py¶from django.http import HttpResponse
from django.template import loader
from .models import Question
def index(request):
latest_question_list = Question.objects.order_by("-pub_date")[:5]
template = loader.get_template("polls/index.html")
context = {"latest_question_list": latest_question_list}
return HttpResponse(template.render(context, request))
Ese código carga la plantilla llamada polls/index.html y le pasa un contexto. El contexto es un diccionario que mapea nombres de variables de plantilla a objetos Python.
Carga la página apuntando tu navegador a «/polls/», y deberías ver una lista con puntos enumerados conteniendo la pregunta «¿Qué hay?» del Tutorial 2. El enlace apunta a la página de detalles de la pregunta.
render()¶Es un idiomático muy común cargar una plantilla, llenar un contexto y devolver un objeto HttpResponse con el resultado de la plantilla renderizada. Django proporciona un atajo. Aquí está la vista completa index() reescrita:
polls/views.py¶from django.shortcuts import render
from .models import Question
def index(request):
latest_question_list = Question.objects.order_by("-pub_date")[:5]
context = {"latest_question_list": latest_question_list}
return render(request, "polls/index.html", context)
Ten en cuenta que una vez que hayamos hecho esto en todas estas vistas, ya no necesitaremos importar loader y HttpResponse (quieres mantener HttpResponse si todavía tienes los métodos de stub para detail, results y vote).
La función render() toma el objeto solicitud como su primer argumento, un nombre de plantilla como su segundo argumento y un diccionario como su tercer argumento opcional. Devuelve un objeto HttpResponse con la plantilla dada renderizada con el contexto dado.
Ahora, vamos a abordar la vista de detalles de la pregunta – la página que muestra el texto de la pregunta para un voto determinado. Aquí está la vista:
polls/views.py¶from django.http import Http404
from django.shortcuts import render
from .models import Question
# ...
def detail(request, question_id):
try:
question = Question.objects.get(pk=question_id)
except Question.DoesNotExist:
raise Http404("Question does not exist")
return render(request, "polls/detail.html", {"question": question})
El nuevo concepto aquí: La vista eleva la Http404 excepción si una pregunta con el ID solicitado no existe.
Vamos a discutir qué podrías poner en esa plantilla polls/detail.html un poco más adelante, pero si deseas obtener rápidamente el ejemplo anterior funcionando, un archivo conteniendo solo:
polls/templates/polls/detail.html¶{{ question }}
te permitirá empezar por ahora.
get_object_or_404()¶It’s a very common idiom to use get()
and raise Http404 if the object doesn’t exist. Django
provides a shortcut. Here’s the detail() view, rewritten:
polls/views.py¶from django.shortcuts import get_object_or_404, render
from .models import Question
# ...
def detail(request, question_id):
question = get_object_or_404(Question, pk=question_id)
return render(request, "polls/detail.html", {"question": question})
La función get_object_or_404() toma un modelo de Django como su primer argumento y un número arbitrario de argumentos de palabra clave, que se pasan a la función get() del administrador del modelo. Lanza Http404 si el objeto no existe.
Filosofía
¿Por qué usamos una función auxiliar get_object_or_404() en lugar de atrapar automáticamente las excepciones ObjectDoesNotExist a un nivel superior, o hacer que la API del modelo lance Http404 en lugar de ObjectDoesNotExist?
Porque eso acoplaría el capa del modelo a la capa de vistas. Uno de los objetivos de diseño más importantes de Django es mantener una acoplamiento suelto. Se introduce un acoplamiento controlado en el módulo django.shortcuts.
También hay una función get_list_or_404() que funciona exactamente como get_object_or_404() – excepto utilizando la función filter() en lugar de get(). Lanza Http404 si la lista está vacía.
Vuelve al detail() view para nuestra aplicación de encuesta. Dado el contexto variable question, aquí está lo que podría ser el template polls/detail.html:
polls/templates/polls/detail.html¶<h1>{{ question.question_text }}</h1>
<ul>
{% for choice in question.choice_set.all %}
<li>{{ choice.choice_text }}</li>
{% endfor %}
</ul>
El sistema de plantillas utiliza la sintaxis de búsqueda con puntos para acceder a las atributos de variables. En el ejemplo de {{ question.question_text }}, primero Django hace una búsqueda en un diccionario del objeto question. Si eso falla, intenta una búsqueda de atributo – que funciona en este caso. Si la búsqueda de atributo hubiera fallado, habría probado una búsqueda de índice de lista.
El llamado a métodos sucede en el bucle {% for %}: question.choice_set.all se interpreta como el código Python question.choice_set.all(), que devuelve un iterable de objetos Choice y es adecuado para usar con la etiqueta {% for %}.
Consulte la guía de plantillas :doc:` </topics/templates>` para más información sobre las plantillas.
Recuerda que cuando escribimos el enlace a una pregunta en la plantilla polls/index.html, el enlace estaba parcialmente codificado de esta manera:
<li><a href="/polls/{{ question.id }}/">{{ question.question_text }}</a></li>
El problema con este enfoque estrechamente acoplado es que se vuelve difícil cambiar URLs en proyectos con muchas plantillas. Sin embargo, ya que definiste el argumento name en las funciones path() del módulo polls.urls, puedes eliminar la dependencia de URL específicas definidas en tus configuraciones de url utilizando el etiqueta de plantilla {% url %}:
<li><a href="{% url 'detail' question.id %}">{{ question.question_text }}</a></li>
La forma en que funciona es buscando la definición de URL tal como se especifica en el módulo polls.urls. Puedes ver exactamente dónde está definida la URL del nombre “detail” a continuación:
...
# the 'name' value as called by the {% url %} template tag
path("<int:question_id>/", views.detail, name="detail"),
...
Si deseas cambiar la URL de la vista de detalles de encuestas a algo diferente, quizás a algo como polls/specifics/12/ en lugar de hacerlo en la plantilla (o plantillas) lo harías en polls/urls.py:
...
# added the word 'specifics'
path("specifics/<int:question_id>/", views.detail, name="detail"),
...
El proyecto tutorial tiene solo una aplicación, polls. En proyectos Django reales, podrían haber cinco, diez, veinte aplicaciones o más. ¿Cómo diferencia Django los nombres de URL entre ellas? Por ejemplo, la aplicación polls tiene una vista detail, y así podría tenerla también una aplicación en el mismo proyecto para un blog. ¿Cómo se hace para que Django sepa qué vista de la aplicación a utilizar cuando se utiliza la etiqueta de plantilla {% url %}?
La respuesta es agregar nombrespaciados a tu archivo URLconf. En el archivo polls/urls.py, ve adelante y agrega un app_name para establecer el espacio de nombres de aplicación:
polls/urls.py¶from django.urls import path
from . import views
app_name = "polls"
urlpatterns = [
path("", views.index, name="index"),
path("<int:question_id>/", views.detail, name="detail"),
path("<int:question_id>/results/", views.results, name="results"),
path("<int:question_id>/vote/", views.vote, name="vote"),
]
Ahora cambia la plantilla polls/index.html desde:
polls/templates/polls/index.html¶<li><a href="{% url 'detail' question.id %}">{{ question.question_text }}</a></li>
to point at the namespaced detail view:
polls/templates/polls/index.html¶<li><a href="{% url 'polls:detail' question.id %}">{{ question.question_text }}</a></li>
When estás cómodo escribiendo vistas, lee parte 4 de este tutorial para aprender los fundamentos sobre el procesamiento de formularios y vistas generales.
may 31, 2026