# documentación de Django - 3.2.11.dev

# Escribiendo su primera aplicación en Django, parte 3

Este tutorial comienza donde quedó el Tutorial 2. Vamos a continuar con la aplicación Web para encuestas y vamos a enfocarnos en crear la interfaz pública –«vistas»

Dónde obtener ayuda:

If you’re having trouble going through this tutorial, please head over to the Getting Help section of the FAQ.

## Información general

Una vista es un «tipo» de página Web de su aplicación Django, que generalmente cumple una función específica y tiene una plantilla específica. Por ejemplo, en una aplicación de blog, usted puede tener las siguientes vistas:

- La página de inicio del blog – muestra las últimas pocas entradas.
- Página de entrada de «detail» – página de enlace permanente para una entrada individual.
- Página de archivo basado en el año – muestra todos los meses con entradas en un año determinado.
- Página de archivo basado en el mes – muestra todos los días con las entradas en un mes determinado.
- Página de archivo basado en el día – muestra todas las entradas de un día determinado.
- Comentar – gestiona la publicación de comentarios en una entrada determinada.

En nuestra aplicación encuestas, vamos a tener las siguientes cuatro vistas:

- La página «índice» de preguntas – muestra las últimas preguntas.
- La página «detalle» de preguntas– muestra un texto de pregunta sin resultados, pero con un formulario para votar.
- La página «resultados» de preguntas – muestra los resultados de una pregunta en particular.
- Votar – gestiona la votación para una elección concreta en una pregunta específica.

In Django, web pages and other content are delivered by views. Each view is represented by a Python function (or method, in the case of class-based views). Django will choose a view by examining the URL that’s requested (to be precise, the part of the URL after the domain name).

Now in your time on the web you may have come across such beauties as `ME2/Sites/dirmod.htm?sid=&type=gen&mod=Core+Pages&gid=A6CD4967199A42D9B65B1B`. You will be pleased to know that Django allows us much more elegant URL patterns than that.

A URL pattern is the general form of a URL - for example: `/newsarchive/<year>/<month>/`.

Para llegar desde una URL a una vista Django usa lo que se conoce como “URLconfs”. Una URLconf mapea patrones de URL a vistas.

Este tutorial proporciona instrucción básica en el uso de URLconfs. Usted puede consultar URL dispatcher para más información.

## Escribiendo más vistas

A continuación vamos a agregar más vistas a `polls/views.py`. Estas vistas son un poco 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)

