{"title":"Escribiendo su primera aplicación en Django, parte 4","version":"4.1","locale":"es","docname":"intro/tutorial04","url":"/es/4.1/intro/tutorial04/","canonical":"https://djangodocs.dev/es/4.1/intro/tutorial04/","summary":"This tutorial begins where Tutorial 3 left off. We’re continuing the web-poll application and will focus on form processing and cutting down our code. Dónde obtener…","html":"<h1>Escribiendo su primera aplicación en Django, parte 4<a class=\"heading-anchor\" href=\"#writing-your-first-django-app-part-4\"><span class=\"visually-hidden\">Link to this heading</span><span aria-hidden=\"true\">#</span></a></h1>\n<p>This tutorial begins where <a class=\"reference internal\" href=\"/es/4.1/intro/tutorial03/\"><span class=\"doc\">Tutorial 3</span></a> left off. We’re\ncontinuing the web-poll application and will focus on form processing and\ncutting down our code.</p>\n<aside class=\"admonition-where-to-get-help admonition\">\n<p class=\"admonition-title\">Dónde obtener ayuda:</p>\n<p>If you’re having trouble going through this tutorial, please head over to\nthe <a class=\"reference internal\" href=\"/es/4.1/faq/help/\"><span class=\"doc\">Getting Help</span></a> section of the FAQ.</p>\n</aside>\n<section id=\"write-a-minimal-form\">\n<h2>Write a minimal form<a class=\"heading-anchor\" href=\"#write-a-minimal-form\"><span class=\"visually-hidden\">Link to this heading</span><span aria-hidden=\"true\">#</span></a></h2>\n<p>Vamos a actualizar nuestra plantilla de detalles de encuestas («polls/detail.html») a partir del último tutorial, de modo que la plantilla contenga un elemento HTML <code class=\"docutils literal notranslate\"><span class=\"pre\">&lt;form&gt;</span></code>:</p>\n<figure class=\"code-block code-block-captioned\" data-language=\"html+django\"><figcaption class=\"code-block-caption\"><code class=\"docutils literal notranslate\"><span class=\"pre\">polls/templates/polls/detail.html</span></code></figcaption>\n<div class=\"code-block-toolbar\"><span class=\"code-block-language\">Django template</span><button type=\"button\" class=\"copy-button\" data-copy hidden><span class=\"copy-button-label\">Copy</span></button></div><pre role=\"group\" tabindex=\"0\" aria-label=\"Django template code\"><code><span class=\"p\">&lt;</span><span class=\"nt\">form</span> <span class=\"na\">action</span><span class=\"o\">=</span><span class=\"s\">&quot;</span><span class=\"cp\">{%</span> <span class=\"k\">url</span> <span class=\"s1\">&#39;polls:vote&#39;</span> <span class=\"nv\">question.id</span> <span class=\"cp\">%}</span><span class=\"s\">&quot;</span> <span class=\"na\">method</span><span class=\"o\">=</span><span class=\"s\">&quot;post&quot;</span><span class=\"p\">&gt;</span>\n<span class=\"cp\">{%</span> <span class=\"k\">csrf_token</span> <span class=\"cp\">%}</span>\n<span class=\"p\">&lt;</span><span class=\"nt\">fieldset</span><span class=\"p\">&gt;</span>\n    <span class=\"p\">&lt;</span><span class=\"nt\">legend</span><span class=\"p\">&gt;&lt;</span><span class=\"nt\">h1</span><span class=\"p\">&gt;</span><span class=\"cp\">{{</span> <span class=\"nv\">question.question_text</span> <span class=\"cp\">}}</span><span class=\"p\">&lt;/</span><span class=\"nt\">h1</span><span class=\"p\">&gt;&lt;/</span><span class=\"nt\">legend</span><span class=\"p\">&gt;</span>\n    <span class=\"cp\">{%</span> <span class=\"k\">if</span> <span class=\"nv\">error_message</span> <span class=\"cp\">%}</span><span class=\"p\">&lt;</span><span class=\"nt\">p</span><span class=\"p\">&gt;&lt;</span><span class=\"nt\">strong</span><span class=\"p\">&gt;</span><span class=\"cp\">{{</span> <span class=\"nv\">error_message</span> <span class=\"cp\">}}</span><span class=\"p\">&lt;/</span><span class=\"nt\">strong</span><span class=\"p\">&gt;&lt;/</span><span class=\"nt\">p</span><span class=\"p\">&gt;</span><span class=\"cp\">{%</span> <span class=\"k\">endif</span> <span class=\"cp\">%}</span>\n    <span class=\"cp\">{%</span> <span class=\"k\">for</span> <span class=\"nv\">choice</span> <span class=\"k\">in</span> <span class=\"nv\">question.choice_set.all</span> <span class=\"cp\">%}</span>\n        <span class=\"p\">&lt;</span><span class=\"nt\">input</span> <span class=\"na\">type</span><span class=\"o\">=</span><span class=\"s\">&quot;radio&quot;</span> <span class=\"na\">name</span><span class=\"o\">=</span><span class=\"s\">&quot;choice&quot;</span> <span class=\"na\">id</span><span class=\"o\">=</span><span class=\"s\">&quot;choice</span><span class=\"cp\">{{</span> <span class=\"nb\">forloop</span><span class=\"nv\">.counter</span> <span class=\"cp\">}}</span><span class=\"s\">&quot;</span> <span class=\"na\">value</span><span class=\"o\">=</span><span class=\"s\">&quot;</span><span class=\"cp\">{{</span> <span class=\"nv\">choice.id</span> <span class=\"cp\">}}</span><span class=\"s\">&quot;</span><span class=\"p\">&gt;</span>\n        <span class=\"p\">&lt;</span><span class=\"nt\">label</span> <span class=\"na\">for</span><span class=\"o\">=</span><span class=\"s\">&quot;choice</span><span class=\"cp\">{{</span> <span class=\"nb\">forloop</span><span class=\"nv\">.counter</span> <span class=\"cp\">}}</span><span class=\"s\">&quot;</span><span class=\"p\">&gt;</span><span class=\"cp\">{{</span> <span class=\"nv\">choice.choice_text</span> <span class=\"cp\">}}</span><span class=\"p\">&lt;/</span><span class=\"nt\">label</span><span class=\"p\">&gt;&lt;</span><span class=\"nt\">br</span><span class=\"p\">&gt;</span>\n    <span class=\"cp\">{%</span> <span class=\"k\">endfor</span> <span class=\"cp\">%}</span>\n<span class=\"p\">&lt;/</span><span class=\"nt\">fieldset</span><span class=\"p\">&gt;</span>\n<span class=\"p\">&lt;</span><span class=\"nt\">input</span> <span class=\"na\">type</span><span class=\"o\">=</span><span class=\"s\">&quot;submit&quot;</span> <span class=\"na\">value</span><span class=\"o\">=</span><span class=\"s\">&quot;Vote&quot;</span><span class=\"p\">&gt;</span>\n<span class=\"p\">&lt;/</span><span class=\"nt\">form</span><span class=\"p\">&gt;</span>\n</code></pre></figure>\n<p>Un resumen rápido:</p>\n<ul class=\"simple\">\n<li><p>La plantilla anterior muestra un botón de opción para cada pregunta de selección. El <code class=\"docutils literal notranslate\"><span class=\"pre\">value</span></code> de cada botón de opción es la ID de la pregunta de selección asociada. El <code class=\"docutils literal notranslate\"><span class=\"pre\">name</span></code> de cada botón de opción es <code class=\"docutils literal notranslate\"><span class=\"pre\">&quot;choice&quot;</span></code>. Esto significa que cuando alguien selecciona uno de los botones de opción y envía el formulario, enviará los datos POST <code class=\"docutils literal notranslate\"><span class=\"pre\">choice=#</span></code> donde # es la ID de la opción seleccionada. Este es el concepto básico de los formularios HTML.</p></li>\n<li><p>We set the form’s <code class=\"docutils literal notranslate\"><span class=\"pre\">action</span></code> to <code class=\"docutils literal notranslate\"><span class=\"pre\">{%</span> <span class=\"pre\">url</span> <span class=\"pre\">'polls:vote'</span> <span class=\"pre\">question.id</span> <span class=\"pre\">%}</span></code>, and we\nset <code class=\"docutils literal notranslate\"><span class=\"pre\">method=&quot;post&quot;</span></code>. Using <code class=\"docutils literal notranslate\"><span class=\"pre\">method=&quot;post&quot;</span></code> (as opposed to\n<code class=\"docutils literal notranslate\"><span class=\"pre\">method=&quot;get&quot;</span></code>) is very important, because the act of submitting this\nform will alter data server-side. Whenever you create a form that alters\ndata server-side, use <code class=\"docutils literal notranslate\"><span class=\"pre\">method=&quot;post&quot;</span></code>. This tip isn’t specific to\nDjango; it’s good web development practice in general.</p></li>\n<li><p>`` forloop.counter`` indica cuántas veces la etiqueta <a class=\"reference internal\" href=\"/es/4.1/ref/templates/builtins/#std-templatetag-for\"><code class=\"xref std std-ttag docutils literal notranslate\"><span class=\"pre\">for</span></code></a> ha pasado por el bucle</p></li>\n<li><p>Since we’re creating a POST form (which can have the effect of modifying\ndata), we need to worry about Cross Site Request Forgeries.\nThankfully, you don’t have to worry too hard, because Django comes with a\nhelpful system for protecting against it. In short, all POST forms that are\ntargeted at internal URLs should use the <a class=\"reference internal\" href=\"/es/4.1/ref/templates/builtins/#std-templatetag-csrf_token\"><code class=\"xref std std-ttag docutils literal notranslate\"><span class=\"pre\">{%</span> <span class=\"pre\">csrf_token</span> <span class=\"pre\">%}</span></code></a>\ntemplate tag.</p></li>\n</ul>\n<p>A continuación, vamos a crear una vista Django que maneje los datos enviados y haga algo con ello. Recuerde que en el <a class=\"reference internal\" href=\"/es/4.1/intro/tutorial03/\"><span class=\"doc\">Tutorial 3</span></a>, creamos un URLconf para la aplicación encuestas que incluye esta línea:</p>\n<figure class=\"code-block code-block-captioned\" data-language=\"python\"><figcaption class=\"code-block-caption\"><code class=\"docutils literal notranslate\"><span class=\"pre\">polls/urls.py</span></code></figcaption>\n<div class=\"code-block-toolbar\"><span class=\"code-block-language\">Python</span><button type=\"button\" class=\"copy-button\" data-copy hidden><span class=\"copy-button-label\">Copy</span></button></div><pre role=\"group\" tabindex=\"0\" aria-label=\"Python code\"><code><span class=\"n\">path</span><span class=\"p\">(</span><span class=\"s1\">&#39;&lt;int:question_id&gt;/vote/&#39;</span><span class=\"p\">,</span> <span class=\"n\">views</span><span class=\"o\">.</span><span class=\"n\">vote</span><span class=\"p\">,</span> <span class=\"n\">name</span><span class=\"o\">=</span><span class=\"s1\">&#39;vote&#39;</span><span class=\"p\">),</span>\n</code></pre></figure>\n<p>También creamos una implementación simulada de la función <code class=\"docutils literal notranslate\"><span class=\"pre\">vote()</span></code>. Vamos a crear una versión real. Agregue lo siguiente a <code class=\"docutils literal notranslate\"><span class=\"pre\">polls/views.py</span></code>:</p>\n<figure class=\"code-block code-block-captioned\" data-language=\"python\"><figcaption class=\"code-block-caption\"><code class=\"docutils literal notranslate\"><span class=\"pre\">polls/views.py</span></code></figcaption>\n<div class=\"code-block-toolbar\"><span class=\"code-block-language\">Python</span><button type=\"button\" class=\"copy-button\" data-copy hidden><span class=\"copy-button-label\">Copy</span></button></div><pre role=\"group\" tabindex=\"0\" aria-label=\"Python code\"><code><span class=\"kn\">from</span><span class=\"w\"> </span><span class=\"nn\">django.http</span><span class=\"w\"> </span><span class=\"kn\">import</span> <span class=\"n\">HttpResponse</span><span class=\"p\">,</span> <span class=\"n\">HttpResponseRedirect</span>\n<span class=\"kn\">from</span><span class=\"w\"> </span><span class=\"nn\">django.shortcuts</span><span class=\"w\"> </span><span class=\"kn\">import</span> <span class=\"n\">get_object_or_404</span><span class=\"p\">,</span> <span class=\"n\">render</span>\n<span class=\"kn\">from</span><span class=\"w\"> </span><span class=\"nn\">django.urls</span><span class=\"w\"> </span><span class=\"kn\">import</span> <span class=\"n\">reverse</span>\n\n<span class=\"kn\">from</span><span class=\"w\"> </span><span class=\"nn\">.models</span><span class=\"w\"> </span><span class=\"kn\">import</span> <span class=\"n\">Choice</span><span class=\"p\">,</span> <span class=\"n\">Question</span>\n<span class=\"c1\"># ...</span>\n<span class=\"k\">def</span><span class=\"w\"> </span><span class=\"nf\">vote</span><span class=\"p\">(</span><span class=\"n\">request</span><span class=\"p\">,</span> <span class=\"n\">question_id</span><span class=\"p\">):</span>\n    <span class=\"n\">question</span> <span class=\"o\">=</span> <span class=\"n\">get_object_or_404</span><span class=\"p\">(</span><span class=\"n\">Question</span><span class=\"p\">,</span> <span class=\"n\">pk</span><span class=\"o\">=</span><span class=\"n\">question_id</span><span class=\"p\">)</span>\n    <span class=\"k\">try</span><span class=\"p\">:</span>\n        <span class=\"n\">selected_choice</span> <span class=\"o\">=</span> <span class=\"n\">question</span><span class=\"o\">.</span><span class=\"n\">choice_set</span><span class=\"o\">.</span><span class=\"n\">get</span><span class=\"p\">(</span><span class=\"n\">pk</span><span class=\"o\">=</span><span class=\"n\">request</span><span class=\"o\">.</span><span class=\"n\">POST</span><span class=\"p\">[</span><span class=\"s1\">&#39;choice&#39;</span><span class=\"p\">])</span>\n    <span class=\"k\">except</span> <span class=\"p\">(</span><span class=\"ne\">KeyError</span><span class=\"p\">,</span> <span class=\"n\">Choice</span><span class=\"o\">.</span><span class=\"n\">DoesNotExist</span><span class=\"p\">):</span>\n        <span class=\"c1\"># Redisplay the question voting form.</span>\n        <span class=\"k\">return</span> <span class=\"n\">render</span><span class=\"p\">(</span><span class=\"n\">request</span><span class=\"p\">,</span> <span class=\"s1\">&#39;polls/detail.html&#39;</span><span class=\"p\">,</span> <span class=\"p\">{</span>\n            <span class=\"s1\">&#39;question&#39;</span><span class=\"p\">:</span> <span class=\"n\">question</span><span class=\"p\">,</span>\n            <span class=\"s1\">&#39;error_message&#39;</span><span class=\"p\">:</span> <span class=\"s2\">&quot;You didn&#39;t select a choice.&quot;</span><span class=\"p\">,</span>\n        <span class=\"p\">})</span>\n    <span class=\"k\">else</span><span class=\"p\">:</span>\n        <span class=\"n\">selected_choice</span><span class=\"o\">.</span><span class=\"n\">votes</span> <span class=\"o\">+=</span> <span class=\"mi\">1</span>\n        <span class=\"n\">selected_choice</span><span class=\"o\">.</span><span class=\"n\">save</span><span class=\"p\">()</span>\n        <span class=\"c1\"># Always return an HttpResponseRedirect after successfully dealing</span>\n        <span class=\"c1\"># with POST data. This prevents data from being posted twice if a</span>\n        <span class=\"c1\"># user hits the Back button.</span>\n        <span class=\"k\">return</span> <span class=\"n\">HttpResponseRedirect</span><span class=\"p\">(</span><span class=\"n\">reverse</span><span class=\"p\">(</span><span class=\"s1\">&#39;polls:results&#39;</span><span class=\"p\">,</span> <span class=\"n\">args</span><span class=\"o\">=</span><span class=\"p\">(</span><span class=\"n\">question</span><span class=\"o\">.</span><span class=\"n\">id</span><span class=\"p\">,)))</span>\n</code></pre></figure>\n<p>Este código incluye algunas cosas que no hemos cubierto todavía en este tutorial:</p>\n<ul>\n<li><p><a class=\"reference internal\" href=\"/es/4.1/ref/request-response/#django.http.HttpRequest.POST\" title=\"django.http.HttpRequest.POST\"><code class=\"xref py py-attr docutils literal notranslate\"><span class=\"pre\">request.POST</span></code></a> es un objeto tipo diccionario que le permite acceder a los datos presentados mediante el nombre clave. En este caso, <code class=\"docutils literal notranslate\"><span class=\"pre\">request.POST['choice']</span></code> retorna la ID de la opción seleccionada como una cadena. Los valores <a class=\"reference internal\" href=\"/es/4.1/ref/request-response/#django.http.HttpRequest.POST\" title=\"django.http.HttpRequest.POST\"><code class=\"xref py py-attr docutils literal notranslate\"><span class=\"pre\">request.POST</span></code></a> son siempre cadenas.</p>\n<p>Tenga en cuenta que Django también proporciona <a class=\"reference internal\" href=\"/es/4.1/ref/request-response/#django.http.HttpRequest.GET\" title=\"django.http.HttpRequest.GET\"><code class=\"xref py py-attr docutils literal notranslate\"><span class=\"pre\">request.GET</span></code></a> para acceder a datos GET de la misma manera, pero estamos usando de manera explícita <a class=\"reference internal\" href=\"/es/4.1/ref/request-response/#django.http.HttpRequest.POST\" title=\"django.http.HttpRequest.POST\"><code class=\"xref py py-attr docutils literal notranslate\"><span class=\"pre\">request.POST</span></code></a> en nuestro código, para garantizar que los datos sólo se modifiquen a través de una llamada POST.</p>\n</li>\n<li><p><code class=\"docutils literal notranslate\"><span class=\"pre\">request.POST['choice']</span></code> levantará un excepción :exc: KeyError si <code class=\"docutils literal notranslate\"><span class=\"pre\">choice</span></code> no fue proporcionado en los datos POST. El código anterior busca la excepción <a class=\"reference external\" href=\"https://docs.python.org/3/library/exceptions.html#KeyError\" title=\"(en Python versión 3.14)\"><code class=\"xref py py-exc docutils literal notranslate\"><span class=\"pre\">KeyError</span></code></a> y muestra de nuevo el formulario de pregunta con un mensaje de error si no se ha proporcionado <code class=\"docutils literal notranslate\"><span class=\"pre\">choice</span></code>.</p></li>\n<li><p>Después de incrementar el conteo de la elección, el código retorna una <a class=\"reference internal\" href=\"/es/4.1/ref/request-response/#django.http.HttpResponseRedirect\" title=\"django.http.HttpResponseRedirect\"><code class=\"xref py py-class docutils literal notranslate\"><span class=\"pre\">HttpResponseRedirect</span></code></a> en lugar de una <a class=\"reference internal\" href=\"/es/4.1/ref/request-response/#django.http.HttpResponse\" title=\"django.http.HttpResponse\"><code class=\"xref py py-class docutils literal notranslate\"><span class=\"pre\">HttpResponse</span></code></a> normal <a class=\"reference internal\" href=\"/es/4.1/ref/request-response/#django.http.HttpResponseRedirect\" title=\"django.http.HttpResponseRedirect\"><code class=\"xref py py-class docutils literal notranslate\"><span class=\"pre\">HttpResponseRedirect</span></code></a> toma un único argumento: La URL a la que el usuario será redirigido (vea el siguiente aspecto de cómo construimos la URL en este caso).</p>\n<p>As the Python comment above points out, you should always return an\n<a class=\"reference internal\" href=\"/es/4.1/ref/request-response/#django.http.HttpResponseRedirect\" title=\"django.http.HttpResponseRedirect\"><code class=\"xref py py-class docutils literal notranslate\"><span class=\"pre\">HttpResponseRedirect</span></code></a> after successfully dealing with\nPOST data. This tip isn’t specific to Django; it’s good web development\npractice in general.</p>\n</li>\n<li><p>Estamos utilizando la función <a class=\"reference internal\" href=\"/es/4.1/ref/urlresolvers/#django.urls.reverse\" title=\"django.urls.reverse\"><code class=\"xref py py-func docutils literal notranslate\"><span class=\"pre\">reverse()</span></code></a> en el constructor <a class=\"reference internal\" href=\"/es/4.1/ref/request-response/#django.http.HttpResponseRedirect\" title=\"django.http.HttpResponseRedirect\"><code class=\"xref py py-class docutils literal notranslate\"><span class=\"pre\">HttpResponseRedirect</span></code></a> en este ejemplo. Esta función ayuda a evitar tener que codificar una URL en la función de vista. Se proporciona el nombre de la vista a la que queremos pasar el control y la parte de la variable del patrón de URL que señala esa vista. En este caso, utilizando la URLconf que configuramos en el <a class=\"reference internal\" href=\"/es/4.1/intro/tutorial03/\"><span class=\"doc\">Tutorial 3</span></a>, esta llamada <a class=\"reference internal\" href=\"/es/4.1/ref/urlresolvers/#django.urls.reverse\" title=\"django.urls.reverse\"><code class=\"xref py py-func docutils literal notranslate\"><span class=\"pre\">reverse()</span></code></a> retornará una cadena como</p>\n<div class=\"code-block\" data-language=\"default\"><div class=\"code-block-toolbar\"><span class=\"code-block-language\">Code</span><button type=\"button\" class=\"copy-button\" data-copy hidden><span class=\"copy-button-label\">Copy</span></button></div><pre role=\"group\" tabindex=\"0\" aria-label=\"Code code\"><code><span class=\"s1\">&#39;/polls/3/results/&#39;</span>\n</code></pre></div>\n<p>donde el <code class=\"docutils literal notranslate\"><span class=\"pre\">3</span></code> es el valor de <code class=\"docutils literal notranslate\"><span class=\"pre\">question.id</span></code>. Esta URL redirigida entonces llamará a la vista <code class=\"docutils literal notranslate\"><span class=\"pre\">'results'</span></code> para mostrar la página final.</p>\n</li>\n</ul>\n<p>Como se mencionó en el <a class=\"reference internal\" href=\"/es/4.1/intro/tutorial03/\"><span class=\"doc\">Tutorial 3</span></a>, <code class=\"docutils literal notranslate\"><span class=\"pre\">request</span></code> es un objeto <a class=\"reference internal\" href=\"/es/4.1/ref/request-response/#django.http.HttpRequest\" title=\"django.http.HttpRequest\"><code class=\"xref py py-class docutils literal notranslate\"><span class=\"pre\">HttpRequest</span></code></a> . Para más información sobre los objetos <a class=\"reference internal\" href=\"/es/4.1/ref/request-response/#django.http.HttpRequest\" title=\"django.http.HttpRequest\"><code class=\"xref py py-class docutils literal notranslate\"><span class=\"pre\">HttpRequest</span></code></a>, consulte la <a class=\"reference internal\" href=\"/es/4.1/ref/request-response/\"><span class=\"doc\">documentación de petición y respuesta</span></a>.</p>\n<p>Después de que alguien vota en una pregunta, la vista <code class=\"docutils literal notranslate\"><span class=\"pre\">vote()</span></code> remite a la página de resultados de la pregunta. Vamos a escribir dicha vista:</p>\n<figure class=\"code-block code-block-captioned\" data-language=\"python\"><figcaption class=\"code-block-caption\"><code class=\"docutils literal notranslate\"><span class=\"pre\">polls/views.py</span></code></figcaption>\n<div class=\"code-block-toolbar\"><span class=\"code-block-language\">Python</span><button type=\"button\" class=\"copy-button\" data-copy hidden><span class=\"copy-button-label\">Copy</span></button></div><pre role=\"group\" tabindex=\"0\" aria-label=\"Python code\"><code><span class=\"kn\">from</span><span class=\"w\"> </span><span class=\"nn\">django.shortcuts</span><span class=\"w\"> </span><span class=\"kn\">import</span> <span class=\"n\">get_object_or_404</span><span class=\"p\">,</span> <span class=\"n\">render</span>\n\n\n<span class=\"k\">def</span><span class=\"w\"> </span><span class=\"nf\">results</span><span class=\"p\">(</span><span class=\"n\">request</span><span class=\"p\">,</span> <span class=\"n\">question_id</span><span class=\"p\">):</span>\n    <span class=\"n\">question</span> <span class=\"o\">=</span> <span class=\"n\">get_object_or_404</span><span class=\"p\">(</span><span class=\"n\">Question</span><span class=\"p\">,</span> <span class=\"n\">pk</span><span class=\"o\">=</span><span class=\"n\">question_id</span><span class=\"p\">)</span>\n    <span class=\"k\">return</span> <span class=\"n\">render</span><span class=\"p\">(</span><span class=\"n\">request</span><span class=\"p\">,</span> <span class=\"s1\">&#39;polls/results.html&#39;</span><span class=\"p\">,</span> <span class=\"p\">{</span><span class=\"s1\">&#39;question&#39;</span><span class=\"p\">:</span> <span class=\"n\">question</span><span class=\"p\">})</span>\n</code></pre></figure>\n<p>Esto es casi exactamente lo mismo que la vista <code class=\"docutils literal notranslate\"><span class=\"pre\">detail()</span></code> del <a class=\"reference internal\" href=\"/es/4.1/intro/tutorial03/\"><span class=\"doc\">Tutorial 3</span></a>. La única diferencia es el nombre de la plantilla. Solucionaremos esta redundancia más tarde.</p>\n<p>Ahora, cree una plantilla <code class=\"docutils literal notranslate\"><span class=\"pre\">polls/results.html</span></code>:</p>\n<figure class=\"code-block code-block-captioned\" data-language=\"html+django\"><figcaption class=\"code-block-caption\"><code class=\"docutils literal notranslate\"><span class=\"pre\">polls/templates/polls/results.html</span></code></figcaption>\n<div class=\"code-block-toolbar\"><span class=\"code-block-language\">Django template</span><button type=\"button\" class=\"copy-button\" data-copy hidden><span class=\"copy-button-label\">Copy</span></button></div><pre role=\"group\" tabindex=\"0\" aria-label=\"Django template code\"><code><span class=\"p\">&lt;</span><span class=\"nt\">h1</span><span class=\"p\">&gt;</span><span class=\"cp\">{{</span> <span class=\"nv\">question.question_text</span> <span class=\"cp\">}}</span><span class=\"p\">&lt;/</span><span class=\"nt\">h1</span><span class=\"p\">&gt;</span>\n\n<span class=\"p\">&lt;</span><span class=\"nt\">ul</span><span class=\"p\">&gt;</span>\n<span class=\"cp\">{%</span> <span class=\"k\">for</span> <span class=\"nv\">choice</span> <span class=\"k\">in</span> <span class=\"nv\">question.choice_set.all</span> <span class=\"cp\">%}</span>\n    <span class=\"p\">&lt;</span><span class=\"nt\">li</span><span class=\"p\">&gt;</span><span class=\"cp\">{{</span> <span class=\"nv\">choice.choice_text</span> <span class=\"cp\">}}</span> -- <span class=\"cp\">{{</span> <span class=\"nv\">choice.votes</span> <span class=\"cp\">}}</span> vote<span class=\"cp\">{{</span> <span class=\"nv\">choice.votes</span><span class=\"o\">|</span><span class=\"nf\">pluralize</span> <span class=\"cp\">}}</span><span class=\"p\">&lt;/</span><span class=\"nt\">li</span><span class=\"p\">&gt;</span>\n<span class=\"cp\">{%</span> <span class=\"k\">endfor</span> <span class=\"cp\">%}</span>\n<span class=\"p\">&lt;/</span><span class=\"nt\">ul</span><span class=\"p\">&gt;</span>\n\n<span class=\"p\">&lt;</span><span class=\"nt\">a</span> <span class=\"na\">href</span><span class=\"o\">=</span><span class=\"s\">&quot;</span><span class=\"cp\">{%</span> <span class=\"k\">url</span> <span class=\"s1\">&#39;polls:detail&#39;</span> <span class=\"nv\">question.id</span> <span class=\"cp\">%}</span><span class=\"s\">&quot;</span><span class=\"p\">&gt;</span>Vote again?<span class=\"p\">&lt;/</span><span class=\"nt\">a</span><span class=\"p\">&gt;</span>\n</code></pre></figure>\n<p>A continuación, vaya a <code class=\"docutils literal notranslate\"><span class=\"pre\">/polls/1/</span></code> en su navegador y vote en la pregunta. Debería ver una página de resultados que se actualiza cada vez que usted vota. Si usted envía el formulario sin haber seleccionado una opción, usted debería ver el mensaje de error.</p>\n<aside class=\"admonition admonition-note\" role=\"note\">\n<p class=\"admonition-title\">Nota</p>\n<p>El código para nuestra vista <code class=\"docutils literal notranslate\"><span class=\"pre\">vote()</span></code> tiene un pequeño problema. Primero, obtiene el objeto <code class=\"docutils literal notranslate\"><span class=\"pre\">selected_choice</span></code> de la base de datos, luego, calcula el nuevo valor de <code class=\"docutils literal notranslate\"><span class=\"pre\">votes</span></code>, y después lo guarda de nuevo en la base de datos. Si dos usuarios de su sitio Web intentan votar <em>exactamente en el mismo momento</em>, esto podría salir mal: El mismo valor, digamos 42, será recuperado para <code class=\"docutils literal notranslate\"><span class=\"pre\">votes</span></code>. Entonces, se calcula y se guarda el nuevo valor de 43 para ambos usuarios, pero el 44 sería el valor esperado.</p>\n<p>Esto se conoce como una <em>condición de carrera</em>. Si usted está interesado, puede leer :ref: avoiding-race-conditions-using-f para aprender cómo solucionar este problema.</p>\n</aside>\n</section>\n<section id=\"use-generic-views-less-code-is-better\">\n<h2>Utilice vistas genéricas: menos código es mejor<a class=\"heading-anchor\" href=\"#use-generic-views-less-code-is-better\"><span class=\"visually-hidden\">Link to this heading</span><span aria-hidden=\"true\">#</span></a></h2>\n<p>The <code class=\"docutils literal notranslate\"><span class=\"pre\">detail()</span></code> (from <a class=\"reference internal\" href=\"/es/4.1/intro/tutorial03/\"><span class=\"doc\">Tutorial 3</span></a>) and <code class=\"docutils literal notranslate\"><span class=\"pre\">results()</span></code>\nviews are very short – and, as mentioned above, redundant. The <code class=\"docutils literal notranslate\"><span class=\"pre\">index()</span></code>\nview, which displays a list of polls, is similar.</p>\n<p>These views represent a common case of basic web development: getting data from\nthe database according to a parameter passed in the URL, loading a template and\nreturning the rendered template. Because this is so common, Django provides a\nshortcut, called the «generic views» system.</p>\n<p>Las vistas genéricas abstraen los patrones comunes hasta el punto en que ni siquiera es necesario escribir código Python para escribir una aplicación.</p>\n<p>Let’s convert our poll app to use the generic views system, so we can delete a\nbunch of our own code. We’ll have to take a few steps to make the conversion.\nWe will:</p>\n<ol class=\"arabic simple\">\n<li><p>Convertir el URLconf.</p></li>\n<li><p>Elimina algunas de las vistas viejas e innecesarias.</p></li>\n<li><p>Introduce nuevas vistas basadas en las vistas genéricas de Django.</p></li>\n</ol>\n<p>Siga leyendo para conocer más detalles.</p>\n<aside class=\"admonition-why-the-code-shuffle admonition\">\n<p class=\"admonition-title\">¿Por qué el intercambio de código?</p>\n<p>Generalmente, cuando se escribe una aplicación de Django, usted podrá evaluar si las vistas genéricas son una buena opción para su problema y ​​las utilizará desde el principio en lugar de reestructurar su código a medio camino. Sin embargo, este tutorial intencionadamente se ha centrado hasta ahora en escribir las vistas «de la manera difícil» para centrarse en los conceptos esenciales.</p>\n<p>Debe saber matemáticas básicas antes de comenzar a utilizar una calculadora.</p>\n</aside>\n<section id=\"amend-urlconf\">\n<h3>Modifique el URLconf<a class=\"heading-anchor\" href=\"#amend-urlconf\"><span class=\"visually-hidden\">Link to this heading</span><span aria-hidden=\"true\">#</span></a></h3>\n<p>Primero, abra el URLconf <code class=\"docutils literal notranslate\"><span class=\"pre\">polls/urls.py</span></code> y modifíquelo de la siguiente manera:</p>\n<figure class=\"code-block code-block-captioned\" data-language=\"python\"><figcaption class=\"code-block-caption\"><code class=\"docutils literal notranslate\"><span class=\"pre\">polls/urls.py</span></code></figcaption>\n<div class=\"code-block-toolbar\"><span class=\"code-block-language\">Python</span><button type=\"button\" class=\"copy-button\" data-copy hidden><span class=\"copy-button-label\">Copy</span></button></div><pre role=\"group\" tabindex=\"0\" aria-label=\"Python code\"><code><span class=\"kn\">from</span><span class=\"w\"> </span><span class=\"nn\">django.urls</span><span class=\"w\"> </span><span class=\"kn\">import</span> <span class=\"n\">path</span>\n\n<span class=\"kn\">from</span><span class=\"w\"> </span><span class=\"nn\">.</span><span class=\"w\"> </span><span class=\"kn\">import</span> <span class=\"n\">views</span>\n\n<span class=\"n\">app_name</span> <span class=\"o\">=</span> <span class=\"s1\">&#39;polls&#39;</span>\n<span class=\"n\">urlpatterns</span> <span class=\"o\">=</span> <span class=\"p\">[</span>\n    <span class=\"n\">path</span><span class=\"p\">(</span><span class=\"s1\">&#39;&#39;</span><span class=\"p\">,</span> <span class=\"n\">views</span><span class=\"o\">.</span><span class=\"n\">IndexView</span><span class=\"o\">.</span><span class=\"n\">as_view</span><span class=\"p\">(),</span> <span class=\"n\">name</span><span class=\"o\">=</span><span class=\"s1\">&#39;index&#39;</span><span class=\"p\">),</span>\n    <span class=\"n\">path</span><span class=\"p\">(</span><span class=\"s1\">&#39;&lt;int:pk&gt;/&#39;</span><span class=\"p\">,</span> <span class=\"n\">views</span><span class=\"o\">.</span><span class=\"n\">DetailView</span><span class=\"o\">.</span><span class=\"n\">as_view</span><span class=\"p\">(),</span> <span class=\"n\">name</span><span class=\"o\">=</span><span class=\"s1\">&#39;detail&#39;</span><span class=\"p\">),</span>\n    <span class=\"n\">path</span><span class=\"p\">(</span><span class=\"s1\">&#39;&lt;int:pk&gt;/results/&#39;</span><span class=\"p\">,</span> <span class=\"n\">views</span><span class=\"o\">.</span><span class=\"n\">ResultsView</span><span class=\"o\">.</span><span class=\"n\">as_view</span><span class=\"p\">(),</span> <span class=\"n\">name</span><span class=\"o\">=</span><span class=\"s1\">&#39;results&#39;</span><span class=\"p\">),</span>\n    <span class=\"n\">path</span><span class=\"p\">(</span><span class=\"s1\">&#39;&lt;int:question_id&gt;/vote/&#39;</span><span class=\"p\">,</span> <span class=\"n\">views</span><span class=\"o\">.</span><span class=\"n\">vote</span><span class=\"p\">,</span> <span class=\"n\">name</span><span class=\"o\">=</span><span class=\"s1\">&#39;vote&#39;</span><span class=\"p\">),</span>\n<span class=\"p\">]</span>\n</code></pre></figure>\n<p>Tenga en cuenta que el nombre del patrón coincidente en las cadenas de ruta de los patrones segundo y tercero ha cambiado de <code class=\"docutils literal notranslate\"><span class=\"pre\">&lt;question_id&gt;</span></code> a <code class=\"docutils literal notranslate\"><span class=\"pre\">&lt;pk&gt;</span></code>.</p>\n</section>\n<section id=\"amend-views\">\n<h3>Modifique las vistas<a class=\"heading-anchor\" href=\"#amend-views\"><span class=\"visually-hidden\">Link to this heading</span><span aria-hidden=\"true\">#</span></a></h3>\n<p>A continuación, vamos a eliminar nuestras viejas vistas <code class=\"docutils literal notranslate\"><span class=\"pre\">index</span></code>, <code class=\"docutils literal notranslate\"><span class=\"pre\">detail</span></code> y <code class=\"docutils literal notranslate\"><span class=\"pre\">results</span></code> y en su lugar vamos a usar las vistas genéricas de Django. Para ello, abra el archivo <code class=\"docutils literal notranslate\"><span class=\"pre\">polls/views.py</span></code> y modifíquelo de la siguiente manera:</p>\n<figure class=\"code-block code-block-captioned\" data-language=\"python\"><figcaption class=\"code-block-caption\"><code class=\"docutils literal notranslate\"><span class=\"pre\">polls/views.py</span></code></figcaption>\n<div class=\"code-block-toolbar\"><span class=\"code-block-language\">Python</span><button type=\"button\" class=\"copy-button\" data-copy hidden><span class=\"copy-button-label\">Copy</span></button></div><pre role=\"group\" tabindex=\"0\" aria-label=\"Python code\"><code><span class=\"kn\">from</span><span class=\"w\"> </span><span class=\"nn\">django.http</span><span class=\"w\"> </span><span class=\"kn\">import</span> <span class=\"n\">HttpResponseRedirect</span>\n<span class=\"kn\">from</span><span class=\"w\"> </span><span class=\"nn\">django.shortcuts</span><span class=\"w\"> </span><span class=\"kn\">import</span> <span class=\"n\">get_object_or_404</span><span class=\"p\">,</span> <span class=\"n\">render</span>\n<span class=\"kn\">from</span><span class=\"w\"> </span><span class=\"nn\">django.urls</span><span class=\"w\"> </span><span class=\"kn\">import</span> <span class=\"n\">reverse</span>\n<span class=\"kn\">from</span><span class=\"w\"> </span><span class=\"nn\">django.views</span><span class=\"w\"> </span><span class=\"kn\">import</span> <span class=\"n\">generic</span>\n\n<span class=\"kn\">from</span><span class=\"w\"> </span><span class=\"nn\">.models</span><span class=\"w\"> </span><span class=\"kn\">import</span> <span class=\"n\">Choice</span><span class=\"p\">,</span> <span class=\"n\">Question</span>\n\n\n<span class=\"k\">class</span><span class=\"w\"> </span><span class=\"nc\">IndexView</span><span class=\"p\">(</span><span class=\"n\">generic</span><span class=\"o\">.</span><span class=\"n\">ListView</span><span class=\"p\">):</span>\n    <span class=\"n\">template_name</span> <span class=\"o\">=</span> <span class=\"s1\">&#39;polls/index.html&#39;</span>\n    <span class=\"n\">context_object_name</span> <span class=\"o\">=</span> <span class=\"s1\">&#39;latest_question_list&#39;</span>\n\n    <span class=\"k\">def</span><span class=\"w\"> </span><span class=\"nf\">get_queryset</span><span class=\"p\">(</span><span class=\"bp\">self</span><span class=\"p\">):</span>\n<span class=\"w\">        </span><span class=\"sd\">&quot;&quot;&quot;Return the last five published questions.&quot;&quot;&quot;</span>\n        <span class=\"k\">return</span> <span class=\"n\">Question</span><span class=\"o\">.</span><span class=\"n\">objects</span><span class=\"o\">.</span><span class=\"n\">order_by</span><span class=\"p\">(</span><span class=\"s1\">&#39;-pub_date&#39;</span><span class=\"p\">)[:</span><span class=\"mi\">5</span><span class=\"p\">]</span>\n\n\n<span class=\"k\">class</span><span class=\"w\"> </span><span class=\"nc\">DetailView</span><span class=\"p\">(</span><span class=\"n\">generic</span><span class=\"o\">.</span><span class=\"n\">DetailView</span><span class=\"p\">):</span>\n    <span class=\"n\">model</span> <span class=\"o\">=</span> <span class=\"n\">Question</span>\n    <span class=\"n\">template_name</span> <span class=\"o\">=</span> <span class=\"s1\">&#39;polls/detail.html&#39;</span>\n\n\n<span class=\"k\">class</span><span class=\"w\"> </span><span class=\"nc\">ResultsView</span><span class=\"p\">(</span><span class=\"n\">generic</span><span class=\"o\">.</span><span class=\"n\">DetailView</span><span class=\"p\">):</span>\n    <span class=\"n\">model</span> <span class=\"o\">=</span> <span class=\"n\">Question</span>\n    <span class=\"n\">template_name</span> <span class=\"o\">=</span> <span class=\"s1\">&#39;polls/results.html&#39;</span>\n\n\n<span class=\"k\">def</span><span class=\"w\"> </span><span class=\"nf\">vote</span><span class=\"p\">(</span><span class=\"n\">request</span><span class=\"p\">,</span> <span class=\"n\">question_id</span><span class=\"p\">):</span>\n    <span class=\"o\">...</span> <span class=\"c1\"># same as above, no changes needed.</span>\n</code></pre></figure>\n<p>Estamos utilizando dos vistas genéricas aquí: <a class=\"reference internal\" href=\"/es/4.1/ref/class-based-views/generic-display/#django.views.generic.list.ListView\" title=\"django.views.generic.list.ListView\"><code class=\"xref py py-class docutils literal notranslate\"><span class=\"pre\">ListView</span></code></a> y <a class=\"reference internal\" href=\"/es/4.1/ref/class-based-views/generic-display/#django.views.generic.detail.DetailView\" title=\"django.views.generic.detail.DetailView\"><code class=\"xref py py-class docutils literal notranslate\"><span class=\"pre\">DetailView</span></code></a>. Respectivamente, esas dos vistas abstraen los conceptos de «mostrar una lista de objetos» y «mostrar una página de detalles para un tipo específico de objeto.»</p>\n<ul class=\"simple\">\n<li><p>Cada vista genérica tiene que saber cuál es el modelo sobre el que estará actuando. Esto se proporciona utilizando el atributo <code class=\"docutils literal notranslate\"><span class=\"pre\">model</span></code>.</p></li>\n<li><p>La vista genérica <a class=\"reference internal\" href=\"/es/4.1/ref/class-based-views/generic-display/#django.views.generic.detail.DetailView\" title=\"django.views.generic.detail.DetailView\"><code class=\"xref py py-class docutils literal notranslate\"><span class=\"pre\">DetailView</span></code></a> espera que el valor de la clave primaria capturado desde la URL sea denominado <code class=\"docutils literal notranslate\"><span class=\"pre\">&quot;pk&quot;</span></code>, por lo que hemos cambiado``question_id`` a <code class=\"docutils literal notranslate\"><span class=\"pre\">pk</span></code> para las vistas genéricas.</p></li>\n</ul>\n<p>Por defecto, la vista genérica <a class=\"reference internal\" href=\"/es/4.1/ref/class-based-views/generic-display/#django.views.generic.detail.DetailView\" title=\"django.views.generic.detail.DetailView\"><code class=\"xref py py-class docutils literal notranslate\"><span class=\"pre\">DetailView</span></code></a> utiliza una plantilla llamada <code class=\"docutils literal notranslate\"><span class=\"pre\">&lt;app</span> <span class=\"pre\">name&gt;/&lt;model</span> <span class=\"pre\">name&gt;_detail.html</span></code>. En nuestro caso, utilizaría la plantilla <code class=\"docutils literal notranslate\"><span class=\"pre\">&quot;polls/question_detail.html&quot;</span></code>. El atributo <code class=\"docutils literal notranslate\"><span class=\"pre\">template_name</span></code> se utiliza para indicarle a Django que utilice un nombre de plantilla específico en vez del nombre de plantilla generado de forma automática. También especificamos el atributo <code class=\"docutils literal notranslate\"><span class=\"pre\">template_name</span></code> para la vista de lista <code class=\"docutils literal notranslate\"><span class=\"pre\">results</span></code>, esto garantiza que la vista de resultados y la vista detalle tengan un aspecto diferente cuando sean creadas, a pesar de que las dos son una vista genérica <code class=\"xref py py-class docutils literal notranslate\"><span class=\"pre\">DetailView</span></code> en segundo plano.</p>\n<p>Del mismo modo, la vista genérica <a class=\"reference internal\" href=\"/es/4.1/ref/class-based-views/generic-display/#django.views.generic.list.ListView\" title=\"django.views.generic.list.ListView\"><code class=\"xref py py-class docutils literal notranslate\"><span class=\"pre\">ListView</span></code></a> utiliza una plantilla predeterminada llamada <code class=\"docutils literal notranslate\"><span class=\"pre\">&lt;app</span> <span class=\"pre\">name&gt;/&lt;model</span> <span class=\"pre\">name&gt;_list.html</span></code>; utilizamos el atributo <code class=\"docutils literal notranslate\"><span class=\"pre\">template_name</span></code> para indicarle a <a class=\"reference internal\" href=\"/es/4.1/ref/class-based-views/generic-display/#django.views.generic.list.ListView\" title=\"django.views.generic.list.ListView\"><code class=\"xref py py-class docutils literal notranslate\"><span class=\"pre\">ListView</span></code></a> que utilice nuestra plantilla <code class=\"docutils literal notranslate\"><span class=\"pre\">&quot;polls/index.html&quot;</span></code> existente.</p>\n<p>In previous parts of the tutorial, the templates have been provided\nwith a context that contains the <code class=\"docutils literal notranslate\"><span class=\"pre\">question</span></code> and <code class=\"docutils literal notranslate\"><span class=\"pre\">latest_question_list</span></code>\ncontext variables. For <code class=\"docutils literal notranslate\"><span class=\"pre\">DetailView</span></code> the <code class=\"docutils literal notranslate\"><span class=\"pre\">question</span></code> variable is provided\nautomatically – since we’re using a Django model (<code class=\"docutils literal notranslate\"><span class=\"pre\">Question</span></code>), Django\nis able to determine an appropriate name for the context variable.\nHowever, for ListView, the automatically generated context variable is\n<code class=\"docutils literal notranslate\"><span class=\"pre\">question_list</span></code>. To override this we provide the <code class=\"docutils literal notranslate\"><span class=\"pre\">context_object_name</span></code>\nattribute, specifying that we want to use <code class=\"docutils literal notranslate\"><span class=\"pre\">latest_question_list</span></code> instead.\nAs an alternative approach, you could change your templates to match\nthe new default context variables – but it’s a lot easier to tell Django to\nuse the variable you want.</p>\n<p>Ejecute el servidor y utilice su nueva aplicación encuesta basada en las vistas genéricas.</p>\n<p>Para más detalles sobre las vistas genéricas, consulte la <a class=\"reference internal\" href=\"/es/4.1/topics/class-based-views/\"><span class=\"doc\">documentación de las vistas genéricas</span></a>.</p>\n<p>Cuando esté familiarizado con los formularios y las vistas genéricas, lea la <a class=\"reference internal\" href=\"/es/4.1/intro/tutorial05/\"><span class=\"doc\">parte 5 de este tutorial</span></a> para aprender sobre cómo probar nuestra aplicación encuestas.</p>\n</section>\n</section>","rootId":"writing-your-first-django-app-part-4","toc":[{"title":"Write a minimal form","anchor":"write-a-minimal-form","children":[]},{"title":"Utilice vistas genéricas: menos código es mejor","anchor":"use-generic-views-less-code-is-better","children":[{"title":"Modifique el URLconf","anchor":"amend-urlconf","children":[]},{"title":"Modifique las vistas","anchor":"amend-views","children":[]}]}],"breadcrumbs":[{"docname":"intro/index","title":"Empezando","url":"/es/4.1/intro/"}],"prev":{"docname":"intro/tutorial03","title":"Escribiendo su primera aplicación en Django, parte 3","url":"/es/4.1/intro/tutorial03/"},"next":{"docname":"intro/tutorial05","title":"Escribiendo su primera aplicación en Django, parte 5","url":"/es/4.1/intro/tutorial05/"},"formats":{"html":"/es/4.1/intro/tutorial04/","markdown":"/es/4.1/intro/tutorial04.md","json":"/es/4.1/intro/tutorial04.json"},"source":"https://github.com/django/django/blob/stable/4.1.x/docs/intro/tutorial04.txt","official":"https://docs.djangoproject.com/es/4.1/intro/tutorial04/","inVersions":["6.1","6.0","5.2","5.1","5.0","4.2","4.1","4.0","3.2","3.1","3.0","2.2","2.1","2.0","1.11","1.10","1.9"],"inLocales":["en","zh-hans","fr","ja","id","it","pt-br","ko","es","el","pl"]}