aboutsummaryrefslogtreecommitdiff
path: root/files/es/learn/server-side/django/generic_views/index.html
diff options
context:
space:
mode:
Diffstat (limited to 'files/es/learn/server-side/django/generic_views/index.html')
-rw-r--r--files/es/learn/server-side/django/generic_views/index.html640
1 files changed, 640 insertions, 0 deletions
diff --git a/files/es/learn/server-side/django/generic_views/index.html b/files/es/learn/server-side/django/generic_views/index.html
new file mode 100644
index 0000000000..7eb6830a87
--- /dev/null
+++ b/files/es/learn/server-side/django/generic_views/index.html
@@ -0,0 +1,640 @@
+---
+title: 'Tutorial de Django Parte 6: Listas genéricas y vistas de detalle'
+slug: Learn/Server-side/Django/Generic_views
+tags:
+ - Aprender
+ - Principiante
+ - Tutorial
+ - django
+ - plantillas django
+ - vistas django
+translation_of: Learn/Server-side/Django/Generic_views
+---
+<div>{{LearnSidebar}}</div>
+
+<div>{{PreviousMenuNext("Learn/Server-side/Django/Home_page", "Learn/Server-side/Django/Sessions", "Learn/Server-side/Django")}}</div>
+
+<p class="summary">Este tutorial extiende nuestro sitio web de la <a href="https://developer.mozilla.org/en-US/docs/Learn/Server-side/Django/Tutorial_local_library_website" style="font-size: 1.25rem;">BibliotecaLocal</a><span style="font-size: 1.25rem;">, añadiendo páginas de listas y detalles de libros y autores. Aquí aprenderemos sobre vistas genéricas basadas en clases, y mostraremos cómo éstas pueden reducir la cantidad de código que tienes que escribir para casos de uso común. También entraremos en el manejo de URL en gran detalle, mostrando cómo realizar un emparejamiento de patrones básico</span><span style="font-size: 1.25rem;">.</span></p>
+
+<table class="learn-box standard-table">
+ <tbody>
+ <tr>
+ <th scope="row">Requisitos previos:</th>
+ <td>
+ <p>Completa todos los tópicos anteriores del tutorial, incluyendo <a href="https://developer.mozilla.org/es/docs/Learn/Server-side/Django/Home_page">Tutorial de Django Parte 5: Creación de tu página de inicio</a>.</p>
+ </td>
+ </tr>
+ <tr>
+ <th scope="row">Objetivo:</th>
+ <td>
+ <p>Entender dónde y cómo usar las vistas genéricas basadas en clases, y cómo extraer patrones desde las URLs y enviar la información a las vistas.</p>
+ </td>
+ </tr>
+ </tbody>
+</table>
+
+<h2 id="Visión_General">Visión General</h2>
+
+<p>En este tutorial vamos a completar la primera versión del sitio web <a href="https://developer.mozilla.org/es/docs/Learn/Server-side/Django/Tutorial_local_library_website">BibliotecaLocal</a> añadiendo páginas de lista y detalle para libros y autores (o para ser más precisos, te mostraremos cómo implementar las páginas de libros, ¡y te dejaremos crear las páginas de autores por tí mismo!)</p>
+
+<p>El proceso es similar al de creación de la página índice, que mostramos en el tutorial anterior. Aquí también necesitaremos crear mapeos URL, vistas y plantillas. La principal diferencia es que para las páginas de detalle tendremos el reto adicional de extraer información desde patrones en las URLs y pasarla a la vista. Para estas páginas vamos a mostrar un tipo de vista completamente diferente: vistas genéricas de lista y detalle basadas en clases. Estas pueden reducir significativamente la cantidad de código requerido para las vistas, haciéndolas más fáciles de escribir y mantener.</p>
+
+<p>La parte final del tutorial mostrará cómo paginar tu información al usar vistas de lista genéricas basadas en clases.</p>
+
+<h2 id="Página_de_lista_de_libros">Página de lista de libros</h2>
+
+<p>La página de lista de libros desplegará una lista con todos los registros de libros disponibles en la página, a la cual se accede usando la URL:  <code>catalog/books/</code>. La página desplegará un título y un autor para cada registro, siendo el título un hipervículo a la página de detalle de libro relacionada. La página tendrá la misma estructura y navegación que todas las demás páginas en el sitio, y por tanto podemos extender la plantilla base (<strong>base_generic.html</strong>) que creamos en el tutorial anterior.</p>
+
+<h3 id="Mapeo_URL">Mapeo URL</h3>
+
+<p>Abre <strong>/catalog/urls.py</strong> y copia allí la línea que se muestra abajo en negrita. De manera muy similar al mapeo de nuestro índice, esta función <code>url()</code> define una expresión regular (RE) para comparala con la URL (<strong>r'^books/$'</strong>), una función de vista que será llamada si la URL coincide (<code>views.BookListView.as_view()</code>) y un nombre para este mapeo en particular.</p>
+
+<pre class="brush: python">urlpatterns = [
+ url(r'^$', views.index, name='index'),
+<strong>  url(r'^books/$', views.BookListView.as_view(), name='books'),</strong>
+]</pre>
+
+<p>Esta expresión regular coincide con URLs iguales a <code>books/</code> (<code>^</code> es un marcador de inicio de cadena y <code>$</code> es un marcador de fin de cadena). Como se discutió en el tutorial anterior, la URL debió previamente haber coincidido con <code>/catalog</code>, de modo que la vista será llamada en realidad para la URL: <code>/catalog/books/</code>.</p>
+
+<p>La función de vista tiene un formato diferente al anterior -- eso es porque esta vista será en realidad implementada como una clase. Heredaremos desde una función de vista genérica existente que ya hace la mayoría de lo que queremos que esta función de vista haga, en lugar de escribir la nuestra propia desde el inicio.</p>
+
+<p>Para las vistas basadas en clases de Django, accedemos a una función de vista apropiada llamando al método de clase <code>as_view()</code>. Esto hace todo el trabajo de crear una instancia de la clase, y asegurarse de que los métodos controladores correctos sean llamados para las solicitudes HTTP entrantes.</p>
+
+<h3 id="Vista_(basada_en_clases)">Vista (basada en clases)</h3>
+
+<p>Podríamos fácilmente escribir la vista de lista de libros como una función regular (tal como nuestra vista de índice anterior), la cual consultaría a la base de datos por todos los libros, y luego llamar a <code>render()</code> para pasar dicha lista a una plantilla específica. Sin embargo, en lugar de eso usaremos una vista de lista genérica basada en clases (ListView) -- una clase que hereda desde una vista existente. Debido a que la vista genérica ya implementa la mayoría de la funcionalidad que necesitamos, y sigue la práctica adecuada de Django, seremos capaces de crear una vista de lista más robusta con menos código, menos repetición, y por último menos mantenimiento.</p>
+
+<p>Abre <strong>catalog/views.py</strong>, y copia el siguiente código al final del archivo:</p>
+
+<pre class="brush: python">from django.views import generic
+
+class BookListView(generic.ListView):
+ model = Book</pre>
+
+<p>¡Eso es todo! La vista genérica consultará a la base de datos para obtener todos los registros del modelo especificado (<code>Book</code>) y renderizará una plantilla ubicada en <strong>/locallibrary/catalog/templates/catalog/book_list.html</strong> (que crearemos más abajo). Dentro de la plantilla puedes acceder a la lista de libros mediante la variable de plantilla llamada <code>object_list</code> O <code>book_list</code> (esto es, genéricamente, "<code><em>nombre_del_modelo</em>_list</code>").</p>
+
+<div class="note">
+<p><strong>Nota</strong>: Esta ruta complicada para la ubicación de la plantilla no es un error de digitación -- las vistas genéricas buscan plantillas en <code><em>/application_name/the_model_name</em>_list.html</code> (<code>catalog/book_list.html</code> en este caso) dentro del directorio de la aplicación <code>/<em>application_name</em>/templates/</code> (<code>/catalog/templates/</code>).</p>
+</div>
+
+<p>Puedes añadir atributos para cambiar el comportamiento por defecto de arriba. Por ejemplo, puedes especificar otro archivo de plantilla si necesitas tener múltiples vistas que usen el mismo modelo, o puedes querer usar un nombre diferente de variable de plantilla si <code>book_list</code> no resulta intuitivo para tu caso particular de uso de plantilla. Posiblemente la variación más útil es cambiar/filtrar el conjunto de resultados que se devuelve, así, en lugar de listar todos los libros podrías listar los 5 libros más leídos por otros usuarios.</p>
+
+<pre class="brush: python">class BookListView(generic.ListView):
+ model = Book
+ context_object_name = 'my_book_list' # your own name for the list as a template variable
+ queryset = Book.objects.filter(title__icontains='war')[:5] # Get 5 books containing the title war
+ template_name = 'books/my_arbitrary_template_name_list.html' # Specify your own template name/location</pre>
+
+<h4 id="Sobreescribiendo_métodos_en_vistas_basadas_en_clases">Sobreescribiendo métodos en vistas basadas en clases</h4>
+
+<p>Si bien no necesitamos hacerlo aquí, puedes también sobreescribir algunos de los métodos de clase.</p>
+
+<p>Por ejemplo, podemos sobreescribir el método <code>get_queryset()</code> para cambiar la lista de registros devueltos. Esto es más flexible que simplemente ajustar el atributo <code>queryset</code> como lo hicimos en el fragmento de código anterior (aunque en este caso no existe un beneficio real):</p>
+
+<p> </p>
+
+<pre class="brush: python">class BookListView(generic.ListView):
+ model = Book
+
+    def get_queryset(self):
+        return Book.objects.filter(title__icontains='war')[:5] # Get 5 books containing the title war
+</pre>
+
+<p> </p>
+
+<p>Podríamos también sobreescribir <code>get_context_data()</code> con el objeto de pasar variables de contexto adicionales a la plantilla (ej. la lista de libros se pasa por defecto). El fragmento de abajo muestra cómo añadir una variable llamada "some_data" al contexto (la misma estaría entonces disponible como una variable de plantilla).</p>
+
+<pre class="brush: python">class BookListView(generic.ListView):
+ model = Book
+
+    def get_context_data(self, **kwargs):
+        # Call the base implementation first to get a context
+        context = super(BookListView, self).get_context_data(**kwargs)
+        # Get the blog from id and add it to the context
+        context['some_data'] = 'This is just some data'
+        return context</pre>
+
+<p>Cuando se hace esto es importante seguir este patrón:</p>
+
+<ul>
+ <li>Primero obtener el contexto existente desde nuestra superclase.</li>
+ <li>Luego añadir tu nueva información de contexto.</li>
+ <li>Luego devolver el nuevo contexto (actualizado).</li>
+</ul>
+
+<div class="note">
+<p><strong>Nota</strong>: Revisa <a href="https://docs.djangoproject.com/en/1.10/topics/class-based-views/generic-display/">Built-in class-based generic views</a> (Django docs) para muchos más ejemplos de lo que puedes hacer.</p>
+</div>
+
+<h3 id="Creando_la_plantilla_de_Vista_de_Lista">Creando la plantilla de Vista de Lista</h3>
+
+<p>Crea el archivo HTML <strong>/locallibrary/catalog/templates/catalog/book_list.html</strong> y copia allí el texto de abajo. Como se discutió antes, este es el archivo de plantilla por defecto esperado por la vista de lista genérica basada en clases (para un modelo llamado <code>Book</code> en una aplicación llamada <code>catalog</code>).</p>
+
+<p>Las plantillas para las vistas genéricas son como cualquier otra plantilla (si bien el contexto/información enviada a la plantilla puede variar, por supuesto). Como con nuestra plantilla <em>índice</em>, extendemos nuestra plantilla base en la primera línea, y luego reemplazamos el bloque llamado <code>content</code>.</p>
+
+<pre class="brush: html">{% extends "base_generic.html" %}
+
+{% block content %}
+    &lt;h1&gt;Book List&lt;/h1&gt;
+
+    <strong>{% if book_list %}</strong>
+    &lt;ul&gt;
+
+      {% for book in book_list %}
+      &lt;li&gt;
+        &lt;a href="\{{ book.get_absolute_url }}"&gt;\{{ book.title }}&lt;/a&gt; (\{{book.author}})
+      &lt;/li&gt;
+      {% endfor %}
+
+    &lt;/ul&gt;
+    <strong>{% else %}</strong>
+      &lt;p&gt;There are no books in the library.&lt;/p&gt;
+    <strong>{% endif %} </strong>
+{% endblock %}</pre>
+
+<p>La vista envía el contexto (lista de libros) por defecto como <code>object_list</code> y <code>book_list</code> (son áliases, cualquiera de ellos funcionará).</p>
+
+<h4 id="Ejecución_condicional">Ejecución condicional</h4>
+
+<p>Usamos las etiquetas de plantilla <code><a href="https://docs.djangoproject.com/en/1.10/ref/templates/builtins/#if">if</a></code>, <code>else</code> y <code>endif</code> para revisar si la <code>book_list</code> ha sido definida y no está vacía. Si <code>book_list</code> está vacía, entonces la cláusula <code>else</code> despliega un texto que explica que no existen libros a listar. Si <code>Book_list</code> no está vacía, entonces iteramos a través de la lista de libros.</p>
+
+<pre class="brush: html"><strong>{% if book_list %}</strong>
+ &lt;!-- code here to list the books --&gt;
+<strong>{% else %}</strong>
+ &lt;p&gt;There are no books in the library.&lt;/p&gt;
+<strong>{% endif %}</strong>
+</pre>
+
+<p>La condicional de arriba solo revisa un caso, pero puedes revisar condiciones adicionales usando la etiqueta de plantilla <code>elif</code> (ej. <code>{% elif var2 %}</code>). Para mayor información sobre operadores condicionales mira: <a href="https://docs.djangoproject.com/en/1.10/ref/templates/builtins/#if">if</a>, <a href="https://docs.djangoproject.com/en/1.10/ref/templates/builtins/#ifequal-and-ifnotequal">ifequal/ifnotequal</a>, y <a href="https://docs.djangoproject.com/en/1.10/ref/templates/builtins/#ifchanged">ifchanged</a> en <a href="https://docs.djangoproject.com/en/1.10/ref/templates/builtins">Built-in template tags and filters</a> (Django Docs).</p>
+
+<h4 id="Lazos_For">Lazos For</h4>
+
+<p>La plantilla usa las etiquetas de plantilla <a href="https://docs.djangoproject.com/en/1.10/ref/templates/builtins/#for">for</a> y <code>endfor</code> para iterar a través de la lista de libros, como se muestra abajo. Cada iteración asigna a la variable de plantilla <code>book</code> la información para el ítem actual de la lista.</p>
+
+<pre class="brush: html">{% for <strong>book</strong> in book_list %}
+ &lt;li&gt; &lt;!-- code here get information from each <strong>book</strong> item --&gt; &lt;/li&gt;
+{% endfor %}
+</pre>
+
+<p>Si bien no se usan aquí, Django creará otras variables dentro del lazo que puedes usar para monitorear la iteración. Por ejemplo, puedes revisar la variable <code>forloop.last</code> para realizar un procesamiento condicional la última vez que el lazo se ejecuta.</p>
+
+<h4 id="Accediendo_a_las_variables">Accediendo a las variables</h4>
+
+<p>El código dentro del lazo crea un ítem de lista para cada libro, que muestra tanto el título (como un enlace hacia la vista de detalle que aún no creamos) como el autor.</p>
+
+<pre class="brush: html">&lt;a href="\{{ book.get_absolute_url }}"&gt;\{{ book.title }}&lt;/a&gt; (\{{book.author}})
+</pre>
+
+<p>Accedemos a los <em>campos</em> del registro del libro asociado usando la "notación de punto" (ej. <code>book.title</code> y <code>book.author</code>), donde el texto que sigue a la palabra <code>book</code> es el nombre del campo (como se definió en el modelo).</p>
+
+<p>También podemos invocar <em>funciones</em> en el modelo desde dentro de nuestra plantilla -- en este caso invocamos a <code>book.get_absolute_url()</code> para obtener una URL que se podría usar para desplegar la página de detalle relacionada. Esto funciona siempre y cuando la función no tenga ningún argumento (¡no hay forma de enviar argumentos!).</p>
+
+<div class="note">
+<p><strong>Nota</strong>: Debemos tener cuidado de los "efectos secundarios" al invocar funciones en las plantillas. Aquí solo obtenemos una URL para desplegar, pero una función puede hacer casi cualquier cosa -- ¡no quisieramos borrar nuestra base de datos (por ejemplo) solo por renderizar nuestra plantilla!</p>
+</div>
+
+<h4 id="Actualizar_la_plantilla_base">Actualizar la plantilla base</h4>
+
+<p>Abre la plantilla base (<strong>/locallibrary/catalog/templates/<em>base_generic.html</em></strong>) e inserta <strong>{% url 'books' %}</strong> en el enlace URL para <strong>All books</strong>, como se muestra abajo. Esto habilitará el enlace en todas las páginas (podemos ubicar esto exitosamente en su lugar ahora que hemos creado el mapeo url "books").</p>
+
+<pre class="brush: python">&lt;li&gt;&lt;a href="{% url 'index' %}"&gt;Home&lt;/a&gt;&lt;/li&gt;
+<strong>&lt;li&gt;&lt;a href="{% url 'books' %}"&gt;All books&lt;/a&gt;&lt;/li&gt;</strong>
+&lt;li&gt;&lt;a href=""&gt;All authors&lt;/a&gt;&lt;/li&gt;</pre>
+
+<h3 id="¿Cómo_se_ve">¿Cómo se ve?</h3>
+
+<p>Aún no podrás ver la lista de libros, porque aún nos falta una dependencia -- el mapeo URL para las páginas de detalle de libros, que se necesita para crear los hipervínculos a los libros individuales. Mostraremos tanto la lista de libros como las vistas de detalle después de la siguiente sección.</p>
+
+<h2 id="Página_de_detalle_de_libros">Página de detalle de libros</h2>
+
+<p>La página de detalle de libro desplegará información sobre un libro específico, a la que se accede usando la URL <code>catalog/book/<em>&lt;id&gt;</em></code> (donde <code>&lt;id&gt;</code> es la clave primaria para el libro). Además de los campos en el modelo <code>Book</code> (autor, resumen, ISBN, idioma, y género), listaremos también los detalles de las copias disponibles (<code>BookInstances</code>) incluyendo su estado, fecha de devolución esperada, edición e id. Esto permitirá a nuestros lectores no solo saber sobre el libro en sí, sino también confirmar si está disponible o cuándo lo estará.</p>
+
+<h3 id="Mapeo_URL_2">Mapeo URL</h3>
+
+<p>Abre <strong>/catalos/urls.py</strong> y añade el mapeador URL <strong>'book-detail'</strong> que se muestra abajo en negrita. Esta función <code>url()</code> define un patrón, vista de detalle genérica basada en clases asociada, y un nombre.</p>
+
+<pre class="brush: python">urlpatterns = [
+ url(r'^$', views.index, name='index'),
+  url(r'^books/$', views.BookListView.as_view(), name='books'),
+<strong>  url(r'^book/(?P&lt;pk&gt;\d+)$', views.BookDetailView.as_view(), name='book-detail'),</strong>
+]</pre>
+
+<p>A diferencia de nuestros mapeadores anteriores, en este caso estamos usando nuestra expresión regular (RE) para comparar frente a un "patrón" real en lugar de una simple cadena. Lo que hace esta RE en particular es coincidir con cualquier URL que empiece por <code>book/</code>, seguido de uno o más <em>dígitos</em> (números) antes del marcador de fin de línea. Mientras se realiza la comparación, se "capturan" los dígitos y son enviados a la función de vista como un parámetro llamado <code>pk</code>.</p>
+
+<div class="note">
+<p><strong>Nota</strong>: Como ya se discutió antes, nuestra URL coincidente es en realidad <code>catalog/book/&lt;digits&gt;</code> (como estamos en la aplicación <strong>catalog</strong>, se asume <code>/catalog/</code>).</p>
+</div>
+
+<div class="warning">
+<p><strong>Importante</strong>: La vista de detalle genérica basada en clases <em>espera</em> que se le envíe un parámetro llamado pk. Si estás escribiendo tu propia vista de función, puedes usar el nombre de parámetro que quieras, o incluso enviar la información como un argumento sin nombre.</p>
+</div>
+
+<h4 id="Una_breve_introducción_a_las_expresiones_regulares">Una breve introducción a las expresiones regulares</h4>
+
+<p>Las <a href="https://docs.python.org/3/library/re.html">expresiones regulares</a> son una herramienta de mapeo de patrones increíblemente poderosa. Hemos dicho poco sobre ellas hasta ahora porque estuvimos comparando con cadenas fijas en nuestras URLs (en lugar de con patrones) y porque son, francamente, bastante intuitivas e intimidantes para los principiantes.</p>
+
+<div class="note">
+<p><strong>Nota</strong>: ¡No te asustes! Los tipos de patrones con los que estaremos comparando son bastante simples, ¡y en muchos casos están bien documentados!</p>
+</div>
+
+<p>Lo primero que hay que saber es que las expresiones regulares deberían ser declaradas normalmente usando la sintaxis de literal de cadena sin procesar (esto es, están encerradas así: <strong>r'&lt;tu expresión regular va aquí&gt;'</strong>).</p>
+
+<p>Las partes principales de la sintaxis que necesitarás saber para declarar las coincidencias de patrones son:</p>
+
+<table class="standard-table">
+ <thead>
+ <tr>
+ <th scope="col">Símbolo</th>
+ <th scope="col">Significado</th>
+ </tr>
+ </thead>
+ <tbody>
+ <tr>
+ <td>^</td>
+ <td>Coincide con el inicio del texto</td>
+ </tr>
+ <tr>
+ <td>$</td>
+ <td>Coincide con el fin del texto</td>
+ </tr>
+ <tr>
+ <td>\d</td>
+ <td>Coincide con un dígito (0, 1, 2, ... 9)</td>
+ </tr>
+ <tr>
+ <td>\w</td>
+ <td>
+ <p>Concide con un caracter de palabra, ej. cualquier caracter del alfabeto en mayúscula o minúscula, dígito o guión bajo (_)</p>
+ </td>
+ </tr>
+ <tr>
+ <td>+</td>
+ <td>
+ <p>Concide con uno o más caracteres del precedente. Por ejemplo, para coincidir con uno o más dígitos usarías <code>\d+</code>. Para concidir con uno o más caracteres "a", podrías usar <code>a+</code></p>
+ </td>
+ </tr>
+ <tr>
+ <td>*</td>
+ <td>
+ <p>Concide con cero o más caracteres del precedente. Por ejemplo, para coincidir con nada o una palabra podrías usar <code>\w*</code></p>
+ </td>
+ </tr>
+ <tr>
+ <td>( )</td>
+ <td>
+ <p>Captura la parte del patrón dentro de los paréntesis. Todos los valores capturados serán enviados a la vista como parámetros sin nombre (si se captura múltiples patrones, los parámetros asociados serán enviados en el  órden en que fueron declaradas las capturas).</p>
+ </td>
+ </tr>
+ <tr>
+ <td>(?P&lt;<em>name</em>&gt;...)</td>
+ <td>
+ <p>Captura el patrón (indicado por ...) como una variable con nombre (en este caso "name"). Los valores capturados se envían a la vista con el nombre especificado. Tu vista debe, por tanto, ¡declarar un argumento con el mismo nombre!</p>
+ </td>
+ </tr>
+ <tr>
+ <td>[  ]</td>
+ <td>
+ <p>Concide con un caracter del conjunto. Por ejemplo, [abc] coincidirá con 'a' o 'b' o 'c'. [-\w] coincidrá con el caracter '-' o con cualquier letra.</p>
+ </td>
+ </tr>
+ </tbody>
+</table>
+
+<p>¡La mayoría de los caracteres restantes se pueden tomar literalmente!</p>
+
+<p>Consideremos algunos ejemplos reales de patrones:</p>
+
+<table class="standard-table">
+ <thead>
+ <tr>
+ <th scope="col">Patrón</th>
+ <th scope="col">Descripción</th>
+ </tr>
+ </thead>
+ <tbody>
+ <tr>
+ <td><strong>r'^book/(?P&lt;pk&gt;\d+)$'</strong></td>
+ <td>
+ <p>Esta es la RE usada en nuestro mapeador url. Concide con una cadena que tiene <code>book/</code> al inicio de la línea (<strong>^book/</strong>), luego tiene uno o más dígitos (<code>\d+</code>), y luego termina (sin ningún caracter que no sea un dígito antes del marcador de fin de línea).</p>
+
+ <p>También captura todos los dígitos <strong>(?P&lt;pk&gt;\d+)</strong> y los envía a la vista como un parámetro llamado 'pk'. <strong>¡Los valores capturados siempre se envían como cadena!</strong></p>
+
+ <p>Por ejemplo, esto coincidiría con <code>book/1234</code>, y enviaría una variable <code>pk='1234'</code> a la vista.</p>
+ </td>
+ </tr>
+ <tr>
+ <td><strong>r'^book/(\d+)$'</strong></td>
+ <td>
+ <p>Esta expresión coincide con las mismas URLs que el caso anterior. La información capturada se enviaría a la vista como un argumento sin nombre.</p>
+ </td>
+ </tr>
+ <tr>
+ <td><strong>r'^book/(?P&lt;stub&gt;[-\w]+)$'</strong></td>
+ <td>
+ <p>Esta expresión coincide con una cadena que tiene <code>book/</code> al inicio de la línea (<strong>^book/</strong>), luego tiene uno o más caracteres que son <em>o bien</em> '-' o una letra (<strong>[-\w]+</strong>), y luego termina. También captura este conjunto de caracteres y los envía a la vista como un parámetro llamado 'stub'.</p>
+
+ <p>Este es un patrón bastante típico para un "talón". Estos talones representan claves primarias en URLs amigables basadas en palabras para la información. Podrías usar un talón si quisieras que la URL de un libro sea más informativa. Por ejemplo, <code>/catalog/book/the-secret-garden</code> en lugar de <code>/catalog/book/33</code>.</p>
+ </td>
+ </tr>
+ </tbody>
+</table>
+
+<p>Puedes capturar múltiples patrones en una sola comparación, y por tanto codificar bastantes datos diferentes en una URL.</p>
+
+<div class="note">
+<p><strong>Nota</strong>: Como reto, considera cómo podrías codificar una url para listar todos los libros publicados en un año, mes y día en particular, y la RE que podría usarse para la comparación.</p>
+</div>
+
+<h4 id="Enviado_opciones_adicionales_en_tus_mapeos_URL">Enviado opciones adicionales en tus mapeos URL</h4>
+
+<p>Una característica que no hemos usado aquí, pero que te puede resultar valiosa, es que puedes declarar y enviar <a href="https://docs.djangoproject.com/en/1.10/topics/http/urls/#views-extra-options">opciones adicionales</a> a la vista. Las opciones se declaran como un diccionario que envías como el tercer argumento sin nombre a la función <code>url()</code>. Esta estrategia puede resultar útil si quiere usar la misma vista para múltiples recursos, y enviar información para configurar su comportamiento en cada caso (abajo suministramos una plantilla diferente en cada caso).</p>
+
+<pre class="brush: python">url(r'^/url/$', views.my_reused_view, {'my_template_name': 'some_path'}, name='aurl'),
+url(r'^/anotherurl/$', views.my_reused_view, {'my_template_name': 'another_path'}, name='anotherurl'),
+
+</pre>
+
+<div class="note">
+<p><strong>Nota</strong>: Tanto las opciones extra como los patrones capturados con nombre se envían a la vista como argumentos <em>con nombre</em>. Si usas el <strong>mismo nombre</strong> tanto para un patrón capturado como para una opción extra, solo el valor del patrón capturado será enviado a la vista (el valor especificado para la opción extra será eliminado).</p>
+</div>
+
+<h3 id="Vista_(basada_en_clases)_2">Vista (basada en clases)</h3>
+
+<p>Abre <strong>catalog/views.py</strong> y copia el siguiente código al final del archivo:</p>
+
+<pre class="brush: python">class BookDetailView(generic.DetailView):
+    model = Book</pre>
+
+<p>¡Eso es todo! Lo único que necesitas hacer ahora es crear una plantilla llamada <strong>/locallibrary/catalog/templates/catalog/book_detail.html</strong>, y la vista enviará la información en la base de datos para el registro del libro específico, extraído por el mapeador URL. Dentro de la plantilla puedes acceder a la lista de libros mediante la variable de plantilla llamada <code>object</code> o <code>book</code> (esto es, genéricamente, "<em><code>el_nombre_del_modelo</code></em>").</p>
+
+<p>Si lo necesitas puedes cambiar la plantilla usada y el nombre del objeto de contexto usado para referenciar al libro en la plantilla. Puedes también sobreescribir métodos para, por ejemplo, añadir información adicional al contexto.</p>
+
+<h4 id="¿Qué_sucede_si_el_registro_no_existe">¿Qué sucede si el registro no existe?</h4>
+
+<p>Si un registro solicitado no existe, la vista de detalle genérica basada en clases lanzará automáticamente por tí una excepción de tipo Http404 -- en producción, esto desplegará automáticamente una página apropiada de "recurso no encontrado", que puedes personalizar si lo deseas.</p>
+
+<p>Solo para darte una idea sobre cómo funciona esto, el fragmento de código de abajo demuestra cómo implementarías la vista basada en clases como una función, si <strong>no</strong> estuvieras usando la vista de detalle genérica basada en clases.</p>
+
+<pre class="brush: python">def book_detail_view(request,pk):
+ try:
+ book_id=Book.objects.get(pk=pk)
+ except Book.DoesNotExist:
+ raise Http404("Book does not exist")
+
+  #book_id=get_object_or_404(Book, pk=pk)
+
+ return render(
+ request,
+ 'catalog/book_detail.html',
+ context={'book':book_id,}
+ )
+</pre>
+
+<p>Primero, la vista intenta recuperar el registro del libro específico desde el modelo. Si esto falla, la vista debería lanzar una excepción de tipo <code>Http404</code> para indicar que el libro "no se encuentra". El último paso es, como de costumbre, llamar a <code>render()</code> con el nombre de la plantilla y la información del libro en el parámetro <code>context</code> (como un diccionario).</p>
+
+<div class="note">
+<p><strong>Nota</strong>: <code>get_object_or_404()</code> (que se muestra comentado arriba), es un atajo conveniente para lanzar una excepción de tipo <code>Http404</code> si el registro no se encuentra.</p>
+</div>
+
+<h3 id="Creando_la_plantilla_de_Vista_de_Detalle">Creando la plantilla de Vista de Detalle</h3>
+
+<p>Crea el archivo HTML <strong>/locallibrary/catalog/templates/catalog/book_detail.html</strong> y ponle el contenido de abajo. Como se discutió antes, este es el nombre de archivo por defecto esperado por la vista de detalle genérica basada en clases (para un modelo llamado <code>Book</code> en una aplicación llamada <code>catalog</code>).</p>
+
+<pre class="brush: html">{% extends "base_generic.html" %}
+
+{% block content %}
+ &lt;h1&gt;Title: \{{ book.title }}&lt;/h1&gt;
+
+ &lt;p&gt;&lt;strong&gt;Author:&lt;/strong&gt; &lt;a href=""&gt;\{{ book.author }}&lt;/a&gt;&lt;/p&gt; &lt;!-- author detail link not yet defined --&gt;
+ &lt;p&gt;&lt;strong&gt;Summary:&lt;/strong&gt; \{{ book.summary }}&lt;/p&gt;
+ &lt;p&gt;&lt;strong&gt;ISBN:&lt;/strong&gt; \{{ book.isbn }}&lt;/p&gt;
+ &lt;p&gt;&lt;strong&gt;Language:&lt;/strong&gt; \{{ book.language }}&lt;/p&gt;
+ &lt;p&gt;&lt;strong&gt;Genre:&lt;/strong&gt; {% for genre in book.genre.all %} \{{ genre }}{% if not forloop.last %}, {% endif %}{% endfor %}&lt;/p&gt;
+
+ &lt;div style="margin-left:20px;margin-top:20px"&gt;
+ &lt;h4&gt;Copies&lt;/h4&gt;
+
+ {% for copy in book.bookinstance_set.all %}
+ &lt;hr&gt;
+ &lt;p class="{% if copy.status == 'a' %}text-success{% elif copy.status == 'm' %}text-danger{% else %}text-warning{% endif %}"&gt;\{{ copy.get_status_display }}&lt;/p&gt;
+ {% if copy.status != 'a' %}&lt;p&gt;&lt;strong&gt;Due to be returned:&lt;/strong&gt; \{{copy.due_back}}&lt;/p&gt;{% endif %}
+ &lt;p&gt;&lt;strong&gt;Imprint:&lt;/strong&gt; \{{copy.imprint}}&lt;/p&gt;
+ &lt;p class="text-muted"&gt;&lt;strong&gt;Id:&lt;/strong&gt; \{{copy.id}}&lt;/p&gt;
+ {% endfor %}
+ &lt;/div&gt;
+{% endblock %}</pre>
+
+<ul>
+</ul>
+
+<div class="note">
+<p>El enlace <code>author</code> en la plantilla de arriba tiene una URL vacía porque no hemos creado aún una página de detalle de autor. Una vez que esta exista, deberías actualizar la URL así:</p>
+
+<pre>&lt;a href="<strong>{% url 'author-detail' book.author.pk %}</strong>"&gt;\{{ book.author }}&lt;/a&gt;
+</pre>
+</div>
+
+<p>Aunque es un poco más larga, casi todo lo que existe en esta plantilla se ha descrito previamente:</p>
+
+<ul>
+ <li>Extendemos nuestra plantilla base y sobreescribimos el bloque "content"</li>
+ <li>Usamos procesamiento condicional para determinar si desplegar o no contenidos específicos</li>
+ <li>Usamos lazos <code>for</code> para iterar a través de listas de objetos.</li>
+ <li>Accedemos a los campos de contexto usando la notación de puntos (como hemos usado la vista de detalle genérica, el contexto se llama <code>book</code>; podríamos también usar "<code>object</code>")</li>
+</ul>
+
+<p>Lo interesante que no hemos visto previamente es la función <code>book.bookinstance_set.all()</code>. Este método es "automágicamente" creado por Django para devolver el conjunto de registros de <code>BookInstance</code> asociado con un <code>Book</code> en particular.</p>
+
+<pre class="brush: python">{% for copy in book.bookinstance_set.all %}
+&lt;!-- code to iterate across each copy/instance of a book --&gt;
+{% endfor %}</pre>
+
+<p>Este método es necesario porque has declarado un campo <code>ForeignKey</code> (uno-a-muchos) únicamente en la lado "uno" de la relación. Como no haces nada para declarar la relación en el otro modelo ("muchos"), este no tiene ningún campo para obtener el conjunto de registros asociados. Para superar este problema, Django construye una función apropiadamente llamada "búsqueda reversa" que puedes usar. El nombre de la función se construye convirtiendo a minúsculas el nombre del modelo donde la <code>ForeignKey</code> fue declarada, seguido por <code>_set</code> (así, la función creada en <code>Book</code> es <code>bookinstance_set()</code>).</p>
+
+<div class="note">
+<p><strong>Nota</strong>: Aquí usamos <code>all()</code> para obtener todos los registros (la opción por defecto). A pesar de que puedes usar el método <code>filter()</code> para obtener un subconjunto de registros en el código, no puedes hacerlo directamente en las plantillas porque no puedes especificar argumentos para las funciones.</p>
+
+<p>Ten también cuidado de que si no defines un orden (en tu vista o modelo basado en clases), verás errores arrojados por el servidor de dearrollo como este:</p>
+
+<pre>[29/May/2017 18:37:53] "GET /catalog/books/?page=1 HTTP/1.1" 200 1637
+/foo/local_library/venv/lib/python3.5/site-packages/django/views/generic/list.py:99: UnorderedObjectListWarning: Pagination may yield inconsistent results with an unordered object_list: &lt;QuerySet [&lt;Author: Ortiz, David&gt;, &lt;Author: H. McRaven, William&gt;, &lt;Author: Leigh, Melinda&gt;]&gt;
+ allow_empty_first_page=allow_empty_first_page, **kwargs)
+</pre>
+
+<p>Eso sucede porque el <a href="https://docs.djangoproject.com/en/1.10/topics/pagination/#paginator-objects">objeto paginador</a> espera ver una cláusula ORDER BY siendo ejecutada en tu base de datos subyacente. Sin ella, ¡no puede estar seguro de que los registros devueltos están en el orden correcto!</p>
+
+<p>Este tutorial no llegó a la <strong>Paginación</strong> (aún, pero pronto lo hará), pero como no puedes uar <code>sort_by()</code> y enviar un parámetro (el mismo con <code>filter()</code> descrito arriba) tendrás que escoger entre tres opciones:</p>
+
+<ol>
+ <li>Añadir un <code>ordering</code> dentro de una declaración <code>class Meta</code> en tu modelo.</li>
+ <li>Añadir un atributo <code>queryset</code> en tu vista basada en clases personalizada, especificando un <code>order_by()</code>.</li>
+ <li>Añadir un método <code>get_queryset</code> a tu vista basada en clases pesonalizada y también especificar el <code>order_by()</code>.</li>
+</ol>
+
+<p>Si te decides por la opción <code>class Meta</code> para el modelo Author (probablemente no tan flexible como personalizar la vista basada en clases, pero lo suficientemente fácil), terminarás con algo como esto:</p>
+
+<pre>class Author(models.Model):
+ first_name = models.CharField(max_length=100)
+ last_name = models.CharField(max_length=100)
+ date_of_birth = models.DateField(null=True, blank=True)
+ date_of_death = models.DateField('Died', null=True, blank=True)
+
+ def get_absolute_url(self):
+ return reverse('author-detail', args=[str(self.id)])
+
+ def __str__(self):
+ return '%s, %s' % (self.last_name, self.first_name)
+
+<strong> class Meta:
+ ordering = ['last_name']</strong></pre>
+
+<p>Por supuesto, el campo no tiene que ser <code>last_name</code>: podría ser cualquier otro.</p>
+Y por último, pero no menos importante, deberías ordenar por un atributo/columna que tenga un índice real (único o no) en tu base de datos para evitar problemas de rendimiento. Por supuesto, esto no será necesario aquí (y probablemente nos estemos adelantando mucho) para la pequeña cantidad de libros (¡y usuarios!), pero es algo a tener en cuenta para proyectos futuros.</div>
+
+<h2 id="¿Cómo_se_ve_2">¿Cómo se ve?</h2>
+
+<p>En este punto deberíamos haber creado todo lo necesario para desplegar tanto la lista de libros como las páginas de detalles de libros. Ejecuta el servidor (<code>python3 manage.py runserver</code>) y dirígete en tu navegador a <a href="http://127.0.0.1:8000/">http://127.0.0.1:8000/</a>.</p>
+
+<div class="warning">
+<p><strong>Advertencia</strong>: No hagas click aún en ningún enlace de autor o de detalles de autores -- ¡los crearás en el reto!</p>
+</div>
+
+<p>Haz click en el enlace <strong>All books</strong> para desplegar la lista de libros.</p>
+
+<p><img alt="Book List Page" src="https://mdn.mozillademos.org/files/14049/book_list_page_no_pagination.png" style="border-style: solid; border-width: 1px; display: block; height: 216px; margin: 0px auto; width: 823px;"></p>
+
+<p>Luego haz click en un enlace a uno de tus libros. Si todo está correcto, deberías ver algo como la siguiente pantalla.</p>
+
+<p><img alt="Book Detail Page" src="https://mdn.mozillademos.org/files/14051/book_detail_page_no_pagination.png" style="border-style: solid; border-width: 1px; display: block; height: 783px; margin: 0px auto; width: 926px;"></p>
+
+<h2 id="Paginación">Paginación</h2>
+
+<p>Si apenas tienes unos pocos registros, nuestra página de lista de libros se verá bien. Sin embargo, cuando llegues a las decenas o centenas de registros la página tomará progresivamente más tiempo en cargar (y tendrá demasiado contenido para navegar adecuadamente). La solución a este problema es añadir paginación a tus vistas de lista, reduciendo el número de Ítems desplegados en cada página.</p>
+
+<p>Django incluye un excelente soporte para paginación. Mejor aún, este está incluído en las vistas de lista genéricas basadas en clases, ¡así que no tienes que hacer mucho para habilitarlo!</p>
+
+<h3 id="Vistas">Vistas</h3>
+
+<p>Abre <strong>catalog/views.py</strong>, y añadie la línea <code>paginate_by</code> que se muestra abajo en negrita.</p>
+
+<pre class="brush: python">class BookListView(generic.ListView):
+ model = Book
+ <strong>paginate_by = 10</strong></pre>
+
+<p>Con esta adición, apenas tengas más de 10 registros la vista comenzará a paginar la información que envía a la plantilla. A las diferentes páginas se accede usando parámetros GET -- para acceder a la página 2 usarías la URL: <code>/catalog/books/?<strong>page=2</strong></code>.</p>
+
+<h3 id="Plantillas">Plantillas</h3>
+
+<p>Ahora que la información está paginada, necesitamos añadir soporte a la plantilla para desplazarse a través del conjunto de resultados. Como podríamos querer hacer lo mismo en todas las vistas de lista, lo haremos de una forma en la que puede ser añadida a la plantilla base.</p>
+
+<p>Abre <strong>/locallibrary/catalog/templates/<em>base_generic.html </em></strong>y copia el siguiente bloque <code>pagination</code> debajo de nuestro bloque <code>content</code> (resaltado abajo en negrita). El código primero revisa si la paginación está habilitada en la página actual. Si lo está, añade enlaces <code>next</code> y <code>previous</code> apropiados (y el número de la página actual).</p>
+
+<pre class="brush: python"><strong>{% block content %}{% endblock %}</strong>
+
+{% block pagination %}
+ {% if is_paginated %}
+ &lt;div class="pagination"&gt;
+ &lt;span class="page-links"&gt;
+ {% if page_obj.has_previous %}
+ &lt;a href="\{{ request.path }}?page=\{{ page_obj.previous_page_number }}"&gt;previous&lt;/a&gt;
+ {% endif %}
+ &lt;span class="page-current"&gt;
+ Page \{{ page_obj.number }} of \{{ page_obj.paginator.num_pages }}.
+ &lt;/span&gt;
+ {% if page_obj.has_next %}
+ &lt;a href="\{{ request.path }}?page=\{{ page_obj.next_page_number }}"&gt;next&lt;/a&gt;
+ {% endif %}
+ &lt;/span&gt;
+ &lt;/div&gt;
+ {% endif %}
+{% endblock %} </pre>
+
+<p><code>page_obj</code> es un objeto <a href="https://docs.djangoproject.com/en/1.10/topics/pagination/#paginator-objects">Paginator</a> que existirá si se usa la paginación en la página actual. Te permite obtener toda la información sobre la página actual, páginas anteriores, cuántas páginas hay, etc.</p>
+
+<p>Usamos <code>\{{ request.path }}</code> para obtener la URL de la página actual para crear a su vez los enlaces de paginación. Esto es útil, porque es independiente del objeto que estamos paginando.</p>
+
+<p>¡Eso es todo!</p>
+
+<h3 id="¿Cómo_se_ve_3">¿Cómo se ve?</h3>
+
+<p>La captura de pantalla de abajo muestra cómo se ve la paginación -- si no has ingresado más de 10 títulos en tu base de datos, puedes probarlo más fácilmente reduciendo el número especificado en la línea <code>paginate_by</code> en tu archivo <strong>catalog/views.py</strong>. Para obtener el resultado de abajo lo cambiamos a <code>paginate_by = 2</code>.</p>
+
+<p>Los enlaces de paginación se muestran en la parte de abajo, con enlaces de next/previous desplegados dependiendo de en qué página estés</p>
+
+<p><img alt="Book List Page - paginated" src="https://mdn.mozillademos.org/files/14057/book_list_paginated.png" style="border-style: solid; border-width: 1px; display: block; height: 216px; margin: 0px auto; width: 924px;"></p>
+
+<h2 id="Rétate_a_tí_mismo">Rétate a tí mismo</h2>
+
+<p>El reto en este artículo es crear las vistas de lista y detalle para autores, que se requieren para completar el proyecto. Estas deberían estar disponibles en las siguientes URLs:</p>
+
+<ul>
+ <li><code>catalog/authors/</code> — La lista de todos los autores.</li>
+ <li><code>catalog/author/<em>&lt;id&gt;</em></code><em> </em>— La vista de detalle para el autor específico con un valor en el campo de clave primaria de <em><code>&lt;id&gt;</code></em></li>
+</ul>
+
+<p>El código requerido para los mapeadores URL y las vistas debería ser virtualmente idéntico a las vistas de lista y detalle para <code>Book</code> que creamos arriba. Las plantillas serán diferentes, pero tendrán un comportamiento similar.</p>
+
+<div class="note">
+<p><strong>Nota</strong>:</p>
+
+<ul>
+ <li>Una vez que has creado el mapeador URL para la página de lista de autores, necesitarás también actualizar el enlace <strong>All authors</strong> en la plantilla base. Sigue el <a href="#Update_the_base_template">mismo proceso</a> que hicimos cuando actualizamos el enlace <strong>All books</strong>.</li>
+ <li>Una vez que has creado el mapeador URL para la página de detalle de autores, deberías también actualizar la <a href="#Creating_the_Detail_View_template">plantilla de vista de detalle de libros </a>(<strong>/locallibrary/catalog/templates/catalog/book_detail.html</strong>) de modo que el enlace de autor apunte a tu nueva página de detalle de autor (en lugar de ser una URL vacía). La línea cambiará para añadir la etiqueta de plantilla que se muestra en negrita abajo.</li>
+</ul>
+
+<pre class="brush: html">&lt;p&gt;&lt;strong&gt;Author:&lt;/strong&gt; &lt;a href="<strong>{% url 'author-detail' book.author.pk %}</strong>"&gt;\{{ book.author }}&lt;/a&gt;&lt;/p&gt; </pre>
+</div>
+
+<p>Cuando termines, tus páginas deberían lucir similares a las capturas de pantalla de abajo.</p>
+
+<p><img alt="Author List Page" src="https://mdn.mozillademos.org/files/14053/author_list_page_no_pagination.png" style="border-style: solid; border-width: 1px; display: block; margin: 0px auto;"></p>
+
+<ul>
+</ul>
+
+<p><img alt="Author Detail Page" src="https://mdn.mozillademos.org/files/14055/author_detail_page_no_pagination.png" style="border-style: solid; border-width: 1px; display: block; height: 358px; margin: 0px auto; width: 825px;"></p>
+
+<ul>
+</ul>
+
+<h2 id="Resumen">Resumen</h2>
+
+<p>Felicitaciones, ¡la funcionalidad de nuestra biblioteca básica está ahora completa!</p>
+
+<p>En este artículo hemos aprendido cómo usar las vistas genéricas basadas en clases de lista y detalle, y las hemos usado para crear páginas para ver nuestros libros y autores. Sobre la marcha hemos aprendido sobre coincidencia de patrones con expresiones regulares, y cómo puedes enviar información desde las URLs a tus vistas. Hemos también aprendido unos cuantos trucos más para usar plantillas. Por último hemos mostrado cómo paginar vistas de lista, de modo que nuestras listas sean manejables incluso si tenemos muchos registros.</p>
+
+<p>En los siguientes artículos extenderemos esta biblioteca para añadir soporte para cuentas de usuario, y así demostrar la autenticación de usuarios, permisos, sesiones y formularios.</p>
+
+<h2 id="Mira_también">Mira también</h2>
+
+<ul>
+ <li><a href="https://docs.djangoproject.com/en/1.10/topics/class-based-views/generic-display/">Vistas genéricas basadas en clases incluídas</a> (Django docs)</li>
+ <li><a href="https://docs.djangoproject.com/en/1.10/ref/class-based-views/generic-display/">Vistas genéricas de despliegue</a> (Django docs)</li>
+ <li><a href="https://docs.djangoproject.com/en/1.10/topics/class-based-views/intro/">Introducción a las vistas basadas en clases</a> (Django docs)</li>
+ <li><a href="https://docs.djangoproject.com/en/1.10/ref/templates/builtins">Etiquetas de plantilla y filtros incluídos</a> (Django docs).</li>
+ <li><a href="https://docs.djangoproject.com/en/1.10/topics/pagination/">Paginación</a> (Django docs)</li>
+</ul>
+
+<p>{{PreviousMenuNext("Learn/Server-side/Django/Home_page", "Learn/Server-side/Django/Sessions", "Learn/Server-side/Django")}}</p>
+
+<p> </p>
+
+<h2 id="En_este_módulo">En este módulo</h2>
+
+<ul>
+ <li><a href="/es/docs/Learn/Server-side/Django/Introducción">Introducción a Django</a></li>
+ <li><a href="/es/docs/Learn/Server-side/Django/development_environment">Configurando un entorno de desarrollo Django</a></li>
+ <li><a href="/es/docs/Learn/Server-side/Django/Tutorial_local_library_website">Tutorial de Django: El sito web de la Biblioteca Local</a></li>
+ <li><a href="/es/docs/Learn/Server-side/Django/skeleton_website">Tutorial de Django Parte 2: Creando el esqueleto de un sitio web</a></li>
+ <li><a href="/es/docs/Learn/Server-side/Django/Models">Tutorial de Django Parte 3: Usando modelos</a></li>
+ <li><a href="/es/docs/Learn/Server-side/Django/Admin_site">Tutorial de Django Parte 4: Sitio de administración de Django</a></li>
+ <li><a href="/es/docs/Learn/Server-side/Django/Home_page">Tutorial de Django Parte 5: Creando nuestra página de inicio</a></li>
+ <li><a href="/es/docs/Learn/Server-side/Django/Generic_views">Tutorial de Django Parte 6: Listas genéricas y vistas de detalle</a></li>
+ <li><a href="/es/docs/Learn/Server-side/Django/Sessions">Tutorial de Django Parte 7: Framework de sesiones</a></li>
+ <li><a href="/es/docs/Learn/Server-side/Django/Authentication">Tutorial de Django Parte 8: Autenticación de usuarios y permisos</a></li>
+ <li><a href="/es/docs/Learn/Server-side/Django/Forms">Tutorial de Django Parte 9: Trabajando con formularios</a></li>
+ <li><a href="/es/docs/Learn/Server-side/Django/Testing">Tutorial de Django Parte 10: Probando una aplicación web de Django</a></li>
+ <li><a href="/es/docs/Learn/Server-side/Django/Deployment">Tutorial de Django Parte 11: Poniendo Django en producción</a></li>
+ <li><a href="/es/docs/Learn/Server-side/Django/web_application_security">Seguridad en aplicaciones web Django</a></li>
+ <li><a href="/es/docs/Learn/Server-side/Django/django_assessment_blog">DIY Django mini blog</a></li>
+</ul>