Introducción
Este tutorial comienza donde dejó Configurar la base de datos. Continuaremos con la aplicación de sondeo web y nos centraremos en la creación de la interfaz pública: "vistas".
💡 Este tutorial está traducido por IA desde la versión en inglés. Para ver la versión original, puedes hacer clic aquí
Este tutorial comienza donde dejó Configurar la base de datos. Continuaremos con la aplicación de sondeo web y nos centraremos en la creación de la interfaz pública: "vistas".
Una vista es un "tipo" de página web en su aplicación de Django que generalmente tiene una función específica y una plantilla específica. Por ejemplo, en una aplicación de blog, es posible que tenga las siguientes vistas:
En nuestra aplicación de sondeo, tendremos las siguientes cuatro vistas:
En Django, las páginas web y otros contenidos se entregan por vistas. Cada vista está representada por una función de Python (o un método, en el caso de las vistas basadas en clases). Django elegirá una vista examinando la URL que se solicita (para ser precisos, la parte de la URL después del nombre de dominio).
Ahora, durante su tiempo en la web, es posible que haya encontrado hermosas URLs como ME2/Sites/dirmod.htm?sid=&type=gen&mod=Core+Pages&gid=A6CD4967199A42D9B65B1B
. Tendrá el agrado de saber que Django nos permite patrones de URL mucho más elegantes 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 patrones de URL a vistas.
Este tutorial proporciona instrucciones básicas sobre el uso de URLconfs, y puede consultar /topics/http/urls
para obtener más información.
Ahora agreguemos algunas más vistas a polls/views.py
. Estas vistas son un poco diferentes, porque toman un argumento:
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)
Conecte estas nuevas vistas al módulo polls.urls
agregando las siguientes llamadas a ~django.urls.path
:
Edite el archivo polls/urls.py
y agregue las siguientes líneas:
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"),
]
Ahora, ejecute el servidor nuevamente:
cd ~/project/mysite
python manage.py runserver 0.0.0.0:8080
Vaya a la pestaña Web 8080, en /polls/34/
. Ejecutará el método detail()
y mostrará el ID que proporcione en la URL. Pruebe también /polls/34/results/
y /polls/34/vote/
; estos mostrarán las páginas de resultados y de votación con marcadores de posición.
Cuando alguien solicita una página de su sitio web, digamos, /polls/34/
, Django cargará el módulo de Python mysite.urls
porque está apuntado por la configuración ROOT_URLCONF
. Encuentra la variable llamada urlpatterns
y recorre los patrones en orden. Después de encontrar la coincidencia en 'polls/'
, quita el texto coincidente ("polls/"
) y envía el texto restante, "34/"
, al URLconf de 'polls.urls'
para su procesamiento adicional. Allí coincide con '<int:question_id>/'
, lo que resulta en una llamada a la vista detail()
de la siguiente manera:
detail(request=<HttpRequest object>, question_id=34)
La parte question_id=34
proviene de <int:question_id>
. Usando corchetes angulares, "captura" una parte de la URL y la envía como un argumento de palabras clave a la función de vista. La parte question_id
de la cadena define el nombre que se usará para identificar el patrón coincidente, y la parte int
es un convertidor que determina qué patrones deben coincidir con esta parte de la ruta de URL. El dos puntos (:
) separa el convertidor y el nombre del patrón.
Cada vista es responsable de hacer una de dos cosas: devolver un objeto ~django.http.HttpResponse
que contenga el contenido de la página solicitada, o generar una excepción como ~django.http.Http404
. Lo demás depende de ti.
Tu vista puede leer registros de una base de datos, o no. Puede usar un sistema de plantillas como el de Django, o un sistema de plantillas de terceros para Python, o no. Puede generar un archivo PDF, emitir XML, crear un archivo ZIP en el vuelo, cualquier cosa que desees, usando cualquier biblioteca de Python que desees.
Todo lo que Django quiere es ese objeto ~django.http.HttpResponse
. O una excepción.
Por conveniencia, usemos la propia API de base de datos de Django, que cubrimos en el Tutorial 2. Aquí está un intento de una nueva vista index()
, que muestra las últimas 5 preguntas de sondeo del sistema, separadas por comas, según la fecha de publicación:
Edite el archivo polls/views.py
y cámbielo para que se vea así:
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)
## Deje las demás vistas (detail, results, vote) sin cambios
Sin embargo, aquí hay un problema: el diseño de la página está codificado en duro en la vista. Si quieres cambiar la forma en que se ve la página, tendrás que editar este código de Python. Entonces, usemos el sistema de plantillas de Django para separar el diseño del Python creando una plantilla que la vista pueda usar.
Primero, cree un directorio llamado templates
en su directorio polls
. Django buscará plantillas ahí.
La configuración TEMPLATES
de su proyecto describe cómo Django cargará y renderizará las plantillas. El archivo de configuración predeterminado configura un backend DjangoTemplates
cuya opción APP_DIRS <TEMPLATES-APP_DIRS>
está establecida en True
. Por convención, DjangoTemplates
busca un subdirectorio "templates" en cada una de las INSTALLED_APPS
.
Dentro del directorio templates
que acabas de crear, cree otro directorio llamado polls
, y dentro de ese cree un archivo llamado index.html
. En otras palabras, su plantilla debe estar en polls/templates/polls/index.html
. Debido a cómo funciona el cargador de plantillas app_directories
como se describe anteriormente, puede referirse a esta plantilla dentro de Django como polls/index.html
.
Ahora podríamos poder evitar poner nuestras plantillas directamente en polls/templates
(en lugar de crear otro subdirectorio polls
), pero en realidad sería una mala idea. Django elegirá la primera plantilla que encuentre cuyo nombre coincida, y si tuvieras una plantilla con el mismo nombre en una diferente aplicación, Django no sería capaz de distinguir entre ellas.
Necesitamos poder apuntar a Django a la correcta, y la mejor manera de garantizar esto es mediante el espaciado de nombres. Es decir, al poner esas plantillas dentro de otro directorio nombrado con el nombre de la aplicación misma.
Ponga el siguiente código en esa plantilla:
{% 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 el tutorial más corto, todos los ejemplos de plantilla usan HTML incompleto. En sus propios proyectos debe usar documentos HTML completos.
Ahora actualicemos nuestra vista index
en polls/views.py
para usar la plantilla:
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 los nombres de variables de plantilla a objetos de Python.
Ejecute el servidor nuevamente:
python manage.py runserver 0.0.0.0:8080
Cargue la página apuntando su navegador a "/polls/", y debería ver una lista con viñetas que contiene la pregunta "¿Qué pasa?" del Tutorial 2. El enlace apunta a la página de detalles de la pregunta.
~django.shortcuts.render
Es un idioma muy común cargar una plantilla, llenar un contexto y devolver un objeto ~django.http.HttpResponse
con el resultado de la plantilla renderizada. Django proporciona un atajo. Aquí está la vista index()
completa, reescrita:
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)
Tenga en cuenta que una vez que lo hemos hecho en todas estas vistas, ya no necesitamos importar ~django.template.loader
y ~django.http.HttpResponse
(deberá mantener HttpResponse
si todavía tiene los métodos de esbozo para detail
, results
y vote
).
La función ~django.shortcuts.render
toma el objeto de solicitud como su primer argumento, el nombre de una plantilla como su segundo argumento y un diccionario como su tercer argumento opcional. Devuelve un objeto ~django.http.HttpResponse
de la plantilla dada renderizada con el contexto dado.
Ahora, abordemos la vista de detalles de la pregunta, la página que muestra el texto de la pregunta para un sondeo dado. Aquí está la vista:
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 lanza la excepción ~django.http.Http404
si no existe una pregunta con el ID solicitado.
Vamos a discutir un poco más adelante qué podrías poner en esa plantilla polls/detail.html
, pero si quieres que el ejemplo anterior funcione rápidamente, un archivo que contenga solo:
{{ question }}
te ayudará a empezar por ahora.
~django.shortcuts.get_object_or_404
Es un idioma muy común usar ~django.db.models.query.QuerySet.get
y lanzar ~django.http.Http404
si el objeto no existe. Django proporciona un atajo. Aquí está la vista detail()
, reescrita:
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 ~django.shortcuts.get_object_or_404
toma un modelo de Django como su primer argumento y un número arbitrario de argumentos de palabras clave, que pasa a la función ~django.db.models.query.QuerySet.get
del administrador del modelo. Lanza ~django.http.Http404
si el objeto no existe.
¿Por qué usamos una función auxiliar ~django.shortcuts.get_object_or_404
en lugar de capturar automáticamente las excepciones ~django.core.exceptions.ObjectDoesNotExist
en un nivel superior, o hacer que la API del modelo lance ~django.http.Http404
en lugar de ~django.core.exceptions.ObjectDoesNotExist
?
Porque eso uniría la capa del modelo a la capa de la vista. Uno de los objetivos de diseño principales de Django es mantener un acoplamiento débil. Un poco de acoplamiento controlado se introduce en el módulo django.shortcuts
.
También hay una función ~django.shortcuts.get_list_or_404
, que funciona de la misma manera que ~django.shortcuts.get_object_or_404
, excepto que utiliza ~django.db.models.query.QuerySet.filter
en lugar de ~django.db.models.query.QuerySet.get
. Lanza ~django.http.Http404
si la lista está vacía.
Volvamos a la vista detail()
de nuestra aplicación de sondeo. Dada la variable de contexto question
, así podría verse la plantilla 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 por puntos para acceder a los atributos de las variables. En el ejemplo de {{ question.question_text }}
, primero Django realiza una búsqueda en el diccionario del objeto question
. Si falla, intenta una búsqueda de atributo, lo que funciona en este caso. Si la búsqueda de atributo hubiera fallado, habría intentado una búsqueda por índice de lista.
La llamada a métodos se produce en el bucle {% for %}<for>
: question.choice_set.all
se interpreta como el código de Python question.choice_set.all()
, que devuelve un iterable de objetos Choice
y es adecuado para utilizarse en la etiqueta {% for %}<for>
.
Recuerde, cuando escribimos el enlace a una pregunta en la plantilla polls/index.html
, el enlace estaba parcialmente codificado en duro de la siguiente manera:
<li><a href="/polls/{{ question.id }}/">{{ question.question_text }}</a></li>
El problema de este enfoque codificado en duro y fuertemente acoplado es que se vuelve difícil cambiar las URLs en proyectos con muchas plantillas. Sin embargo, dado que definiste el argumento name
en las funciones ~django.urls.path
del módulo polls.urls
, puedes eliminar la dependencia de las rutas de URL específicas definidas en tus configuraciones de URL mediante la 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 como se especifica en el módulo polls.urls
. Puedes ver exactamente dónde se define el nombre de URL de 'detail' a continuación:
## el valor 'name' como se llama por la etiqueta de plantilla {% url %}
path("<int:question_id>/", views.detail, name="detail"),
Si quieres cambiar la URL de la vista de detalles de los sondeos a algo diferente, quizás a algo como polls/specifics/12/
en lugar de hacerlo en la plantilla (o plantillas), lo cambiarías en polls/urls.py
:
No es necesario cambiar la plantilla en absoluto.
## se agregó la palabra'specifics'
path("specifics/<int:question_id>/", views.detail, name="detail"),
El proyecto del tutorial solo tiene una aplicación, polls
. En los proyectos reales de Django, pueden haber cinco, diez, veinte aplicaciones o más. ¿Cómo distingue Django los nombres de URL entre ellas? Por ejemplo, la aplicación polls
tiene una vista detail
, y también podría haber una aplicación en el mismo proyecto que es para un blog. ¿Cómo se hace para que Django sepa qué vista de aplicación crear para una URL cuando se utiliza la etiqueta de plantilla {% url %}
?
La respuesta es agregar espacios de nombres a su URLconf. En el archivo polls/urls.py
, agregue un app_name
para establecer el espacio de nombres de la aplicación:
from django.urls import path
from. import views
app_name = "polls"
urlpatterns = [
## ej: /polls/
path("", views.index, name="index"),
## ej: /polls/5/
path("<int:question_id>/", views.detail, name="detail"),
## ej: /polls/5/results/
path("<int:question_id>/results/", views.results, name="results"),
## ej: /polls/5/vote/
path("<int:question_id>/vote/", views.vote, name="vote"),
]
Ahora cambie su plantilla polls/index.html
de:
<li><a href="{% url 'detail' question.id %}">{{ question.question_text }}</a></li>
para apuntar a la vista de detalles con espaciado de nombres:
<li><a href="{% url 'polls:detail' question.id %}">{{ question.question_text }}</a></li>
Cuando esté cómodo escribiendo vistas, lea Procesamiento de formularios y reducción de nuestro código para aprender los conceptos básicos sobre el procesamiento de formularios y vistas genéricas.
¡Felicidades! Has completado el laboratorio de Creación de Vistas de la Interfaz Pública. Puedes practicar más laboratorios en LabEx para mejorar tus habilidades.