Una estas nuevas vistas al módulo `polls.urls` añadiendo las siguientes llamadas `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'),
    ]

Eche un vistazo en su navegador a «/polls/34/». Va a ejecutar el método `detail()` y mostrar cualquier ID que usted suministre en la URL. Pruebe «/polls/34/results/» y también «/polls/34/vote/» - estas mostrarán los resultados del marcador de posición y las páginas de votación.

Cuando alguien solicita una página desde su sitio web - digamos, «/polls/34/», Django cargará el módulo Python `mysite.urls` porque está señalado por la configuración `ROOT_URLCONF`. Él encuentra la variable llamada `urlpatterns` y recorre las expresiones regulares en orden. Cuando encuentra la coincidencia `'polls/'` retira el texto que coincide (`"polls/"`) y envía el texto restante - `"34/"` - a la URLconf “polls.urls” para seguir siendo procesado. Allí la coincidencia `'<int:question_id>/'` resulta en una llamada a la vista `detail()` de la siguiente forma:

    detail(request=<HttpRequest object>, question_id=34)

The `question_id=34` part comes from `<int:question_id>`. Using angle brackets «captures» part of the URL and sends it as a keyword argument to the view function. The `question_id` part of the string defines the name that will be used to identify the matched pattern, and the `int` part is a converter that determines what patterns should match this part of the URL path. The colon (`:`) separates the converter and pattern name.

## Escriba vistas que realmente hagan algo

Cada vista es responsable de hacer una de dos cosas: retornar un objeto `HttpResponse` con el contenido de la página solicitada, o levantar una excepción como `Http404`. El resto depende de usted.

Su 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 Python de terceros o no. Puede generar un archivo PDF, salida XML, crear un archivo ZIP en la marcha, todo lo que quiera, utilizando cualquier librería de Python que desee.

Todo Django quiere es ese objeto `HttpResponse` o una excepción.

Debido a que es conveniente, vamos a utilizar la propia API de base de datos de Django, que estudiamos en el Tutorial 2. A continuación señalaremos una nueva vista `index()` que muestra en el sistema las 5 últimas preguntas de la encuesta, separadas por comas, según su 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

Sin embargo, aquí hay un problema: el diseño de la página está codificado de forma fija en la vista. Si desea cambiar la forma en que se ve la página, tendrá que editar el código Python. Así que vamos a usar el sistema de plantillas de Django para separar el diseño de Python mediante la creación de una plantilla que la vista pueda utilizar.

Primero, cree un directorio llamado `templates` en su directorio `polls`. Django buscará las plantillas allí.

La configuración de `TEMPLATES` de su proyecto describe cómo Django cargará y creará las plantillas. El archivo de configuración predeterminada configura un backend `DjangoTemplates` cuya opción `APP_DIRS` está configurada como `True`. Convencionalmente, `DjangoTemplates` busca un subdirectorio «templates» en cada una de las `INSTALLED_APPS`.

Within the `templates` directory you have just created, create another directory called `polls`, and within that create a file called `index.html`. In other words, your template should be at `polls/templates/polls/index.html`. Because of how the `app_directories` template loader works as described above, you can refer to this template within Django as `polls/index.html`.

Espacio de nombres de plantillas

Now we might be able to get away with putting our templates directly in `polls/templates` (rather than creating another `polls` subdirectory), but it would actually be a bad idea. Django will choose the first template it finds whose name matches, and if you had a template with the same name in a different application, Django would be unable to distinguish between them. We need to be able to point Django at the right one, and the best way to ensure this is by namespacing them. That is, by putting those templates inside another directory named for the application itself.

Introduzca 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

To make the tutorial shorter, all template examples use incomplete HTML. In your own projects you should use complete HTML documents.

Ahora vamos a actualizar 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 relaciona los nombres de variables de plantillas con objetos Python.

Cargue la página señalando su navegador en «/polls/», y usted debería ver una lista con viñetas que contiene la pregunta «¿Qué pasa?» del Tutorial 2. El enlace señala la página de detalles de la pregunta.

### Un atajo: `render()`

Es una práctica muy común cargar una plantilla, llenar un contexto y retornar un objeto `HttpResponse` con el resultado de la plantilla creada. Django proporciona un atajo. A continuación la vista `index()` completa, 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)

Tenga en cuenta que una vez que hemos hecho esto en todas estas vistas, ya no necesitamos importar `loader` y `HttpResponse` (usted querrá continuar `HttpResponse` si usted todavía tiene los métodos stub para `detail`, `results` y `vote`).

La función `render()` toma el objeto solicitado como su primer argumento, un nombre de plantilla como su segundo argumento y un diccionario como su tercer argumento opcional. La función retorna un objeto `HttpResponse` de la plantilla determinada creada con el contexto dado.

## Levantar un error 404

Ahora vamos a abordar la vista de detalle de la pregunta: la página que muestra el texto de pregunta para una encuesta determinada. 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 levanta la excepción `Http404` si no existe una pregunta con la ID solicitada.

Hablaremos de lo que podría poner en esa plantilla `polls/detail.html` un poco más tarde, pero si desea empezar a trabajar con el ejemplo anterior, un archivo que contenga simplemente:

polls/templates/polls/detail.html

    {{ question }}

le ayudará a empezar por ahora.

### Un atajo `get_object_or_404()`

Es una práctica muy común utilizar `get()` y levantar la excepción `Http404` si no existe el objeto. Django proporciona un atajo. Aquí está la vista `detail()`, reescrita:

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 Django como su primer argumento y un número arbitrario de argumentos de palabra clave que pasa a la función `get()` del administrador del modelo. Levanta la excepción `Http404` si no existe el objeto.

Filosofía

¿Por qué utilizamos una función auxiliar `get_object_or_404()` en lugar de capturar las excepciones `ObjectDoesNotExist` en un nivel superior, o hacer que la API del modelo levante la excepción `Http404` en vez de la excepción `ObjectDoesNotExist`?

Porque eso acoplaría la capa de modelos con la capa de vistas. Uno de los objetivos de diseño más destacados de Django es mantener el bajo acoplamiento. Algo de acoplamiento controlado se introduce en el módulo `django.shortcuts`.

También hay una función `get_list_or_404()` , que funciona igual que `get_object_or_404()` - excepto usando `filter()` en lugar de `get()`. La misma levanta la excepción `Http404` si la lista está vacía.

## Utilice el sistema de plantillas

Vuelva a la vista `detail()` para nuestra aplicación encuesta. Teniendo en cuenta la variable de contexto `question`, así es como la plantilla `polls/detail.html` podría verse:

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 los atributos de variables. En el ejemplo de `{{question.question_text}}`, Django primero realiza una consulta en el diccionario sobre el objeto `question`. Si esto falla, intenta una búsqueda de atributos que en este caso funciona. Si la búsqueda de atributos hubiera fallado, hubiera intentado una búsqueda de índice de lista.

El llamado del método ocurre en el bucle `{% for%}`: `question.choice_set.all` se interpreta como el código Python `question.choice_set.all()` que retorna un iterable de objetos `Choice` y es adecuado para usarse en la etiqueta `{% for%}`.

Consulte la guía de plantillas para más información sobre las plantillas.

## Quitar URLs codificadas de manera predeterminada en las plantillas

Recuerde que cuando escribimos el enlace para una pregunta en la plantilla `polls/index.html`, el enlace estaba parcialmente codificado de forma predeterminada como este:

    <li><a href="/polls/{{ question.id }}/">{{ question.question_text }}</a></li>

El problema con este método de codificación predeterminada y estrechamente acoplada es que se hace difícil modificar las URLs en proyectos que tengan muchas plantillas. Sin embargo, puesto que usted definió el argumento de nombre en las funciones `path()` en el módulo `polls.urls`, usted puede eliminar la dependencia en rutas URL específicas definida en su configuración de URL usando la etiqueta de plantilla `{% url %}`:

    <li><a href="{% url 'detail' question.id %}">{{ question.question_text }}</a></li>

La forma como esto funciona es buscando la definición de la URL como se específica en el módulo `polls.urls`. Usted puede ver exactamente donde se define el nombre de la URL de “detail” a continuación:

    ...
    # the 'name' value as called by the {% url %} template tag
    path('<int:question_id>/', views.detail, name='detail'),
    ...

Si desea modificar la URL de la vista de detalle de las encuestas a algo diferente, quizás a algo como `polls/specifics/12/`, en lugar de hacerlo en la plantilla (o plantillas), usted la modificaría en `polls/urls.py`:

    ...
    # added the word 'specifics'
    path('specifics/<int:question_id>/', views.detail, name='detail'),
    ...

## Asignando los nombres de URLs

El proyecto tutorial solo tiene una aplicación; `polls`. En proyectos reales de Django, puede haber cinco, diez, veinte o más aplicaciones. ¿Cómo diferencia Django los nombres de las URLs entre ellos? Por ejemplo, la aplicación `polls` tiene una vista `detail`, como la podría tener también una aplicación en el mismo proyecto que es para un blog. ¿Cómo hacer para que Django sepa cual vista de aplicaciones crear para una URL cuando se utiliza la etiqueta de plantilla`` {% url%}``?

La solución es añadir espacios de nombres a su URLconf. En el archivo `polls/urls.py`, añada un `app_name` para configurar el espacio de nombres de la 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 modifique su plantilla `polls/index.html` desde:

polls/templates/polls/index.html

    <li><a href="{% url 'detail' question.id %}">{{ question.question_text }}</a></li>

para señalar la vista de detalle con espacio de nombres:

polls/templates/polls/index.html

    <li><a href="{% url 'polls:detail' question.id %}">{{ question.question_text }}</a></li>

When you’re comfortable with writing views, read part 4 of this tutorial to learn the basics about form processing and generic views.