aboutsummaryrefslogtreecommitdiff
path: root/files/es/learn/server-side/django
diff options
context:
space:
mode:
authorPeter Bengtsson <mail@peterbe.com>2020-12-08 14:41:45 -0500
committerPeter Bengtsson <mail@peterbe.com>2020-12-08 14:41:45 -0500
commit1109132f09d75da9a28b649c7677bb6ce07c40c0 (patch)
tree0dd8b084480983cf9f9680e8aedb92782a921b13 /files/es/learn/server-side/django
parent4b1a9203c547c019fc5398082ae19a3f3d4c3efe (diff)
downloadtranslated-content-1109132f09d75da9a28b649c7677bb6ce07c40c0.tar.gz
translated-content-1109132f09d75da9a28b649c7677bb6ce07c40c0.tar.bz2
translated-content-1109132f09d75da9a28b649c7677bb6ce07c40c0.zip
initial commit
Diffstat (limited to 'files/es/learn/server-side/django')
-rw-r--r--files/es/learn/server-side/django/admin_site/index.html372
-rw-r--r--files/es/learn/server-side/django/authentication/index.html714
-rw-r--r--files/es/learn/server-side/django/deployment/index.html672
-rw-r--r--files/es/learn/server-side/django/development_environment/index.html421
-rw-r--r--files/es/learn/server-side/django/django_assessment_blog/index.html307
-rw-r--r--files/es/learn/server-side/django/forms/index.html661
-rw-r--r--files/es/learn/server-side/django/generic_views/index.html640
-rw-r--r--files/es/learn/server-side/django/home_page/index.html403
-rw-r--r--files/es/learn/server-side/django/index.html66
-rw-r--r--files/es/learn/server-side/django/introducción/index.html282
-rw-r--r--files/es/learn/server-side/django/models/index.html490
-rw-r--r--files/es/learn/server-side/django/sessions/index.html200
-rw-r--r--files/es/learn/server-side/django/skeleton_website/index.html397
-rw-r--r--files/es/learn/server-side/django/testing/index.html906
-rw-r--r--files/es/learn/server-side/django/tutorial_local_library_website/index.html103
-rw-r--r--files/es/learn/server-side/django/web_application_security/index.html176
16 files changed, 6810 insertions, 0 deletions
diff --git a/files/es/learn/server-side/django/admin_site/index.html b/files/es/learn/server-side/django/admin_site/index.html
new file mode 100644
index 0000000000..486d277003
--- /dev/null
+++ b/files/es/learn/server-side/django/admin_site/index.html
@@ -0,0 +1,372 @@
+---
+title: 'Tutorial Django Parte 4: Sitio de Administración de Django'
+slug: Learn/Server-side/Django/Admin_site
+tags:
+ - Aplicación web
+ - Artículo
+ - Codificación
+ - Django Admin
+ - Principiante
+ - Python
+ - Tutorial
+ - django
+ - django_admin
+ - programacion
+translation_of: Learn/Server-side/Django/Admin_site
+---
+<div>{{LearnSidebar}}</div>
+
+<div>{{PreviousMenuNext("Learn/Server-side/Django/Models", "Learn/Server-side/Django/Home_page", "Learn/Server-side/Django")}}</div>
+
+<p class="summary">Ahora que hemos creado modelos para el sitio web de la <a href="https://developer.mozilla.org/es/docs/Learn/Server-side/Django/Tutorial_local_library_website">BibliotecaLocal</a>, usaremos el sitio de administración de Django para añadir algunos datos de libros "reales". Primero mostraremos cómo registrar los modelos en el sitio de administración y luego te mostraremos cómo iniciar sesión y crear algunos datos. Al final del artículo mostraremos algunas formas en las que puedes mejorar más adelante la presentación del sitio de Administración.</p>
+
+<table class="learn-box standard-table">
+ <tbody>
+ <tr>
+ <th scope="row">Pre-requisitos:</th>
+ <td>Primero completa: <a href="https://developer.mozilla.org/es/docs/Learn/Server-side/Django/Models">Tutorial Django Parte 3: Uso de modelos</a>.</td>
+ </tr>
+ <tr>
+ <th scope="row">Objetivo:</th>
+ <td>
+ <p>Entender los beneficios y las limitaciones del sitio de administración de Django, y usarlo para crear algunos registros para nuestros modelos.</p>
+ </td>
+ </tr>
+ </tbody>
+</table>
+
+<h2 id="Introducción">Introducción</h2>
+
+<p>La <em>aplicación</em> de administración de Django puede usar tus modelos para construir automáticamente un área dentro del sitio que puedes usar para crear, consultar, actualizar y borrar registros. Esto puede ahorrarte mucho tiempo de desarrollo, haciendo muy fácil probar tus modelos y darte una idea de si tus datos son correctos. La aplicación de administración también puede ser útil para manejar datos en producción, dependiendo del estilo del sitio web. Desde el proyecto Django solo se recomienda para gestión de datos internos (por ejemplo, solo para uso de administradores o personas internas de tu organización), ya que como enfoque centrado en el modelo no es necesariamente la mejor interfaz posible para todos los usuarios, exponiendo una gran cantidad de detalles innecesarios de los modelos.</p>
+
+<p>Toda la configuración requerida para incluir la aplicación admin en tu sitio Web fue hecha automaticamente cuando  <a href="/en-US/docs/Learn/Server-side/Django/skeleton_website">creaste el esqueleto del proyecto</a> (para información sobre dependencias reales necesarias, vea los <a href="https://docs.djangoproject.com/en/1.10/ref/contrib/admin/">documentos de Django aquí</a>). Como resultado, todo lo que  <strong>debes</strong> hacer para agregar tus modelos a la aplicación admin  es  <em>registrarlos.</em> Al final de este artículo entregaremos una breve demostración sobre como puedes configurar aún más el área de administración para mejorar la visualización de nuestros modelos de datos.</p>
+
+<p>Después de registrar los modelos te mostraremos como crear un nuevo "administrador", iniciar sesión en el sitio, y crear algunos libros, autores, instancias de libros, y géneros. Esto será útil para probar las vistas y plantillas que empezaremos a crear en el siguiente tutorial.</p>
+
+<h2 id="Registrando_los_modelos">Registrando los modelos </h2>
+
+<p>Primero,  abre <strong>admin.py</strong> en la aplicación catálogo (<strong>/locallibrary/catalog/admin.py</strong>). Actualmente se ve como esto — notar que ya importa <code>django.contrib.admin</code>:</p>
+
+<pre class="brush: python">from django.contrib import admin
+
+# Register your models here.
+</pre>
+
+<p>Registra los modelos copiando el texto siguiente al final del archivo. Este simple código esta importando los modelos y después llama a  <code>admin.site.register</code> para registrar a cada uno de ellos.</p>
+
+<pre class="brush: python">from .models import Author, Genre, Book, BookInstance
+
+admin.site.register(Book)
+admin.site.register(Author)
+admin.site.register(Genre)
+admin.site.register(BookInstance)
+</pre>
+
+<div class="note"><strong>Nota</strong>: Si tu aceptaste el desafío de crear un modelo que represente el Lenguaje natural de un libro (<a href="/en-US/docs/Learn/Server-side/Django/Models">ver el artículo tutorial de modelos</a>), importalo y registralo también!</div>
+
+<p>Esta es la forma más simple de registrar un modelo, o modelos, con el sitio. El sitio de administración es altamente personalizable,  y hablaremos más sobre otras formas de registrar tus modelos más abajo.</p>
+
+<h2 id="Creando_un_administrador">Creando un administrador</h2>
+
+<p>Para iniciar sesión en el sitio de administración, necesitamos una cuenta de usuario con estado de <em>Personal</em> habilitado. Para ver y crear registros tambien necesitamos que este usuario tenga permisos para administrar todos nuestros objetos. Puedes crear una cuenta  "administrador" que tenga acceso total al sitio y a todos los permisios necesarios usando <strong>manage.py</strong>.</p>
+
+<p>Usa el siguiente comando, en el mismo directorio de <strong>manage.py</strong>, para crear al administrador. Deberás ingresar un nombre de usuario, dirección email, y una  contraseña <em>fuerte</em>.</p>
+
+<pre class="brush: bash">python3 manage.py createsuperuser
+</pre>
+
+<p>Una vez el comando termine un nuevo administrador será agregado a la base de datos. Ahora reinicia el servidor de desarrollo para que podamos probrar el inicio de sesión:</p>
+
+<pre class="brush: bash">python3 manage.py runserver
+
+</pre>
+
+<h2 id="Iniciar_sesión_y_usar_el_sitio">Iniciar sesión y usar el sitio</h2>
+
+<p>Para iniciar sesión en el sitio, ve a la URL <em>/admin</em> (e.j. <a href="http://127.0.0.1:8000/admin/">http://127.0.0.1:8000/admin</a>) e ingresa tus credenciales de id usuario y contraseña de administrador (serás redirigido a la página <em>login</em>, y entonces volverás a la URL de <em>/admin</em> después de haber ingresado tus datos).</p>
+
+<p>Esta parte del sitio muestra todos tus modelos, agrupados por aplicación instalada. Puedes hacer click en un nombre de modelo para ir a una pantalla que lista todos los registros asociados, y además puedes hacer click sobre esos registros para editarlos. También puedes hacer click directamente sobre el vínculo <strong>Añadir</strong> a continuación de cada modelo para comenzar a crear un registro de ese tipo.</p>
+
+<p><img alt="Admin Site - Home page" src="https://mdn.mozillademos.org/files/13975/admin_home.png" style="display: block; height: 634px; margin: 0px auto; width: 998px;"></p>
+
+<p>Haz click  sobre el vínculo <strong>Añadir</strong> a la derecha de <em>Books</em> para crear un nuevo libro, esto mostrará un diálogo parecido al de abajo). Nota como los títulos de cada campo, el tipo de widget usado, y el <code>help_text</code> (si existe) corresponde con el valor que especificaste en el modelo. </p>
+
+<p>Ingresa valores para los campos. Puede crear nuevos autores o géneros presionandoel botón <strong>+</strong> a continuación del campo respectivo ( o seleccionar un valor existente de las listas si ya las tenías creadas). Cuando termines puedes presionar  <strong>Grabar</strong>, <strong>Grabar y añadir otro</strong>, o <strong>Grabar y continuar editando</strong> para guardar el registro.</p>
+
+<p><img alt="Admin Site - Book Add" src="https://mdn.mozillademos.org/files/13979/admin_book_add.png" style="border-style: solid; border-width: 1px; display: block; height: 780px; margin: 0px auto; width: 841px;"></p>
+
+<div class="note">
+<p><strong>Nota</strong>: En este punto nos gustaría que pasaras algún tiempo añadiendo unos pocos libros, autores, y géneros (ej. Fantasía) a tu aplicación. Asegúrate de que cada autor y género incluye un par de libros diferentes (esto hará tus vistas de lista y detalle más interesantes cuando las implementemos más tarde en la serie de artículos).</p>
+</div>
+
+<p>Cuando hayas terminado de añadir libros, haz click en el enlace <strong>Home </strong>en el separador de arriba para regresar a la página principal de administración. Luego haz click en el enlace <strong>Books </strong>para desplegar la lista actual de libros (o en alguno de los otros enlaces para ver las listas de otros modelos). Ahora que haz añadido unos cuantos libros, la lista debería lucir similar a la captura de pantalla de abajo. Se muestra el título de cada libro; que es el valor devuelto por el método <code>__str__()</code> del modelo Book que especificamos en el artículo anterior.</p>
+
+<p><img alt="Admin Site - List of book objects" src="https://mdn.mozillademos.org/files/13935/admin_book_list.png" style="border-style: solid; border-width: 1px; display: block; height: 407px; margin: 0px auto; width: 1000px;"></p>
+
+<p>Desde esta lista puedes eliminar libros marcando la casilla de verificación junto al libro que no deseas y seleccionando la acción <em>delete... </em>en la lista de selección <em>Action</em>, y luego presionando el botón <strong>Go</strong>. Puedes también añadir nuevos libros presionando el botón <strong>ADD BOOK</strong>.</p>
+
+<p>Puedes editar un libro haciendo click en su nombre en la lista. La página de edición para un libro, como se muestra abajo, es casi idéntica a la página "Add". Las principales diferencias son el título de la página (<em>Change book</em>) y la adición de los botones <strong>Delete</strong>, <strong>HISTORY </strong>y <strong>VIEW ON SITE </strong>(este último aparece porque definimos el método <code>get_absolute_url()</code> en nuestro modelo).</p>
+
+<p><img alt="Admin Site - Book Edit" src="https://mdn.mozillademos.org/files/13977/admin_book_modify.png" style="border-style: solid; border-width: 1px; display: block; height: 780px; margin: 0px auto; width: 841px;"></p>
+
+<p>Ahora regresa a la página <strong>Home </strong>(usando el enlace <em>Home </em>de la barra superior) y observa las listas <strong>Author </strong>y <strong>Genre </strong>-- ya deberías tener algunos registros creados de cuando añadiste los nuevos libros, pero puedes crear algunos más.</p>
+
+<p>Lo que no vas a tener es <em>BookInstances</em>, porque estas no se crean de los libros (si bien puedes crear un <code>Book</code> desde una <code>BookInstance</code> -- esta es la naturaleza de los campos <code>ForeignKey</code>). Regresa a la página <em>Home </em>y presiona el botón <strong>Add </strong>relacionado para desplegar la pantalla <em>Add book instance</em>, como se muestra abajo. Nota el largo y globalmente único Id, que puede ser usado para identificar inequívocamente una única copia de un libro dentro de la biblioteca.</p>
+
+<p><img alt="Admin Site - BookInstance Add" src="https://mdn.mozillademos.org/files/13981/admin_bookinstance_add.png" style="border-style: solid; border-width: 1px; display: block; height: 514px; margin: 0px auto; width: 863px;"></p>
+
+<p>Crea algunos de estos registros para cada uno de tus libros. Establece el status en <em>Available </em>para al menos algunos registros y en <em>On loan</em> para otros. Si el status es <strong>diferente </strong>de <em>Available</em>, especifica también una fecha de <em>Due back</em> (devolución).</p>
+
+<p>¡Eso es todo! Has aprendido a configurar y usar el sitio de administración. También has creado registros para <code>Book</code>, <code>BookInstance</code>, <code>Genre</code> y <code>Author</code> que podremos usar una vez que creemos nuestras propias views (vistas) y templates (plantillas).</p>
+
+<h2 id="Configuración_avanzada">Configuración avanzada</h2>
+
+<p>Django hace un gran trabajo al crear un sitio de administración básico usando la información de los modelos registrados:</p>
+
+<ul>
+ <li>Cada modelo tiene una lista de registros individuales, identificados por la cadena creada por el método <code>__str__()</code> del modelo, y enlazados a vistas/formularios de detalle para edición. Por defecto, esta vista de lista tiene un menú de acción en la parte superior que puedes usar para realizar operaciones de eliminación masiva de los registros.</li>
+ <li>Los formularios de detalle de registro del modelo para edición y adición de registros contienen todos los campos del modelo, organizados verticalmente en su orden de declaración. </li>
+</ul>
+
+<p>Posteriormente puedes personalizar la interfaz para hacerla incluso más fácil de usar. Algunas de las cosas que puedes hacer son:</p>
+
+<ul>
+ <li>Vistas de lista:
+ <ul>
+ <li>Añadir campos e información adicional desplegada para cada registro. </li>
+ <li>Añadir filtros para seleccionar qué registros se listan, basados en fechas u otros tipos de valores (ej. estado de préstamo del libro).</li>
+ <li>Añadir opciones adicionales al menú <em>Action </em>en las vistas de lista y elegir en qué lugar del formulario se despliega este menú.</li>
+ </ul>
+ </li>
+ <li>Vistas de detalle:
+ <ul>
+ <li>Elegir qué campos desplegar (o excluir), junto con su orden, agrupamiento, si son editables, el tipo de control a usarse, orientación, etc.</li>
+ <li>Añadir campos relacionados a un registro para permitir la edición en cadena (ej. proveer la capacidad de añadir y editar registros de libros mientras estás creando su registro de autor).</li>
+ </ul>
+ </li>
+</ul>
+
+<p>En esta sección observaremos unos cuantos cambios que mejorarán la interfaz de nuestra <em>LocalLibrary</em>, incluyendo la adición de más información a las listas de los modelos <code>Book</code> y <code>Author</code>, y mejorando el diseño de sus vistas de edición. No cambiaremos la presentación de los modelos <code>Language</code> y <code>Genre</code> debido a que solo tienen un campo cada uno, ¡por lo que no hay ningún beneficio real de hacerlo!</p>
+
+<p>Puedes encontrar una referencia completa de todas las opciones de personalización del sitio de administración en <a href="https://docs.djangoproject.com/en/1.10/ref/contrib/admin/">The Django Admin site</a> (Django Docs).</p>
+
+<h3 id="Registrar_una_clase_ModelAdmin">Registrar una clase ModelAdmin</h3>
+
+<p>Para cambiar la forma en la que un modelo se despliega en la interfaz de administración debes definir una clase <a href="https://docs.djangoproject.com/en/dev/ref/contrib/admin/#modeladmin-objects">ModelAdmin</a> (que describe el diseño) y registrarla con el modelo.</p>
+
+<p>Comencemos con el modelo Author. Abre <strong>admin.py</strong> en la aplicación catalog (<strong>/locallibrary/catalog/admin.py</strong>). Comenta tu registro original para el modelo <code>Author</code> (colocando el prefijo # en la línea):</p>
+
+<pre class="brush: js"># admin.site.register(Author)</pre>
+
+<p>Ahora añade una nueva clase <code>AuthorAdmin</code> y regístrala como se muestra abajo.</p>
+
+<pre class="brush: python"># Define the admin class
+class AuthorAdmin(admin.ModelAdmin):
+ pass
+
+# Register the admin class with the associated model
+admin.site.register(Author, AuthorAdmin)
+</pre>
+
+<p>Ahora añadiremos clases <code>ModelAdmin </code>para <code>Book</code>, y <code>BookInstance</code>. De nuevo, debemos comentar nuestros registros originales:</p>
+
+<pre class="brush: python">#admin.site.register(Book)
+#admin.site.register(BookInstance)</pre>
+
+<p>Ahora, para crear y registar los nuevos modelos usaremos, para propósitos de esta demostración, la expresión <code>@register </code>para registrar los modelos (hace exactamente lo mismo que <code>admin.site.register()</code>):</p>
+
+<pre class="brush: python"># Register the Admin classes for Book using the decorator
+
+@admin.register(Book)
+class BookAdmin(admin.ModelAdmin):
+ pass
+
+# Register the Admin classes for BookInstance using the decorator
+
+@admin.register(BookInstance)
+class BookInstanceAdmin(admin.ModelAdmin):
+ pass
+</pre>
+
+<p>Al momento todas nuestras clases de administración estás vacías (observa "pass"), así que el comportamiento de administración ¡no cambiará! Ahora podemos extenderlas para definir nuestro comportamiento de administración específico para cada modelo.</p>
+
+<h3 id="Configurar_las_vistas_de_lista">Configurar las vistas de lista</h3>
+
+<p>La <em>LocalLibrary</em> actualmente lista todos los autores usando el nombre generado por el método <code>__str__()</code> del modelo. Esto funciona bien cuando solo tienes unos pocos autores, pero una vez que tienes muchos puedes terminar teniendo duplicados. Para diferenciarlos, o simplemente para mostrar información más interesante sobre cada autor, puedes usar <a href="https://docs.djangoproject.com/en/dev/ref/contrib/admin/#django.contrib.admin.ModelAdmin.list_display">list_display</a> para añadir otros campos a la vista.</p>
+
+<p>Reemplaza tu clase <code>AuthorAdmin </code>con el código de abajo. Los nombres de campos a ser desplegados en la lista están declarados en una tupla en el orden requerido, como se muestra (estos son los mismos nombres especificados en tu modelo original).</p>
+
+<pre class="brush: python">class AuthorAdmin(admin.ModelAdmin):
+ list_display = ('last_name', 'first_name', 'date_of_birth', 'date_of_death')
+</pre>
+
+<p>Recarga el sitio y navega hacia la lista de autores. Ahora deberían desplegarse los campos de arriba, así:</p>
+
+<p><img alt="Admin Site - Improved Author List" src="https://mdn.mozillademos.org/files/14023/admin_improved_author_list.png" style="border-style: solid; border-width: 1px; display: block; height: 302px; margin: 0px auto; width: 941px;"></p>
+
+<p>Para nuestro modelo <code>Book</code> desplegaremos adicionalmente el <code>author</code> y <code>genre</code>. El <code>author</code> es un campo de relación tipo <code>ForeignKey</code> (uno a uno), y por tanto estará representado por el valor <code>__str__()</code> del registro asociado. Reemplaza la clase BookAdmin con la versión de abajo.</p>
+
+<pre>class BookAdmin(admin.ModelAdmin):
+ list_display = ('title', 'author', 'display_genre')</pre>
+
+<p>Desafortunadamente, no podemos especificar directamente el campo <code>genre</code> en <code>list_display</code> porque es un campo <code>ManyToManyField</code> (Django previene esto porque habría un alto "costo" de acceso a base de datos si lo hiciera). En lugar de eso, definiremos una función <code>display_genre</code> para obtener la información como una cadena (esta es la función que hemos llamado arriba; la definiremos más abajo).</p>
+
+<div class="note">
+<p><strong>Nota</strong>: Obtener el <code>genre </code>podría no ser una buena idea aquí, debido al "costo" de la operación en la base de datos. Te mostramos cómo hacerlo porque llamar funciones desde tus modelos puede ser muy útil por otras razones -- por ejemplo para añadir un enlace <em><strong>Delete</strong></em> junto a cada ítem en la lista.</p>
+</div>
+
+<p>Añade el siguiente código en tu modelo <code>Book</code> (<strong>models.py</strong>). Esto crea una cadena con los tres primeros valores del campo <code>genre</code> (si existen) y crea una <code>short_description</code> (descripción corta) que puede ser usada en el sitio de administración por este método.</p>
+
+<pre class="brush: python">    def display_genre(self):
+ """
+ Creates a string for the Genre. This is required to display genre in Admin.
+ """
+ return ', '.join([ genre.name for genre in self.genre.all()[:3] ])
+ display_genre.short_description = 'Genre'
+</pre>
+
+<p>Después de guardar el modelo y actualizar admin, recarga el sitio y ve a la página de lista de <em>Books</em> (libros), deberías ver una lista de libros como la de abajo:</p>
+
+<p><img alt="Admin Site - Improved Book List" src="https://mdn.mozillademos.org/files/14025/admin_improved_book_list.png" style="border-style: solid; border-width: 1px; display: block; height: 337px; margin: 0px auto; width: 947px;"></p>
+
+<p>El modelo <code>Genre</code> (y el modelo <code>Language</code>, si lo definiste) tiene un solo campo, por lo que no tiene sentido crear un modelo adicional para el mismo para desplegar campos adicionales.</p>
+
+<div class="note">
+<p><strong>Nota</strong>: Vale la pena actualizar el modelo <code>BookInstance</code> para mostrar al menos el estado y fecha de devolución esperada. ¡Lo hemos añadido como un reto al final de este artículo!</p>
+</div>
+
+<h3 id="Añadir_filtros_de_lista">Añadir filtros de lista</h3>
+
+<p>Una vez que tienes muchos ítems en una lista, puede ser útil filtrar los ítems que se despliegan. Esto se hace listando campos en el atributo <code>list_filter</code>. Reemplaza tu clase <code>BookInstanceAdmin</code> actual con el fragmento de código de abajo.</p>
+
+<pre class="brush: python">class BookInstanceAdmin(admin.ModelAdmin):
+<strong> list_filter = ('status', 'due_back')</strong>
+</pre>
+
+<p>La vista de lista incluirá ahora un cuadro de filtrado a la derecha. Nota como puedes elegir fechas y estados para filtrar los valores:</p>
+
+<p><img alt="Admin Site - BookInstance List Filters" src="https://mdn.mozillademos.org/files/14037/admin_improved_bookinstance_list_filters.png" style="height: 528px; width: 960px;"></p>
+
+<h3 id="Organizar_el_diseño_de_vista_detallada">Organizar el diseño de vista detallada</h3>
+
+<p>Por defecto, las vistas detalladas organizan todos los campos verticalmente, en su órden de declaración en el modelo. Puedes cambiar el orden de declaración, qué campos se despliegan (o excluyen), si se usa secciones para organizar la información, si los campos se despliegan en horizontal o vertical, e incluso qué controles de edición se usan en los formularios de administración.</p>
+
+<div class="note">
+<p><strong>Nota</strong>: Los modelos de la <em><strong>LocalLibrary </strong></em>son relativamente simples, por lo que no tenemos una gran necesidad de cambiar el diseño; sin embargo haremos algunos cambios solo para mostrarte cómo.</p>
+</div>
+
+<h4 id="Controlando_qué_campos_son_desplegados_y_ordenados">Controlando qué campos son desplegados y ordenados</h4>
+
+<p>Actualiza tu clase <code>AuthorAdmin </code>para añadir la línea <code>fields</code>, como se muestra abajo (en negrita):</p>
+
+<pre class="brush: python">class AuthorAdmin(admin.ModelAdmin):
+ list_display = ('last_name', 'first_name', 'date_of_birth', 'date_of_death')
+<strong> fields = ['first_name', 'last_name', ('date_of_birth', 'date_of_death')]</strong>
+</pre>
+
+<p>El atributo <code>fields</code> lista solo los campos que se van a desplegar en el formulario, en orden. Los campos se despliegan en vertical por defecto, pero se desplegarán en horizontal si los agrupas en una tupla (como se muestra en los campos "date" arriba).</p>
+
+<p>Reinicia tu aplicación y ve a la vista de detalle de autor -- ahora debería aparecer como se muestra abajo:</p>
+
+<p><img alt="Admin Site - Improved Author Detail" src="https://mdn.mozillademos.org/files/14027/admin_improved_author_detail.png" style="border-style: solid; border-width: 1px; display: block; height: 282px; margin: 0px auto; width: 928px;"></p>
+
+<div class="note">
+<p><strong>Nota</strong>: Puedes también usar el atributo <code>exclude</code> para declarar una lista de atributos que se excluirán del formulario (todos los demás atributos en el modelo se desplegarán).</p>
+</div>
+
+<h4 id="Seccionando_la_vista_de_detalle">Seccionando la vista de detalle</h4>
+
+<p>Puedes añadir "secciones" para agrupar información relacionada del modelo dentro del formulario de detalle, usando el atributo <a href="https://docs.djangoproject.com/en/dev/ref/contrib/admin/#django.contrib.admin.ModelAdmin.fieldsets">fieldsets.</a></p>
+
+<p>En el modelo BookInstance tenemos información relacionada a cuál es el libro (ej. <code>name</code>, <code>imprint</code> e <code>id</code>) y a cuándo estará disponible (<code>status</code>, <code>due_back</code>). Podemos configurarlas en diferentes secciones añadiendo el texto en negrita a nuestra clase <code>BookInstanceAdmin</code>.</p>
+
+<pre class="brush: python">@admin.register(BookInstance)
+class BookInstanceAdmin(admin.ModelAdmin):
+ list_filter = ('status', 'due_back')
+
+<strong> fieldsets = (
+ (None, {
+ 'fields': ('book', 'imprint', 'id')
+ }),
+ ('Availability', {
+ 'fields': ('status', 'due_back')
+ }),
+ )</strong></pre>
+
+<p>Cada sección tiene su propio título (o <code>None</code>, si no quieres un título) y una tupla de campos asociada en un diccionario -- el formato es complicado de describir pero bastante fácil de entender si observas el fragmento de código que se encuentra justo arriba.</p>
+
+<p>Reinicia y navega a una vista de instancia de libro (book instance); el formulario debería aparecer como se muestra abajo:</p>
+
+<p><img alt="Admin Site - Improved BookInstance Detail with sections" src="https://mdn.mozillademos.org/files/14029/admin_improved_bookinstance_detail_sections.png" style="border-style: solid; border-width: 1px; display: block; height: 580px; margin: 0px auto; width: 947px;"></p>
+
+<h3 id="Edición_en_cadena_de_registros_asociados">Edición en cadena de registros asociados</h3>
+
+<p>A veces puede tener sentido el añadir registros asociados al mismo tiempo. Por ejemplo, puede tener sentido el tener información tanto de un libro como de las copias específicas que has adquirido del mismo, en la misma página de detalle.</p>
+
+<p>Puedes hacerlo declarando <a href="https://docs.djangoproject.com/en/dev/ref/contrib/admin/#django.contrib.admin.ModelAdmin.inlines">inlines</a>, de tipo <a href="https://docs.djangoproject.com/en/dev/ref/contrib/admin/#django.contrib.admin.TabularInline">TabularInline</a> (diseño horizontal) o <a href="https://docs.djangoproject.com/en/dev/ref/contrib/admin/#django.contrib.admin.StackedInline">StackedInline</a> (diseño vertical, tal como el diseño de modelo por defecto). Puedes añadir la información de <code>BookInstance</code> dentro de nuestro detalle de <code>Book</code> añadiendo las líneas de abajo en negrita cerca de tu <code>BookAdmin</code>:</p>
+
+<pre class="brush: python"><strong>class BooksInstanceInline(admin.TabularInline):
+    model = BookInstance</strong>
+
+@admin.register(Book)
+class BookAdmin(admin.ModelAdmin):
+    list_display = ('title', 'author', 'display_genre')
+<strong>    inlines = [BooksInstanceInline]</strong>
+</pre>
+
+<p>Prueba recargando tu aplicación y observando la vista para un libro -- ahora deberías ver al final las instancias relacionadas a este libro (inmediatamente debajo de los campos de género del libro):</p>
+
+<p><img alt="Admin Site - Book with Inlines" src="https://mdn.mozillademos.org/files/14033/admin_improved_book_detail_inlines.png" style="border-style: solid; border-width: 1px; display: block; height: 889px; margin: 0px auto; width: 937px;"></p>
+
+<p>En este caso, todo lo que hemos hecho es declarar nuestra clase encadenada tabular, que simplemente añade todos los campos del modelo <em>encadenado</em>. Puedes especificar toda clase de información adicional para el diseño incluyendo los campos a mostrar, su órden, si son solo de lectura o no, etc. (ve <a href="https://docs.djangoproject.com/en/dev/ref/contrib/admin/#django.contrib.admin.TabularInline">TabularInline</a> para más información).</p>
+
+<div class="note">
+<p><strong>Nota</strong>: ¡hay algunas lamentables limitaciones a esta funcionalidad! En la captura de pantalla de arriba tenemos tres instancias del libro existentes, seguidas de tres lugares para nuevas instancias del libro (¡que se ven muy similares!). Sería mejor NO tener instancias extras por defecto del libro y simplemente añadirlas mediante el enlace <strong>Add another Book instance</strong>, o poder simplemente listar las <code>BookInstances</code> como enlaces no legibles desde aquí. La primera opción puede hacerse estableciendo el atributo <code>extra</code> a 0 en el modelo <code>BookInstanceInline</code>, inténtalo tú mismo.</p>
+</div>
+
+<h2 id="Rétate_a_tí_mismo">Rétate a tí mismo</h2>
+
+<p>Hemos aprendido mucho en esta sección, así que es hora de que intentes algunas cosas.</p>
+
+<ol>
+ <li>Para la vista de lista de <code>BookInstance</code>, añade código para desplegar el libro, estado, fecha de devolución e id (en lugar del texto por defecto de <code>__str__()</code>).</li>
+ <li>Añade una lista encadenada de ítems <code>Book</code> (libros) a la vista detallada de <code>Author</code> usando el mismo método que usamos para <code>Book</code>/<code>BookInstance</code>.</li>
+</ol>
+
+<ul>
+</ul>
+
+<h2 id="Resumen">Resumen</h2>
+
+<p>¡Eso es todo! Ahora has aprendido cómo configurar el sito de administración tanto en su forma más simple como la mejorada, cómo crear un super usuario, y cómo navegar en el sitio de administración y ver, borrar y actualizar registros. Durante el proceso has creado un grupo de libros, instancias de libros, géneros y autores que seremos capaces de listar y desplegar una vez que creemos nuestras propias vistas y plantillas.</p>
+
+<h2 id="Siguientes_lecturas">Siguientes lecturas</h2>
+
+<ul>
+ <li><a href="https://docs.djangoproject.com/en/1.10/intro/tutorial02/#introducing-the-django-admin">Escribiendo tu primera aplicación Django, parte 2: Introducción a Django Admin</a> (Django docs)</li>
+ <li><a href="https://docs.djangoproject.com/en/1.10/ref/contrib/admin/">El sitio de administración de Django </a>(Django Docs)</li>
+</ul>
+
+<p>{{PreviousMenuNext("Learn/Server-side/Django/Models", "Learn/Server-side/Django/Home_page", "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>
diff --git a/files/es/learn/server-side/django/authentication/index.html b/files/es/learn/server-side/django/authentication/index.html
new file mode 100644
index 0000000000..af07915ad9
--- /dev/null
+++ b/files/es/learn/server-side/django/authentication/index.html
@@ -0,0 +1,714 @@
+---
+title: 'Tutorial de Django Parte 8: Autenticación y permisos de Usuario'
+slug: Learn/Server-side/Django/Authentication
+tags:
+ - Aprender
+ - Artículo
+ - Autenticacion de Django
+ - Autenticación
+ - Principiante
+ - Python
+ - Sesiones
+ - Tutorial
+ - django autenticación
+ - permisos
+translation_of: Learn/Server-side/Django/Authentication
+---
+<div>{{LearnSidebar}}</div>
+
+<div>{{PreviousMenuNext("Learn/Server-side/Django/Sessions", "Learn/Server-side/Django/Forms", "Learn/Server-side/Django")}}</div>
+
+<p class="summary">En este tutorial mostraremos cómo permitir a los usuarios iniciar sesión en tu sitio con sus propias cuentas, y cómo controlar lo que pueden hacer basándose en si han iniciado sesión o no y sus <em>permisos</em>. Como parte de esta demostración extenderemos el 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>, añadiendo páginas de inicio y cierre de sesión, y páginas específicas de usuarios y personal de la biblioteca para ver libros que han sido prestados.</p>
+
+<table class="learn-box standard-table">
+ <tbody>
+ <tr>
+ <th scope="row">Prerequisitos:</th>
+ <td>Completa todos los temas del tutorial anterior, incluyendo: <a href="/en-US/docs/Learn/Server-side/Django/Sessions">Django Tutorial Part 7: Sessions framework</a>.</td>
+ </tr>
+ <tr>
+ <th scope="row">Objetivo:</th>
+ <td>Comprender como configurar y usar la autenticación de usuario y los permisos.</td>
+ </tr>
+ </tbody>
+</table>
+
+<h2 id="Introducción">Introducción</h2>
+
+<p>Django proporciona un sistema de autenticación y autorización ("permisos"), construido sobre el framework de sesión discutido en el <a href="/en-US/docs/Learn/Server-side/Django/Sessions">tutorial anterior</a>, que le permite verificar credenciales de usuario y definir que acciones puede realizar cada usuario. El framework incluye modelos para <code>Users</code> y <code>Groups</code> (una forma genérica de aplicar permisos a más de un usuario a la vez), permisos/indicadores (permissions/flags) que designan si un usuario puede realizar una tarea, formularios y vistas para iniciar sesión en los usuarios, y view tools para restringir el contenido.</p>
+
+<div class="note">
+<p><strong>Nota</strong>: Según Django el sistema de autenticación pretende ser muy genérico, y, por lo tanto,  no proporciona algunas características proporcinadas en otros sistemas de autenticación web. Las soluciones para algunos problemas están disponibles como paquetes de terceros. Por ejemplo, regulación de intentos de inicio de sesión y autenticación frente a terceros (por ej. OAuth).</p>
+</div>
+
+<p>En este tutorial mostraremos cómo habilitar la autenticación de usuarios en el sitio web <a href="https://developer.mozilla.org/en-US/docs/Learn/Server-side/Django/Tutorial_local_library_website">BibliotecaLocal</a>, crear tus propias páginas de login y logout, añadir permisos a tus modelos, y controlar el acceso a las páginas. Usaremos la autenticación/permisos para desplegar listas de libros que han sido solicitados tanto por los usuarios como por los bibliotecarios.</p>
+
+<p>El sistema de autenticación es muy flexible, y puedes crear tus URLs, formularios, vistas y plantillas desde el inicio si quieres, simplemente llamando a la API provista para loguear al usuario. Sin embargo, en este artículo vamos a usar las vistas y formularios de autenticación "en stock" de Django para nuestras páginas de login y logout. De todos modos necesitaremos crear algunas plantillas, pero eso es bastante fácil.</p>
+
+<p>Te mostraremos también cómo crear permisos, y revisar el estado de login y permisos tanto en vistas como en plantillas.</p>
+
+<h2 id="Habilitanto_la_autenticación">Habilitanto la autenticación</h2>
+
+<p>La autenticación fue habilitada automáticamente cuando <a href="/es/docs/Learn/Server-side/Django/skeleton_website">creamos el sitio web esqueleto</a> (en el tutorial 2), así que no necesitas hacer nada más en este punto.</p>
+
+<div class="note">
+<p><strong>Nota</strong>: Toda la configuración necesaria fue hecha por nosotros cuando creamos la aplicación usando el comando <code>django-admin startproject</code>. Las tablas de base de datos para los usuarios y permisos de modelo fueron creados la primera vez que ejecutamos <code>python manage.py migrate</code>.</p>
+</div>
+
+<p>La configuración se establece en las secciones <code>INSTALLED_APPS</code> y <code>MIDDLEWARE</code> del archivo del proyecto (<strong>locallibrary/locallibrary/settings.py</strong>), como se muestra abajo:</p>
+
+<pre class="brush: python notranslate">INSTALLED_APPS = [
+ ...
+<strong>    'django.contrib.auth', </strong>#Core authentication framework and its default models.
+<strong>    'django.contrib.contenttypes', #</strong>Django content type system (allows permissions to be associated with models).
+ ....
+
+MIDDLEWARE = [
+ ...
+<strong>    'django.contrib.sessions.middleware.SessionMiddleware',</strong> #Manages sessions across requests
+ ...
+<strong>    'django.contrib.auth.middleware.AuthenticationMiddleware',</strong> #Associates users with requests using sessions.
+ ....
+</pre>
+
+<h2 id="Creando_usuarios_y_grupos">Creando usuarios y grupos</h2>
+
+<p>Ya creaste tu primer usuario cuando revisamos el <a href="/es/docs/Learn/Server-side/Django/Admin_site">sitio de administración de Django</a> en el tutorial 4 (fue un superusuario, creado con el comando <code>python manage.py createsuperuser</code>). Nuestro superusuario ya está autenticado y tiene todos los permisos, así que necesitaremos crear un usuario de prueba que represente un usuario normal del sitio. Estaremos usando el sitio de administración para crear los grupos y logins de nuestro sitio web <em>BibliotecaLocal</em>, ya que es una de las formas más rápidas de hacerlo.</p>
+
+<div class="note">
+<p><strong>Nota</strong>: Puedes también crear usuarios mediante programación, como se muestra abajo. Tendrías que hacerlo, por ejemplo, si estuvieras desarrollando una interfaz para permitir a los usuarios crear sus propios logins (no deberías dar a los usuarios acceso al sito de administración).</p>
+
+<pre class="brush: python notranslate">from django.contrib.auth.models import User
+
+# Create user and save to the database
+user = User.objects.create_user('myusername', 'myemail@crazymail.com', 'mypassword')
+
+# Update fields and then save again
+user.first_name = 'John'
+user.last_name = 'Citizen'
+user.save()
+
+</pre>
+</div>
+
+<p>A continuación, primero crearemos un grupo y luego un usuario. Aunque no tengamos ningún permiso aún para otorgar a los miembros de nuestra biblioteca, si lo necesitamos para más adelante, será mucho más fácil otorgarlo una vez al grupo que individualmente a cada miembro.</p>
+
+<p>Inicia el servidor de desarrollo y navega hasta el sitio de administracion en tu navegador web local (<a href="http://127.0.0.1:8000/admin/">http://127.0.0.1:8000/admin/</a>). Ingresa al sitio usando las credenciales de la cuenta de tu superusuario. El nivel superior del sitio de administracion "Admin site" muestra todos tus modelos, ordenados por la aplicacion por defecto de Django "django application". Desde la seccion de <strong>Autenticación y Autorización </strong>puedes dar click en los enlaces de <strong>Usuarios </strong>"Users" y <strong>Grupos </strong>"Groups" para ver todos sus registros existentes.</p>
+
+<p><img alt="Admin site - add groups or users" src="https://mdn.mozillademos.org/files/14091/admin_authentication_add.png" style="border-style: solid; border-width: 1px; display: block; height: 364px; margin: 0px auto; width: 661px;"></p>
+
+<p>Primero vamos a crear un nuevo grupo para los miembros de nuestra biblioteca.</p>
+
+<ol>
+ <li>Da click en el boton <strong>Add </strong>"Añadir" (Enseguida de Group) para crear un nuevo grupo ; ingresa el <strong>Nombre </strong>"Name" para el grupo de "Library Members".<img alt="Admin site - add group" src="https://mdn.mozillademos.org/files/14093/admin_authentication_add_group.png" style="border-style: solid; border-width: 1px; display: block; height: 561px; margin: 0px auto; width: 800px;"></li>
+ <li>No necesitamos de ningun permiso para el grupo , entonces solo presiona Save (Seras redirigido a una lista de los grupos disponibles).</li>
+</ol>
+
+<p>Ahora vamos a crear un usuario:</p>
+
+<ol>
+ <li>Navega de vuelta a la pagina de inicio "home" del sitio de administracion "Admin site".</li>
+ <li>Da click en el boton <strong>Add </strong>"Añadir" que queda enseguida de Users "Usuarios" para abrir el cuadro de dialogo de Usuario <strong>Add </strong>"Añadir usuario".<img alt="Admin site - add user pt1" src="https://mdn.mozillademos.org/files/14095/admin_authentication_add_user_prt1.png" style="border-style: solid; border-width: 1px; display: block; height: 409px; margin: 0px auto; width: 800px;"></li>
+ <li>Ingresa un <strong>Nombre de Usuario </strong>"Username", <strong>Contraseña </strong>"Password" y <strong>Confirmacion de Contraseña</strong> "Password confirmation" apropiado para tu usuario de prueba.</li>
+ <li>Presiona <strong>Save </strong>"Guardar" para crear el usuario.<br>
+ <br>
+ El sitio de administrador creara el nuevo usuario e inmediatamente te llevara a la pantalla de <em>Change user  </em>"Cambios del usuario" donde puedes cambiar tu <strong>nombre de usuario</strong> "Username" y agregar informacion para los campos opcionales del modelo de Usuario "User". Estos campos incluyen el primer nombre "first name", el apellido "last name", la direcion de correo electronico "email adress", los estados de los usuarios y sus permisos "users status and permissions" (solo el indicador <strong>Active </strong>"Activo" deberia ser activado). Mas abajo puedes especificar los grupos y permisos del usuario, y ver datos importantes relacionados a el usuario (ej: la fecha en que se agrego y la fecha del ultimo inicio de sesion)</li>
+ <li><img alt="Admin site - add user pt2" src="https://mdn.mozillademos.org/files/14097/admin_authentication_add_user_prt2.png" style="border-style: solid; border-width: 1px; display: block; height: 635px; margin: 0px auto; width: 800px;"></li>
+ <li>En la seccion <em>Groups</em> "Grupos", selecciona el grupo <strong>Library Member</strong> de  la lista de grupos disponibles, y entonces presiona la <strong>la flecha apuntando a la derecha </strong>entre las dos cajas para moverlo dentro de la caja de <em>Chosen groups </em>"Grupos seleccionados".<img alt="Admin site - add user to group" src="https://mdn.mozillademos.org/files/14099/admin_authentication_user_add_group.png" style="border-style: solid; border-width: 1px; display: block; height: 414px; margin: 0px auto; width: 933px;"></li>
+ <li>Aqui no necesitamos hacer nada adicional, entonces de nuevo solo seleciona <strong>SAVE</strong> "Guardar", para ir a la lista de usuarios.</li>
+</ol>
+
+<p>¡Esta hecho! Ahora tienes la cuenta de un miembro normal de la libreria, el cual estara disponible para ser usado en tus pruebas (una vez que hayamos implementado las paginas para permitirles iniciar sesion).</p>
+
+<div class="note">
+<p><strong>Nota</strong>: Deberias intentar crear otro usuario miembro de <em>library.  </em>Al igual que un grupo para los bibliotecarios <em>"Librarians",  </em>¡y agregar usuarios a este tambien! </p>
+</div>
+
+<h2 id="Configurando_nuestras_vistas_de_autenticación">Configurando nuestras vistas de autenticación</h2>
+
+<p>Django provee todo lo necesario para crear las páginas de autenticación para manejar inicio y cierre de sesión y gestión de contraseñas "fuera de la caja". Esto incluye un mapeador de URL, vistas "views" y formularios "forms", pero no incluye las plantillas "templates", ¡Tenemos que crear las nuestras!</p>
+
+<p>En esta sección, mostramos cómo integrar el sistema por defecto en el sitio web de <em>BibliotecaLocal </em>y crear plantillas "templates". Las incluiremos en las URLs principales del proyecto.</p>
+
+<div class="note">
+<p><strong>Nota</strong>: No tienes que usar nada de este código, pero es probable que quieras hacerlo porque hace las cosas mucho más fáciles. Seguramente necesitará cambiar el código de manejo de formularios si cambia su modelo de usuario (¡un tema avanzado!) pero aun asi, todavía podrá usar las funciones de vista de stock.</p>
+</div>
+
+<div class="note">
+<p><strong>Nota: </strong>En este caso podríamos razonablemente poner las páginas de autenticación, incluyendo las direcciones URL y plantillas, dentro de nuestra aplicación de catálogo. Sin embargo, si tuviéramos varias aplicaciones, sería mejor separar este comportamiento de inicio de sesión compartido y tenerlo disponible en todo el sitio, ¡así que eso es lo que hemos mostrado aquí!</p>
+</div>
+
+<h3 id="URLs_del_proyecto">URL's del proyecto</h3>
+
+<p>Añade el siguiente codigo al final del archivo url.py del proyecto (<strong>locallibrary/locallibrary/urls.py</strong>) :</p>
+
+<pre class="brush: python notranslate">#Add Django site authentication urls (for login, logout, password management)
+urlpatterns += [
+ path('accounts/', include('django.contrib.auth.urls')),
+]
+</pre>
+
+<p>Navega hasta la URL <a href="http://127.0.0.1:8000/accounts/">http://127.0.0.1:8000/accounts/</a> (¡Nota la barra inclinada hacia adelante!) y Django mostrara un error, diciendo que no puede encontrar esta URL, y listando todas las URL's que ha intentado. Aqui puedes ver las URL's que funcionaran, por ejemplo:</p>
+
+<div class="note">
+<p><strong>Nota: </strong>Usando el metodo anterior, añade las siguientes URL's con sus respectivos nombres entre corchetes, los cuales pueden ser usados para revertir "reverse" el mapeado de las URL's.  No necesitas implementar nada mas, el anterior mapeado de URL's asigna automaticamente las mencionadas URL's.</p>
+</div>
+
+<div class="note">
+<pre class="brush: python notranslate">^accounts/login/$ [name='login']
+^accounts/logout/$ [name='logout']
+^accounts/password_change/$ [name='password_change']
+^accounts/password_change/done/$ [name='password_change_done']
+^accounts/password_reset/$ [name='password_reset']
+^accounts/password_reset/done/$ [name='password_reset_done']
+^accounts/reset/(?P&lt;uidb64&gt;[0-9A-Za-z_\-]+)/(?P&lt;token&gt;[0-9A-Za-z]{1,13}-[0-9A-Za-z]{1,20})/$ [name='password_reset_confirm']
+^accounts/reset/done/$ [name='password_reset_complete']</pre>
+</div>
+
+<p>Ahora intenta navegar a la URL de inicio de sesion "login"(<a href="http://127.0.0.1:8000/accounts/login/">http://127.0.0.1:8000/accounts/login/</a>). Esto fallara de nuevo, pero ahora con un error diciendote que no encuentra la plantilla "template" requerida (<strong>registration/login.html</strong>) por el buscador de directorios de plantillas . Veras el las siguientes lineas en la seccion amarilla en la parte superior:</p>
+
+<pre class="brush: python notranslate">Exception Type: TemplateDoesNotExist
+Exception Value: <strong>registration/login.html</strong></pre>
+
+<p>El siguiente paso es crear un directorio de registro en la busqueda de directorios y entonces agregar el archivo <strong>login.html.</strong></p>
+
+<h3 id="Directorio_de_plantilla_template">Directorio de plantilla "template"</h3>
+
+<p>Las URL's (y vistas "views" implicitas) que recien hemos añadido esperan encontrar sus plantillas "templates" asociadas en un directorio "<strong>/registration/"</strong> en algún lugar de la ruta de búsqueda de plantillas</p>
+
+<p>Para este sitio pondremos nuestra pagina HTML en el directorio <strong>"templates/registration/". </strong>Este directorio debera estar en el directorio raiz de tu proyecto, es decir, el mismo directorio de las carpetas donde estan <strong>catalog </strong>y <strong>locallibrary</strong>. Por favor ahora crea las carpetas "templates" y dentro de esta "registration".</p>
+
+<div class="note">
+<p><strong>Note:</strong> Your folder structure should now look like the below:<br>
+ locallibrary (django project folder)<br>
+    |_catalog<br>
+    |_locallibrary<br>
+    |_templates <strong>(new)</strong><br>
+                 |_registration</p>
+</div>
+
+<p>Para hacer estos directorios visibles al cargador de plantillas (es decir introducir este directorio en el buscador de directorios de plantillas) abre el archivo de configuracion del proyecto setting.py (<strong>/locallibrary/locallibrary/settings.py)</strong>, y actualiza la seccion de <strong>TEMPLATES </strong>con la linea <strong>'DIRS'</strong> como se muestra a continuacion.</p>
+
+<pre class="brush: python notranslate">TEMPLATES = [
+ {
+ ...
+<strong> 'DIRS': ['./templates',],</strong>
+ 'APP_DIRS': True,
+ ...
+</pre>
+
+<h3 id="Plantilla_inicio_de_sesión_login">Plantilla inicio de sesión "login"</h3>
+
+<div class="warning">
+<p><strong>Importante</strong>: Las plantillas de autenticacion provista en este articulo son versiones muy basicas y ligeramete modificadas de las plantillas de inicio de sesion de demostracion de Django. ¡Necesitas personalizarlos para tus propios usos!</p>
+</div>
+
+<p>Crea un nuevo archivo HTML llamado /<strong>locallibrary/templates/registration/login.html</strong>. suministrado en el siguiente contenido :</p>
+
+<pre class="brush: html notranslate">{% extends "base_generic.html" %}
+
+{% block content %}
+
+{% if form.errors %}
+&lt;p&gt;Your username and password didn't match. Please try again.&lt;/p&gt;
+{% endif %}
+
+{% if next %}
+    {% if user.is_authenticated %}
+    &lt;p&gt;Your account doesn't have access to this page. To proceed,
+    please login with an account that has access.&lt;/p&gt;
+    {% else %}
+    &lt;p&gt;Please login to see this page.&lt;/p&gt;
+    {% endif %}
+{% endif %}
+
+&lt;form method="post" action="{% url 'login' %}"&gt;
+{% csrf_token %}
+
+&lt;div&gt;
+  &lt;td&gt;\{{ form.username.label_tag }}&lt;/td&gt;
+  &lt;td&gt;\{{ form.username }}&lt;/td&gt;
+&lt;/div&gt;
+&lt;div&gt;
+  &lt;td&gt;\{{ form.password.label_tag }}&lt;/td&gt;
+  &lt;td&gt;\{{ form.password }}&lt;/td&gt;
+&lt;/div&gt;
+
+&lt;div&gt;
+  &lt;input type="submit" value="login" /&gt;
+  &lt;input type="hidden" name="next" value="\{{ next }}" /&gt;
+&lt;/div&gt;
+&lt;/form&gt;
+
+{# Assumes you setup the password_reset view in your URLconf #}
+&lt;p&gt;&lt;a href="{% url 'password_reset' %}"&gt;Lost password?&lt;/a&gt;&lt;/p&gt;
+
+{% endblock %}</pre>
+
+<p id="sect1">Estas plantillas comparten algunas similitudes con algunas que hemos visto antes — extiende nuestra plantilla base y sobreescribe el bloque <code>content</code>. El resto del código es un código de manejo de formularios bastante estándar, que trataremos en un tutorial posterior. Todo lo que necesitas saber por ahora es que esto mostrará un formulario en el que puedes introducir tu usuario y contraseña, y que si introduces valores inválidos se te pedirá que ingreses los valores correctos cuando la página refresque.</p>
+
+<p>Navega de vuelta a la página de inicio sesión (<a href="http://127.0.0.1:8000/accounts/login/">http://127.0.0.1:8000/accounts/login/</a>) una vez que hayas guardado tu plantilla, y deberías ver algo como esto:</p>
+
+<p><img alt="Library login page v1" src="https://mdn.mozillademos.org/files/14101/library_login.png" style="border-style: solid; border-width: 1px; display: block; height: 173px; margin: 0px auto; width: 441px;"></p>
+
+<p>Si intentas iniciar sesión tendrá éxito y serás redirigido a otra página (por defecto será <a href="http://127.0.0.1:8000/accounts/profile/">http://127.0.0.1:8000/accounts/profile/</a>). El problema aquí es que, por defecto, Django espera que después de iniciar sesión seas llevado a una página de perfil (que podrá ser el caso o no). Como no has definido esta página todavía, ¡obtendrás otro error!</p>
+
+<p>Abre la configuración del proyecto (<strong>/locallibrary/locallibrary/settings.py</strong>) y añade al final el texto de abajo. Ahora cuando inicies sesión deberías ser redirigido a la página de inicio por defecto.</p>
+
+<pre class="brush: python notranslate"># Redirect to home URL after login (Default redirects to /accounts/profile/)
+LOGIN_REDIRECT_URL = '/'
+</pre>
+
+<h3 id="Plantilla_cierre_de_sesión_logout">Plantilla cierre de sesión "logout"</h3>
+
+<p>Si navegas a la url de cierre de sesión (<a href="http://127.0.0.1:8000/accounts/logout/">http://127.0.0.1:8000/accounts/logout/</a>) verás un extraño comportamiento — tu usuario cerrará la sesión, pero serás llevado a la página de cierre de sesión del <strong>Administrador</strong>. Eso no es lo que quieres, aunque sólo sea porque el enlace de inicio de sesión de esa página te lleva a la pantalla del inicio de sesión del Administrador (y eso sólo está disponible a los usuarios que tienen el permiso <code>is_staff</code>).</p>
+
+<p>Crea y abre el fichero /<strong>locallibrary/templates/registration/logged_out.html</strong>. Copia en él el siguiente texto:</p>
+
+<pre class="brush: html notranslate">{% extends "base_generic.html" %}
+
+{% block content %}
+&lt;p&gt;Logged out!&lt;/p&gt;
+
+&lt;a href="{% url 'login'%}"&gt;Click here to login again.&lt;/a&gt;
+{% endblock %}</pre>
+
+<p>Esta plantilla es muy simple. Tan sólo muestra un mensaje informándote que has cerrado sesión, y provee un enlace que puedes pulsar para volver a la página de inicio de sesión. Si vas a la url de cierre de sesión otra vez, deberías ver esta página:</p>
+
+<p><img alt="Library logout page v1" src="https://mdn.mozillademos.org/files/14103/library_logout.png" style="border-style: solid; border-width: 1px; display: block; height: 169px; margin: 0px auto; width: 385px;"></p>
+
+<h3 id="Plantillas_de_reinicio_de_contraseña_Password_reset">Plantillas de reinicio de contraseña "Password reset"</h3>
+
+<p>El sistema de reinicio de contraseña usa el correo electrónico para enviar al usuario un enlace de reinicio. Necesitas crear formularios para obtener la dirección de correo electrónico del usuario, enviar el correo, permitirle introducir una nueva contraseña y tenerla en cuenta cuando todo el proceso se haya completado.</p>
+
+<p>Las siguientes plantillas pueden usarse como un punto de partida.</p>
+
+<h4 id="Formulario_de_reinicio_de_contraseña">Formulario de reinicio de contraseña</h4>
+
+<p>Este es el formulario para obtener la dirección del correo electrónico del usuario (para enviar el correo de reinicio de contraseña). Crea <strong>/locallibrary/templates/registration/password_reset_form.html</strong>, y establece el siguiente contenido:</p>
+
+<pre class="brush: html notranslate">{% extends "base_generic.html" %}
+{% block content %}
+
+&lt;form action="" method="post"&gt;{% csrf_token %}
+ {% if form.email.errors %} \{{ form.email.errors }} {% endif %}
+ &lt;p&gt;\{{ form.email }}&lt;/p&gt;
+ &lt;input type="submit" class="btn btn-default btn-lg" value="Reset password" /&gt;
+&lt;/form&gt;
+
+{% endblock %}
+</pre>
+
+<h4 id="Reinicio_de_contraseña_hecho">Reinicio de contraseña hecho</h4>
+
+<p>Este formulario es mostrado después de que tu dirección de correo electrónico haya sido recogida. Crea <strong>/locallibrary/templates/registration/password_reset_done.html</strong>, y establece el siguiente contenido:</p>
+
+<pre class="brush: html notranslate">{% extends "base_generic.html" %}
+{% block content %}
+&lt;p&gt;We've emailed you instructions for setting your password. If they haven't arrived in a few minutes, check your spam folder.&lt;/p&gt;
+{% endblock %}
+</pre>
+
+<h4 id="Correo_electrónico_de_reinicio_de_contraseña">Correo electrónico de reinicio de contraseña</h4>
+
+<p>Esta plantilla suministra el texto HTML del correo electrónico, y contiene el enlace de reseteo que enviaremos a los usuarios. Crea <strong>/locallibrary/templates/registration/password_reset_email.html</strong>, y establece el siguiente contenido:</p>
+
+<pre class="brush: html notranslate">Someone asked for password reset for email \{{ email }}. Follow the link below:
+\{{ protocol}}://\{{ domain }}{% url 'password_reset_confirm' uidb64=uid token=token %}
+</pre>
+
+<h4 id="Confirmación_de_reinicio_de_contraseña">Confirmación de reinicio de contraseña</h4>
+
+<p>Esta página es donde introduces una nueva contraseña después de pinchar el enlace en el correo electrónico de reinicio de contraseña. Crea <strong>/locallibrary/templates/registration/password_reset_confirm.html</strong>, y establece el siguiente contenido:</p>
+
+<pre class="brush: html notranslate">{% extends "base_generic.html" %}
+
+{% block content %}
+
+ {% if validlink %}
+ &lt;p&gt;Please enter (and confirm) your new password.&lt;/p&gt;
+ &lt;form action="" method="post"&gt;
+ &lt;div style="display:none"&gt;
+ &lt;input type="hidden" value="\{{ csrf_token }}" name="csrfmiddlewaretoken"&gt;
+ &lt;/div&gt;
+ &lt;table&gt;
+ &lt;tr&gt;
+ &lt;td&gt;\{{ form.new_password1.errors }}
+ &lt;label for="id_new_password1"&gt;New password:&lt;/label&gt;&lt;/td&gt;
+ &lt;td&gt;\{{ form.new_password1 }}&lt;/td&gt;
+ &lt;/tr&gt;
+ &lt;tr&gt;
+ &lt;td&gt;\{{ form.new_password2.errors }}
+ &lt;label for="id_new_password2"&gt;Confirm password:&lt;/label&gt;&lt;/td&gt;
+ &lt;td&gt;\{{ form.new_password2 }}&lt;/td&gt;
+ &lt;/tr&gt;
+ &lt;tr&gt;
+ &lt;td&gt;&lt;/td&gt;
+ &lt;td&gt;&lt;input type="submit" value="Change my password" /&gt;&lt;/td&gt;
+ &lt;/tr&gt;
+ &lt;/table&gt;
+ &lt;/form&gt;
+ {% else %}
+ &lt;h1&gt;Password reset failed&lt;/h1&gt;
+ &lt;p&gt;The password reset link was invalid, possibly because it has already been used. Please request a new password reset.&lt;/p&gt;
+ {% endif %}
+
+{% endblock %}
+</pre>
+
+<h4 id="Reinicio_de_contraseña_completado">Reinicio de contraseña completado</h4>
+
+<p>Este es el último paso de la plantilla de reinicio de contraseña, que es mostrada para notificarte cuando el reinicio de contraseña ha tenido éxito. Crea <strong>/locallibrary/templates/registration/password_reset_complete.html</strong>, y establece el siguiente contenido:</p>
+
+<pre class="brush: html notranslate">{% extends "base_generic.html" %}
+{% block content %}
+
+&lt;h1&gt;The password has been changed!&lt;/h1&gt;
+&lt;p&gt;&lt;a href="{% url 'login' %}"&gt;log in again?&lt;/a&gt;&lt;/p&gt;
+
+{% endblock %}</pre>
+
+<h3 id="Probando_las_nuevas_páginas_de_autenticación">Probando las nuevas páginas de autenticación</h3>
+
+<p>Ahora que has añadido la configuración URL y creado todas estas plantillas, ¡las páginas de autenticación ahora deberían funcionar!</p>
+
+<p>Puedes probar las nuevas páginas de autenticación intentando iniciar sesión y entonces cerrar sesión con tu cuenta de super administrador usando estas URLs:</p>
+
+<ul>
+ <li><a href="http://127.0.0.1:8000/accounts/login/">http://127.0.0.1:8000/accounts/login/</a></li>
+ <li><a href="http://127.0.0.1:8000/accounts/logout/">http://127.0.0.1:8000/accounts/logout/</a></li>
+</ul>
+
+<p>Serás capaz de probar la funcionalidad de reinicio de contraseña desde el enlace de la página de inicio de sesión. <strong>¡Ten cuidado con el hecho de que Django solamente enviará correos de reinicio a las direcciones (usuarios) que ya están almacenadas en la base de datos!</strong></p>
+
+<div class="note">
+<p><strong>Nota</strong>: El sistema de reinicio de contraseña requiere que tu sitio web soporte envío de correo, que está más allá del ámbito de este artículo, por lo que esta parte <strong>no funcionará todavía</strong>. Para permitir el testeo, establece la siguiente línea al final de tu fichero settings.py. Esto registra en la consola cualquier envío de correo electrónico (y así puedes copiar el enlace de reinicio de contraseña desde dicha consola).</p>
+
+<pre class="brush: python notranslate">EMAIL_BACKEND = 'django.core.mail.backends.console.EmailBackend'
+</pre>
+
+<p>Para más información, ver <a href="https://docs.djangoproject.com/en/1.10/topics/email/">Sending email</a> (Django docs).</p>
+</div>
+
+<h2 id="Probando_contra_usuarios_autenticados">Probando contra usuarios autenticados</h2>
+
+<p>Esta sección mira a lo que podemos hacer para controlar selectivamente el contenido que el usuario ve basado en si ha iniciado sesión o no.</p>
+
+<h3 id="Probando_en_plantillas">Probando en plantillas</h3>
+
+<p>Puedes obtener información en las plantillas sobre el usuario que actualmente ha iniciado sesión con la variable de plantillas <code>\{{ user }}</code> (esto se añade por defecto al contexto de la plantilla cuando configuras el proyecto como hicimos en nuestro esqueleto).</p>
+
+<p>Es típico que primero pruebes con la variable de plantilla <code>\{{ user.is_authenticated }}</code> para determinar si el usuario puede ver el contenido específico. Para demostrar esto, lo siguiente que haremos será actualizar nuestra barra lateral "sidebar" para mostrar un enlace de inicio de sesión "Login" si el usuario no ha iniciado sesión, y un cierre de sesión "Logout" en el caso de que sí la haya iniciado.</p>
+
+<p>Abre la plantilla base (<strong>/locallibrary/catalog/templates/base_generic.html</strong>) y copia el siguiente texto en el bloque <code>sidebar</code>, justamente antes de la etiqueta de plantilla <code>endblock</code> .</p>
+
+<pre class="brush: html notranslate"> &lt;ul class="sidebar-nav"&gt;
+
+ ...
+
+ <strong>{% if user.is_authenticated %}</strong>
+ &lt;li&gt;User: <strong>\{{ user.get_username }}</strong>&lt;/li&gt;
+ &lt;li&gt;&lt;a href="{% url 'logout'%}?next=\{{request.path}}"&gt;Logout&lt;/a&gt;&lt;/li&gt;
+ <strong>{% else %}</strong>
+ &lt;li&gt;&lt;a href="{% url 'login'%}?next=\{{request.path}}"&gt;Login&lt;/a&gt;&lt;/li&gt;
+ <strong>{% endif %} </strong>
+ &lt;/ul&gt;</pre>
+
+<p>Como puedes ver, usamos las etiquetas de plantilla <code>if</code>-<code>else</code>-<code>endif</code> para condicionar el texto mostrado basado en si <code>\{{ user.is_authenticated }}</code> es cierto o no. Si el usuario está autenticado, sabemos que tenemos un usuario válido, por lo que llamamos a <strong>\{{ user.get_username }} </strong>para mostrar su nombre.</p>
+
+<p>Creamos los enlaces URLs del inicio y del cierre de sesión usando la etiqueta de plantilla <code>url</code> y los nombres de las respectivas configuraciones de las URL. Nótese también cómo hemos añadido <code>?next=\{{request.path}}</code> al final de las URLs. Lo que esto hace es añadir el párametro URL <font face="Consolas, Liberation Mono, Courier, monospace">next</font> que contiene la dirección (URL) de la página <em>actual</em>, al final de la URL enlazada. Después de que el usuario haya iniciado o cerrado sesión con éxito, las vistas usarán el valor de este "<code>next</code>" para redirigir al usuario de vuelta a la página donde pincharon primeramente el enlace de inicio/cierre de sesión.</p>
+
+<div class="note">
+<p><strong>Nota</strong>: ¡Pruébalo! Si estás en la página de inicio y pinchas en la barra lateral "sidebar", después de que la operación se complete deberías acabar de vuelta en la misma página.</p>
+</div>
+
+<h3 id="Probando_en_vistas">Probando en vistas</h3>
+
+<p>Si estás usando vistas basadas en funciones, la forma más facil para restringir el acceso a tus funciones es aplicar el decorador <code>login_required</code> a tu función de vista, como se muestra más abajo. Si el usuario ha iniciado sesión entonces tu código de vista se ejecutará como normalmente lo hace. Si el usuario no ha iniciado sesión, se redirigirá a la URL de inicio de sesión definida en tu configuración de proyecto (<code>settings.LOGIN_URL</code>), pasando el directorio absoluto actual como el parámetro URL <code>next</code>. Si el usuario tiene éxito en el inicio de sesión entonces será devuelto a esta página, pero esta vez autenticado.</p>
+
+<pre class="brush: python notranslate">from django.contrib.auth.decorators import login_required
+
+@login_required
+def my_view(request):
+ ...</pre>
+
+<div class="note">
+<p><strong>Nota:</strong> ¡Tú puedes hacer el mismo tipo de cosas manualmente probando con <code>request.user.is_authenticated</code>, pero el decorador es mucho más conveniente!</p>
+</div>
+
+<p>De manera similar, la forma más fácil de restringir el acceso a los usuarios que han iniciado sesión en tus vistas basadas en clases es extender de <code>LoginRequiredMixin</code>. Necesitas declarar primeramente este <code>mixin </code>en la lista de super clases, antes de la clase de vista principal.</p>
+
+<pre class="brush: python notranslate">from django.contrib.auth.mixins import LoginRequiredMixin
+
+class MyView(LoginRequiredMixin, View):
+ ...</pre>
+
+<p>Esto tiene exactamente el mismo comportamiento de redirección que el decorador <code>login_required</code>. También puedes especificar una localización alternativa para redirigir al usuario si no están autenticados (<code>login_url</code>), y un nombre de parámetro URL en lugar de "<code>next</code>" para insertar el directorio absoluto actual (<code>redirect_field_name</code>).</p>
+
+<pre class="brush: python notranslate">class MyView(LoginRequiredMixin, View):
+ login_url = '/login/'
+ redirect_field_name = 'redirect_to'
+</pre>
+
+<p>Para detalles adicionales, echa un vistazo a <a href="https://docs.djangoproject.com/en/1.10/topics/auth/default/#limiting-access-to-logged-in-users">Django docs</a>.</p>
+
+<h2 id="Ejemplo_-_listando_los_libros_del_usuario_actual">Ejemplo - listando los libros del usuario actual</h2>
+
+<p>Ahora que sabemos cómo restringir una página a un usuario concreto, vamos a crear una vista de los libros que el usuario tiene prestados actualmente.</p>
+
+<p>Desafortunadamente, ¡todavía no tenemos una forma de pedir prestados los libros a los usuarios! Por eso, antes de que podamos crear la lista de libros vamos primeramente a extender el modelo <code>BookInstance</code> para dar soporte al concepto de pedir prestado y usar la aplicación del Administrador Djando para alquilar un número de libros a nuestro usuario de prueba.</p>
+
+<h3 id="Modelos">Modelos</h3>
+
+<p>Primero vamos a hacer posible para los usuarios tener una <code>BookInstance</code> en alquiler (ya tenemos un <code>status</code> y una fecha <code>due_back</code>, pero no tenemos todavía una asociación entre este modelo y un Usuario). Crearemos uno usando un campo <code>ForeignKey</code> (uno-a-muchos). También necesitaremos un mecanismo sencillo para probar si un libro alquilado está atrasado.</p>
+
+<p>Abre <strong>catalog/models.py</strong>, e importa el modelo <code>User</code> de <code>django.contrib.auth.models</code> (añade esto justamente debajo de la anterior línea de importación, arriba del todo del fichero, para que el <code>User</code> esté disponible para el posterior código del que hace uso):</p>
+
+<pre class="brush: python notranslate">from django.contrib.auth.models import User
+</pre>
+
+<p>Después añade el campo <code>borrower</code> al modelo <code>BookInstance</code>:</p>
+
+<pre class="brush: python notranslate">borrower = models.ForeignKey(User, on_delete=models.SET_NULL, null=True, blank=True)
+</pre>
+
+<p>Ya que estamos aquí, vamos a añadir una propiedad que podamos llamar desde nuestras plantillas para decir si una instancia particular de un libro está atrasada. Mientras que podríamos calcular esto en la misma plantilla, usar una propiedad (<a href="https://docs.python.org/3/library/functions.html#property">property</a>) como se muestra abajo será mucho más eficiente.</p>
+
+<pre class="brush: python notranslate">from datetime import date
+
+@property
+def is_overdue(self):
+ if self.due_back and date.today() &gt; self.due_back:
+ return True
+ return False</pre>
+
+<div class="note">
+<p><strong>Nota</strong>: Primeramente verificamos si la fecha <code>due_back</code> está vacía antes de realizar una comparación. Un campo vacío <code>due_back</code> provocaría a Django arrojar un error en lugar de mostrar la página: los valores vacíos no son comparables. ¡Esto no es algo que queramos para la experiencia de nuestros usuarios!</p>
+</div>
+
+<p>Ahora que hemos actualizado nuestros modelos, necesitaremos hacer migraciones actuales en el proyecto y entonces aplicar esas migraciones:</p>
+
+<pre class="brush: bash notranslate">python3 manage.py makemigrations
+python3 manage.py migrate
+</pre>
+
+<h3 id="Administrador">Administrador</h3>
+
+<p>Ahora abre <strong>catalog/admin.py</strong>, y añade el campo <code>borrower</code> a la clase <code>BookInstanceAdmin</code> en ambas <code>list_display</code> y <code>fieldsets</code> como se muestra abajo. Esto hará el campo visible en la sección de Administrador, por lo que podemos asignar un <code>User</code> a una <code>BookInstance</code> cuando lo necesitemos.</p>
+
+<pre class="brush: python notranslate">@admin.register(BookInstance)
+class BookInstanceAdmin(admin.ModelAdmin):
+    list_display = ('book', 'status'<strong>, 'borrower'</strong>, 'due_back', 'id')
+    list_filter = ('status', 'due_back')
+
+    fieldsets = (
+        (None, {
+            'fields': ('book','imprint', 'id')
+        }),
+        ('Availability', {
+            'fields': ('status', 'due_back'<strong>,'borrower'</strong>)
+        }),
+    )</pre>
+
+<h3 id="Alquilar_unos_pocos_libros">Alquilar unos pocos libros</h3>
+
+<p>Ahora que es posible alquilar libros a un usuario específico, ve y alquila un número de registros <code>BookInstance</code>. Establece su campo <code>borrowed</code> a tu usuario de prueba, establece el <code>status</code> "On loan" (en alquiler) y establece fechas de vencimiento tanto en el futuro como en el pasado.</p>
+
+<div class="note">
+<p><strong>Nota</strong>: No escribiremos el proceso, porque ¡ya sabes cómo usar el sitio de Administrador!</p>
+</div>
+
+<h3 id="Vista_en_alquiler">Vista en alquiler</h3>
+
+<p>Ahora añadiremos una vista para obtener la lista de todos los libros que han sido alquilados al usuario actual. Usaremos la misma vista de lista genérica basada en clases con la que estamos familiarizada, pero esta vez también importaremos y extenderemos de <code>LoginRequiredMixin</code>, por lo que solamente un usuario que ha iniciado sesión podrá llamar a esta vista. También elegiremos declarar una <code>template_name</code> en lugar de usar la de por defecto, porque quizás acabemos teniendo unas pocas listas diferentes de registros de BookInstance, con diferentes vistas y plantillas.</p>
+
+<p>Añade lo siguiente a catalog/views.py:</p>
+
+<pre class="brush: python notranslate">from django.contrib.auth.mixins import LoginRequiredMixin
+
+class LoanedBooksByUserListView(LoginRequiredMixin,generic.ListView):
+ """
+ Generic class-based view listing books on loan to current user.
+ """
+ model = BookInstance
+ template_name ='catalog/bookinstance_list_borrowed_user.html'
+ paginate_by = 10
+
+ def get_queryset(self):
+ return BookInstance.objects.filter(borrower=self.request.user).filter(status__exact='o').order_by('due_back')</pre>
+
+<p>Para restringir nuestra consulta a solamente los objetos BookInstance del usuario actual, vamos a reimplementar <code>get_queryset()</code> como se muestra abajo. Nótese que "o" es el código almacenado para "on loan" (en alquiler) y vamos a ordenar por la fecha <code>due_back</code> para que los elementos más antiguos se muestren primero.</p>
+
+<h3 id="Configuración_URL_para_libros_alquilados">Configuración URL para libros alquilados</h3>
+
+<p>Ahora abre <strong>/catalog/urls.py</strong> y añade la url <code>url()</code> apuntando a la vista anterior (puedes simplemente copiar el texto de abajo al final del fichero).</p>
+
+<pre class="brush: python notranslate">urlpatterns += [
+ url(r'^mybooks/$', views.LoanedBooksByUserListView.as_view(), name='my-borrowed'),
+]</pre>
+
+<h3 id="Plantilla_para_libros_alquilados">Plantilla para libros alquilados</h3>
+
+<p>Ahora todo lo que necesitamos hacer para esta página es añadir una plantilla. Primero, creamos el fichero plantilla <strong>/catalog/templates/catalog/bookinstance_list_borrowed_user.html</strong> y establecemos el siguiente contenido en ella:</p>
+
+<pre class="brush: python notranslate">{% extends "base_generic.html" %}
+
+{% block content %}
+ &lt;h1&gt;Borrowed books&lt;/h1&gt;
+
+ {% if bookinstance_list %}
+ &lt;ul&gt;
+
+ {% for bookinst in bookinstance_list %}
+ &lt;li class="{% if bookinst.is_overdue %}text-danger{% endif %}"&gt;
+ &lt;a href="{% url 'book-detail' bookinst.book.pk %}"&gt;\{{bookinst.book.title}}&lt;/a&gt; (\{{ bookinst.due_back }})
+ &lt;/li&gt;
+ {% endfor %}
+ &lt;/ul&gt;
+
+ {% else %}
+ &lt;p&gt;There are no books borrowed.&lt;/p&gt;
+ {% endif %}
+{% endblock %}</pre>
+
+<p>Esta plantilla es muy similar a esas que hemos creado previamente para los objetos <code>Book</code> y <code>Author</code>. La única "cosa" nueva aquí es que comprobamos el método que hemos añadido en el modelo <code>(bookinst.is_overdue</code>) y lo usamos para cambiar el color de los elementos atrasados.</p>
+
+<p>Cuando el servidor de desarrollo esté en ejecución, deberías ser capaz de ver la lista de los usuarios que han iniciado sesión en tu navegador en <a href="http://127.0.0.1:8000/catalog/mybooks/">http://127.0.0.1:8000/catalog/mybooks/</a>. Prueba esto con tu usuario iniciado en la sesión y cerrado en la sesión (en el segundo caso, deberías ser redirigido a la página de inicio).</p>
+
+<h3 id="Añadir_la_lista_a_la_barra_lateral">Añadir la lista a la barra lateral</h3>
+
+<p>El último paso es añadir un enlace para esta nueva página en la barra lateral "sidebar". Pondremos esto en la misma sección donde mostramos otra información para el usuario que ha iniciado la sesión.</p>
+
+<p>Abre la plantilla base (<strong>/locallibrary/catalog/templates/base_generic.html</strong>) y añade la línea en negrita a la barra lateral como se muestra.</p>
+
+<pre class="brush: python notranslate"> &lt;ul class="sidebar-nav"&gt;
+ {% if user.is_authenticated %}
+ &lt;li&gt;User: \{{ user.get_username }}&lt;/li&gt;
+<strong> &lt;li&gt;&lt;a href="{% url 'my-borrowed' %}"&gt;My Borrowed&lt;/a&gt;&lt;/li&gt;</strong>
+ &lt;li&gt;&lt;a href="{% url 'logout'%}?next=\{{request.path}}"&gt;Logout&lt;/a&gt;&lt;/li&gt;
+ {% else %}
+ &lt;li&gt;&lt;a href="{% url 'login'%}?next=\{{request.path}}"&gt;Login&lt;/a&gt;&lt;/li&gt;
+ {% endif %}
+ &lt;/ul&gt;
+</pre>
+
+<h3 id="¿Cómo_se_ve">¿Cómo se ve?</h3>
+
+<p>Cuando cualquier usuario ha iniciado sesión, verán el enlace <em>My Borrowed</em> (Mis Alquileres) en la barra lateral, y la lista de libros mostrados como se ve abajo (¡el primer libro no tiene fecha de vencimiento, que es un bug que esperamos arreglar en un tutorial posterior!).</p>
+
+<p><img alt="Library - borrowed books by user" src="https://mdn.mozillademos.org/files/14105/library_borrowed_by_user.png" style="border-style: solid; border-width: 1px; display: block; height: 215px; margin: 0px auto; width: 530px;"></p>
+
+<h2 id="Permisos">Permisos</h2>
+
+<p>Los permisos están asociados con los modelos, y definen las operaciones que pueden llevarse a cabo en un modelo instanciado por un usuario que tiene el permiso. Por defecto, Django automáticamente da los permisos <em>add</em>, <em>change</em>, and <em>delete</em> (añadir, cambiar y eliminar) a todos los modelos, que permiten a los usuarios con los permisos realizar las acciones asociadas a través del sitio de Administrador. Tú puedes definir tus propios permisos a los modelos y concedérselos a usuarios específicos. También puedes cambiar los permisos asociados con diferentes instancias del mismo modelo.</p>
+
+<p>Probar permisos en vistas y plantillas es muy similar a probar sobre el estado de autenticación (y, de hecho, probar un permiso también prueba una autenticación).</p>
+
+<h3 id="Modelos_2">Modelos</h3>
+
+<p>La definición de permisos está hecha en la sección del modelo "<code>class Meta</code>", usando el campo <code>permissions</code>. Puedes especificar tantos permisos como necesites en una tupla, cada permiso está definido a sí mismo en una tupla anidada que contiene el nombre del permiso y el valor mostrado del mismo. Por ejemplo, podríamos definir un permiso para permitir a un usuario marcar un libro que ya ha sido devuelto, como se muestra:</p>
+
+<pre class="brush: python notranslate">class BookInstance(models.Model):
+ ...
+  class Meta:
+  ...
+<strong> permissions = (("can_mark_returned", "Set book as returned"),) </strong> </pre>
+
+<p>Podríamos asignar el permiso a un grupo bibliotecario "Librarian" en el sitio de Administración.</p>
+
+<p>Abre <strong>catalog/models.py</strong>, y añade el permiso como se muestra arriba. Necesitarás volver a ejecutar tus migraciones (ejecutar <code>python3 manage.py makemigrations</code> y <code>python3 manage.py migrate</code>) para actualizar la base de datos de forma apropiada.</p>
+
+<h3 id="Plantillas">Plantillas</h3>
+
+<p>Los permisos del usuario actual están almacenados en una variable de plantilla llamada <code>\{{ perms }}</code>. Puedes comprobar si el usuario actual tiene un permiso particular usando el nombre de variable específico con la "app" asociada en Django — ej. <code>\{{ perms.catalog.can_mark_returned }}</code> será <code>True</code> (cierto) si el usuario tiene el permiso, y <code>False</code> (falso) en otro caso. De forma típica probamos el permiso usando la etiqueta de plantilla <code>{% if %}</code> como se muestra:</p>
+
+<pre class="brush: python notranslate">{% if perms.catalog.<code>can_mark_returned</code> %}
+ &lt;!-- We can mark a BookInstance as returned. --&gt;
+  &lt;!-- Perhaps add code to link to a "book return" view here. --&gt;
+{% endif %}
+</pre>
+
+<h3 id="Vistas">Vistas</h3>
+
+<p>Los permisos pueden ser probados en una vista de función usando el decorador <code>permission_required</code> o en una vista basada en clases usando el <code>PermissionRequiredMixin</code>. El patrón y el comportamiento son los mismos que para la autenticación de inicio de sesión, aunque desde luego podrías razonablemente tener que añadir múltiples permisos.</p>
+
+<p>Decorador de vista de funciones:</p>
+
+<pre class="brush: python notranslate">from django.contrib.auth.decorators import permission_required
+
+@permission_required('catalog.<code>can_mark_returned</code>')
+@permission_required('catalog.<code>can_edit</code>')
+def my_view(request):
+ ...</pre>
+
+<p>"Mixin" de permisos requeridos para vistas basadas en clases:</p>
+
+<pre class="brush: python notranslate">from django.contrib.auth.mixins import PermissionRequiredMixin
+
+class MyView(PermissionRequiredMixin, View):
+ permission_required = 'catalog.<code>can_mark_returned</code>'
+ # Or multiple permissions
+ permission_required = ('catalog.<code>can_mark_returned</code>', 'catalog.can_edit')
+ # Note that 'catalog.can_edit' is just an example
+ # the catalog application doesn't have such permission!</pre>
+
+<h3 id="Ejemplo">Ejemplo</h3>
+
+<p>Nosotros no actualizaremos la <em>LocalLibrary</em> aquí; ¡quizás en el siguiente tutorial!</p>
+
+<h2 id="Desafíate_a_ti_mismo">Desafíate a ti mismo</h2>
+
+<p>Anteriormente en este artículo te mostramos cómo crear una página para el usuario actual para listar los libros que había pedido prestado. El desafío ahora es crear una página similar que solamente sea visible para los bibliotecarios, que muestre todos los libros que hayan sido prestados, y que incluya el nombre de cada prestatario.</p>
+
+<p>Deberías ser capaz de seguir el mismo patrón que el de la otra vista. La principal diferencia es que necesitarás restringir la vista a solamente los bibliotecarios. Podrías hacer esto basándote en si es un miembro de los empleados (decorador de función: <code>staff_member_required</code>, variable de plantilla: <code>user.is_staff</code>) pero nosotros te recomendamos que en su lugar uses el permiso <code>can_mark_returned</code> y <code>PermissionRequiredMixin</code>, como se describe en la sección anterior.</p>
+
+<div class="warning">
+<p><strong>Importante</strong>: Recuerda no usar tu súper usuario para pruebas basadas en permisos (la comprobación de permisos siempre devuelve cierto para súper usuarios, ¡incluso si un permiso no ha sido definido todavía!). En su lugar, crea un usuario bibliotecario, y añade entonces la capacidad requerida.</p>
+</div>
+
+<p>Cuando hayas terminado, tu página debería verse algo parecida a la captura de pantalla de abajo.</p>
+
+<p><img alt="All borrowed books, restricted to librarian" src="https://mdn.mozillademos.org/files/14115/library_borrowed_all.png" style="border-style: solid; border-width: 1px; display: block; height: 283px; margin: 0px auto; width: 500px;"></p>
+
+<ul>
+</ul>
+
+<h2 id="Sumario">Sumario</h2>
+
+<p>Excelente trabajo — has creado un sitio web para que los miembros de la biblioteca puedan iniciar sesión y ver su propio contenido, y los bibliotecarios (con el permiso correcto) puedan usarlo para ver todos los libros alquilados y sus prestatarios. En este momento estamos todavía simplemente viendo contenido, pero los mismos principios y técnicas son usados cuando empiezas a modificar y añadir datos.</p>
+
+<p>En nuestro siguiente artículo observaremos cómo puedes usar los formularios de Django para recoger la entrada de datos del usuario, y entonces empezar a modificar algunos de nuestros datos almacenados.</p>
+
+<h2 id="Ver_también">Ver también</h2>
+
+<ul>
+ <li><a href="https://docs.djangoproject.com/en/1.10/topics/auth/">Autenticación de Usuario en Django</a> (Django docs)</li>
+ <li><a href="https://docs.djangoproject.com/en/1.10/topics/auth/default//">Usando el sistema por defecto de Autenticación de Django</a> (Django docs)</li>
+ <li><a href="https://docs.djangoproject.com/en/1.10/topics/class-based-views/intro/#decorating-class-based-views">Introducción a clases basadas en vistas &gt; Decoradores</a> (Django docs)</li>
+</ul>
+
+<p>{{PreviousMenuNext("Learn/Server-side/Django/Sessions", "Learn/Server-side/Django/Forms", "Learn/Server-side/Django")}}</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>
+
+<div id="gtx-trans" style="position: absolute; left: 170px; top: 8184.58px;">
+<div class="gtx-trans-icon"></div>
+</div>
diff --git a/files/es/learn/server-side/django/deployment/index.html b/files/es/learn/server-side/django/deployment/index.html
new file mode 100644
index 0000000000..ddcb7f5c4e
--- /dev/null
+++ b/files/es/learn/server-side/django/deployment/index.html
@@ -0,0 +1,672 @@
+---
+title: 'Tutorial de Django Parte 11: Desplegando Django a producción'
+slug: Learn/Server-side/Django/Deployment
+translation_of: Learn/Server-side/Django/Deployment
+---
+<div>{{LearnSidebar}}</div>
+
+<div>{{PreviousMenuNext("Learn/Server-side/Django/Testing", "Learn/Server-side/Django/web_application_security", "Learn/Server-side/Django")}}</div>
+
+<p class="summary">Ahora que has creado (y probado) un fantastico sitio web para la <a href="https://developer.mozilla.org/en-US/docs/Learn/Server-side/Django/Tutorial_local_library_website">Biblioteca Local</a>, querrás instalarlo en un servidor web público de manera que pueda ser accedido por el personal y los miembros de la biblioteca a través de Internet. Este artículo proporciona una visión general de cómo buscar un host para desplegar tu sitio web y de lo que necesitas hacer para conseguir que tu sitio esté listo en producción.</p>
+
+<table class="learn-box standard-table">
+ <tbody>
+ <tr>
+ <th scope="row">Requisitos previos:</th>
+ <td>Completar todos los tutoriales de los temas previos, incluyendo <a href="/en-US/docs/Learn/Server-side/Django/Testing">Django Tutorial Part 10: Testing a Django web application</a>.</td>
+ </tr>
+ <tr>
+ <th scope="row">Objetivo:</th>
+ <td>Aprender ¿cómo? y ¿dónde? puedes puedes desplegar una app de Django en producción.</td>
+ </tr>
+ </tbody>
+</table>
+
+<h2 id="Introducción">Introducción</h2>
+
+<p>Una vez que tu sitio este terminado (o lo suficientemente terminado como para iniciar una prueba pública) vas a necesitar alojarla en un lugar más público y accesible que tu computadora personal.</p>
+
+<p>Hasta ahora has estado trabajando en un entorno de desarrollo, usando <em>Django development web server </em>para compartir tu sitio con el navegador/red local, y corriendo tu sitio web con configuraciones (inseguras) de desarrollo que exponen la depuración y otra informacion privada. Antes de que puedas alojar tu sitio web externamente, lo primero que tendrás que hacer es:</p>
+
+<ul>
+ <li>Introducir algunos cambios en las configuraciones del proyecto.</li>
+ <li>Elegir un entorno para alojar la aplicacion Django.</li>
+ <li>Elegir un entorno para alojar cualquier archivo estático.</li>
+ <li>Configurar una infraestructura a nivel producción para servir tu sitio web.</li>
+</ul>
+
+<p>Este tutorial provee una guía de opciones para elegir un sitio de alojamiento, una breve descripción general de lo que necesitas hacer para preparar tu aplicación Django en producción, y un ejemplo práctico de cómo instalar el sitio web de LocalLibrary en el servicio de alojamiento en la nube de <a href="https://www.heroku.com/">Heroku</a>.</p>
+
+<h2 id="¿Qué_es_un_entorno_de_producción">¿Qué es un entorno de producción?</h2>
+
+<p>El entorno de producción es el entorno proporcionado por el servidor en el que correrá su sitio web para uso externo. El entorno incluye:</p>
+
+<ul>
+ <li>Equipos en los que el sitio web correrá.</li>
+ <li>Sistema operativo (p.e. Linux, Windows).</li>
+ <li>Lenguajes de programación y librerías sobre las que su sitio web está escrito.</li>
+ <li>Servidor web empleado para servir páginas y otros contenidos (p.e. Nginx, Apache).</li>
+ <li>Servidor de aplicaciones que transmite peticiones "dinámicas" entre su sitio web Django y el servidor web.</li>
+ <li>Bases de datos que su sitio web necesita.</li>
+</ul>
+
+<div class="note">
+<p><strong>Nota</strong>: Dependiendo de como esté configurado su entorno de producción, usted podría disponer también de un proxy inverso, balanceador de carga, etc.</p>
+</div>
+
+<p>El servidor podría estar ubicado en sus propias instalaciones y conectado a Internet a través de un enlace rápido, pero lo más común es utilizar un computador alojado "en la nube". Esto en realidad implica que su código es ejecutado en algún computador remoto (o posiblemente un computador "virtual") en el centro (o centros) de datos de su compañía de servicios de <em>hosting.</em> El servidor remoto normalmente ofrecerá un determinado nivel garantizado de recursos de computación (es decir, CPU, RAM, memoria de almacenamiento, etc) y de conectividad a Internet por un cierto precio.</p>
+
+<p>A este tipo de hardware de computación/comunicaciones accesible de forma remota se le denomina <em>Infrastructure as a Service </em>o<em> Infraestructura como Servicio (IaaS)</em>. Muchos proveedores de IaaS ofrecen la opción de preinstalar un sistema operativo en particular, sobre el cual se debe instalar el resto de componentes de su entorno de producción. Otros permiten seleccionar entornos plenamente configurados, con incluso configuraciones de Django y servidor web establecidas.</p>
+
+<div class="note">
+<p><strong>Nota</strong>: Los entornos pre-construidos le permiten establecer su sitio web de manera muy sencilla, ya que reducen el trabajo de configuración, pero las opciones disponibles podrían limitarle al uso de un servidor (u otros componentes) poco conocido, o podrían estar basados en versiones antiguas del Sistema Operativo. A menudo es preferible que uno mismo instale sus propios componentes, de manera que disponga de los que desee, y en el momento que necesite subir el nivel de prestaciones de alguna parte del sistema, tener cierta idea de por dónde empezar</p>
+</div>
+
+<p>Otros proveedores de <em>hosting</em> incluyen Django como parte de una <em>Plataform as a Service </em>o<em> Plataforma como Servicio (PaaS)</em>. En este tipo de <em>hosting</em> no necesita preocuparse de la mayor parte del entorno de producción (servidor web, servidor de aplicaciones, balanceadores de carga), dado que la plataforma host ya se ocupa de todo ello por usted (así como de casi todo lo necesario para escalar su aplicación). Esto hace el despliegue bastante sencillo, puesto que ya solo necesita concentrarse en su aplicación web y no en el resto de la infraestructura de servidor.</p>
+
+<p>Algunos desarrolladores elegirán la mayor flexibilidad ofrecida por una IaaS frente a una PaaS, mientras que otros valorarán el reducido coste general de mantenimiento y la mayor facilidad de escalado de PaaS. Cuando se está empezando, la instalación del sitio web en un sistema PaaS es mucho más sencilla, así que eso es lo que haremos en este tutorial. </p>
+
+<div class="note">
+<p><strong>Consejo:</strong> Si eliges un proveedor de hosting adaptado a Python/Django, éste debería facilitar instrucciones de cómo instalar un sitio web Django usando diferentes configuraciones de servidor web, servidor de aplicaciones, proxy inverso, etc (esto es irrelevante si eliges una PaaS). Por ejemplo, existen muchas guías paso-a-paso para distintas configuraciones en la <a href="https://www.digitalocean.com/community/tutorials?q=django">Digital Ocean Django community docs</a>.</p>
+</div>
+
+<h2 id="Eligiendo_un_proveedor_de_hosting">Eligiendo un proveedor de hosting</h2>
+
+<p>Existen más de 100 proveedores de hosting de los que se sabe que, o bien dan soporte activo, o funcionan bien con Django (puedes encontrar una lista bastante extensa en <a href="http://djangofriendly.com/hosts/">Djangofriendly hosts</a>). Estos proveedores proporcionan diferentes tipos de entornos (IaaS, PaaS), así como diferentes niveles de recursos de computación y comunicaciones a diferentes precios.</p>
+
+<p>Algunos aspectos a considerar al elegir un host son:</p>
+
+<ul>
+ <li>Una estimación de cómo de ocupado va a estar el sitio y el coste de los recursos de datos y computación requeridos para atender esa demanda.</li>
+ <li>Nivel de soporte para el escalado tanto horizontal (añadir más máquinas) como vertical (subir de nivel con máquinas más potentes), y el coste que ello supone.</li>
+ <li>Dónde están ubicados los centros de datos de los proveedores y, por consiguientte, a cuáles será probablemente más rápido el acceso.</li>
+ <li>Los históricos de permanencia en actividad e inactividad del host.</li>
+ <li>Herramientas proporcionadas para la gestión del sitio  — si son fáciles de usar y si son seguras (Por ejemplo, SFTP frente a FTP). </li>
+ <li>Frameworks incorporadas para poder monitorear tu servidor.</li>
+ <li>Limitaciones conocidas. Algunos hosts bloquearán deliberadamente ciertos servicios (por ejemplo, email). Otros ofrecerán solo un cierto número de horas de "tiempo vivo" en determinados niveles de precios, u ofrecerán solo una pequeña cantidad de almacenamiento.</li>
+ <li>Beneficios adicionales. Algunos proveedores pueden ofrecer de forma gratuita nombres de dominio y soporte para certificados SSL por los que, de otro modo, tendrías que pagar.</li>
+ <li>Si el nivel "gratuito" del que dependes expira al cabo de un tiempo, o si el coste de migrar a un nivel más caro puede implicar que sea más conveniente usar algún otro servicio desde el primer momento.</li>
+</ul>
+
+<p>La buena noticia cuando estás en los comienzos es que existen bastantes sitios que proporcionan entornos de computación de "evaluación", "desarrollo" o "de nivel aficionado" de forma gratuita. Se trata siempre de entornos bastantes limitados/restringidos en recursos, y debes estar precavido en que pueden expirar al cabo de un periodo de introducción. Son, no obstante, muy útiles para probar sitios con poco tráfico en un entorno real, y pueden proporcionar una migración sencilla contratando más recursos si el sitio alcanza más ocupación. Entre las opciones conocidas de esta categoría tenemos <a href="https://www.heroku.com/">Heroku</a>, <a href="https://www.pythonanywhere.com/">Python Anywhere</a>, <a href="http://docs.aws.amazon.com/awsaccountbilling/latest/aboutv2/billing-free-tier.html">Amazon Web Services</a>, <a href="https://azure.microsoft.com/en-us/pricing/details/app-service/">Microsoft Azure</a>, etc. </p>
+
+<p>Muchos proveedores disponen también de un nivel "básico" que proporciona niveles de capacidad de computación más útiles y con menos limitaciones. <a href="https://www.digitalocean.com/">Digital Ocean</a> y <a href="https://www.pythonanywhere.com/">Python Anywhere</a> son ejemplos de proveedores populares de hosting que ofrecen niveles básicos de computación relativamente baratos (en el rango de los 5 a los 10 $USD mensuales).</p>
+
+<div class="note">
+<p><strong>Nota:</strong>  Recuerda que el precio no es el único criterio de selección. Si tu sitio web tiene éxito, la escalabilidad puede convertirse en la consideración más importante.</p>
+</div>
+
+<h2 id="Preparando_tu_sitio_web_para_hacerlo_público">Preparando tu sitio web para hacerlo público</h2>
+
+<p>La <a href="/en-US/docs/Learn/Server-side/Django/skeleton_website">Django skeleton website</a> creada usando las herramientas <em>django-admin </em>y <em>manage.py</em> están configuradas para hacer más sencillo el desarrollo. Muchos de los ajustes del proyecto Django (especificados en <strong>settings.py</strong>) deberían ser distintos en producción, por razones tanto de seguridad como de rendimiento.</p>
+
+<div class="note">
+<p><strong>Consejo:</strong> Es bastante común disponer de un archivo <strong>settings.py</strong> separado en producción, e importar los ajustes sensibles desde un archivo aparte o desde una variable de entorno. Este archivo debería, por tanto, estar protegido, aún cuando el resto del código fuente esté disponible en un repositorio público.</p>
+</div>
+
+<p>Los ajustes críticos que debes comprobar son:</p>
+
+<ul>
+ <li><code>DEBUG</code>. Debería establecerse como <code>False</code> en producción (<code>DEBUG = False</code>). Así se evita que se muestre la traza de depuración sensible/confidencial y la información variable.</li>
+ <li><code>SECRET_KEY</code>. Es un valor aleatorio grande utilizado para la protección CRSF etc. Es importante que la clave utilizada en producción no esté en el control fuente ni accesible desde fuera del servidor de producción. La documentación Django sugiere que debería ser cargada desde una variable de entorno o leída desde un archivo de sólo servicio (<em>serve-only file</em>).</li>
+ <li>
+ <pre class="notranslate"># Read SECRET_KEY from an environment variable
+import os
+SECRET_KEY = os.environ['SECRET_KEY']
+
+#OR
+
+#Read secret key from a file
+with open('/etc/secret_key.txt') as f:
+ SECRET_KEY = f.read().strip()</pre>
+ </li>
+</ul>
+
+<p>Modifiquemos la aplicación <em>LocalLibrary</em> de manera que leamos nuestras variables <code>SECRET_KEY</code> y <code>DEBUG</code> desde variables de entorno si han sido definidas o, en otro caso, usar los valores por defecto del archivo de configuración.</p>
+
+<p><span>Abra </span><strong>/locallibrary/settings.py</strong><span>, deshabilite la configuración original de l </span><code>SECRET_KEY</code><span> y añada las nuevas líneas tal como se muestran abajo en <strong>negrita</strong>. Durante el desarrollo no se especificará ninguna variable de entorno para la clave, por lo que se usará el valor por defecto (no debería importar qué clave utilizas aquí, o si la clave tiene "fugas", dado que no la utilizarás en producción).</span></p>
+
+<pre class="brush: python notranslate"># SECURITY WARNING: keep the secret key used in production secret!
+# SECRET_KEY = 'cg#p$g+j9tax!#a3cup@1$8obt2_+&amp;k3q+pmu)5%asj6yjpkag'
+<strong>import os</strong>
+<strong>SECRET_KEY = os.environ.get('DJANGO_SECRET_KEY', 'cg#p$g+j9tax!#a3cup@1$8obt2_+&amp;k3q+pmu)5%asj6yjpkag')</strong>
+</pre>
+
+<p>A continuación, comenta el ajuste de <code>DEBUG</code> existente y añade la nueva línea que se muestra abajo.</p>
+
+<pre class="brush: python notranslate"># SECURITY WARNING: don't run with debug turned on in production!
+# DEBUG = True
+<strong>DEBUG = bool( os.environ.get('DJANGO_DEBUG', True) )</strong>
+</pre>
+
+<p>El valor de <code>DEBUG</code> será <code>True</code> por defecto, pero será <code>False</code> si el valor de la variable de entorno <code>DJANGO_DEBUG</code> se establece como una cadena vacía, es decir, <code>DJANGO_DEBUG=''</code>.</p>
+
+<div class="note">
+<p><strong>Nota</strong>: Sería más intuitivo si pudiéramos simplemente marcar y desmarcar la variable de entorno <code>DJANGO_DEBUG</code> a <code>True</code> y <code>False</code> directamente, en lugar de usar "cualquier cadena" o "cadena vacía" (respectivamente). Por desgracia, los valores de las variables de entorno son almacenados como cadenas de Python (<em>Python strings</em>), y la única cadena que se evalúa como <code>False</code> es la cadena vacía (por ejemplo, <code>bool('')==False</code>).</p>
+</div>
+
+<p><a href="https://docs.djangoproject.com/en/1.10/howto/deployment/checklist/">Deployment checklist</a> (Django docs) proporciona una lista de comprobación completa de ajustes que podrías querer cambiar. Puedes también sacar una lista de algunos de ellos usando el siguiente comando de terminal:</p>
+
+<pre class="brush: python notranslate">python3 manage.py check --deploy
+</pre>
+
+<h2 id="Ejemplo_Instalando_LocalLibrary_en_Heroku">Ejemplo: Instalando LocalLibrary en Heroku</h2>
+
+<p>Esta sección aborda una demostración práctica de cómo instalar <em>LocalLibrary</em> en <a href="http://heroku.com">Heroku PaaS cloud</a>.</p>
+
+<h3 id="¿Por_qué_Heroku">¿Por qué Heroku?</h3>
+
+<p>Heroku es uno de los servicios PaaS basados en la nube más duraderos y conocidos. Originalmente únicamente daba soporte a aplicaciones Ruby, pero actualmente puede utilizarse para aplicaciones host de muchos entornos de programación, incluido Django!</p>
+
+<p>Vamos a elegir Heroku por varias razones:</p>
+
+<ul>
+ <li>Heroku tiene un nivel gratuito (<a href="https://www.heroku.com/pricing">free tier</a>) que es <em>verdaderamente</em> gratuito (aunque con algunas limitaciones).</li>
+ <li>Como PaaS, Heroku se hace cargo de gran parte de la infraestructura web por nosotros. Esto hace mucho más sencillos los comienzos, al no tener que preocuparnos por servidores, balanceadores de carga, proxys inversos, o cualquier otro aspecto de la infraestructura web, de los que Heroku se ocupa por nosotros en un segundo plano.</li>
+ <li>Aunque tenga algunas limitaciones, éstas no afectarán a nuestra aplicación particular. Por ejemplo:
+ <ul>
+ <li>Heroku sólo proporciona almacenamiento efímero, por lo que los archivos subidos por el usuario no pueden almacenarse de forma segura en el propio Heroku.</li>
+ <li>El nivel gratuito mantendrá dormida cualquier aplicación web inactiva que no haya tenido requerimientos dentro de un periodo de media hora. El sitio puede tardar varios segundos en responder cuando se le despierte.</li>
+ <li>El nivel gratuito limita el tiempo que el sitio puede estar en ejecución a cierta cantidad de horas al mes (sin contar el tiempo que el sitio permanece "dormido"). Esto está bien para un sitio de poco uso o de demostración, pero no es asumible si se necesita una disponibilidad del 100%. </li>
+ <li>Otras limitaciones se relacionan en <a href="https://devcenter.heroku.com/articles/limits">Limits</a> (Heroku docs).</li>
+ </ul>
+ </li>
+ <li>Lo principal es que funciona, y si te termina gustando, escalar tus aplicaciones será muy sencillo.</li>
+</ul>
+
+<p>Si bien Heroku es perfecto para alojar esta demostración, puede no serlo para tu sitio web real. Heroku facilita la instalación y el escalado, a costa de ser menos flexible, y potencialmente mucho más caro una vez que abandonas el nivel gratuito.</p>
+
+<h3 id="¿Cómo_funciona_Heroku">¿Cómo funciona Heroku?</h3>
+
+<p>Heroku ejecuta sitios web Django dentro de uno o más "<a href="https://devcenter.heroku.com/articles/dynos">Dynos</a>", que son contenedores Unix virtualizados y aislados que proporcionan el entorno necesario para ejecutar una aplicación. Los dynos están completamente aislados y disponen de un sistema de archivos <em>efímero</em> (sistema de archivos de vida corta que es limpiado/vaciado cada vez que el dyno se reinicia). Lo único que los dynos comparten por defecto son las variables de configuración (<a href="https://devcenter.heroku.com/articles/config-vars">configuration variables</a>) de las aplicaciones. Heroku utiliza interrnamente un balanceador de carga para distribuir el tráfico web entre todas las dynos "web". Puesto que no se comparte nada entre ellas, Heroku puede escalar una app horizontalmente simplemente añadiendo más dynos (aunque, claro está, podrías necesitar escalar tu base de datos para aceptar conexiones adicionales).</p>
+
+<p>Puesto que el sistema de archivos es efímero, no puedes instalar los servicios requeridos por tu aplicación directamente (por ejemplo, bases de datos, colas, sistemas de cacheado, almacenamiento, servicios de correo electrónico, etc). En su lugar, las aplicaciones web de Heroku usan servicios de respaldo proporcionados por Heroku o terceros como "add-ons" independientes. Una vez adjuntados a tu aplicación web, los dynos acceden a los servicios usando la información contenida en las variables de configuración de la aplicación.</p>
+
+<p>Para ejecutar tu aplicación, Heroku necesita poder instalar el entorno y las dependencias adecuados, y entender cómo están enlazados. Para las apps Django, esta información se proporciona en una serie de archivos de texto:</p>
+
+<ul>
+ <li><strong>runtime.txt</strong>:<strong> </strong>el lenguaje de programación y la versión a utilizar.</li>
+ <li><strong>requirements.txt</strong>: dependiencias de los componentes de Python, incluyendo a Django.</li>
+ <li><strong>Procfile</strong>: Lista de procesos que han de ejecutarse para arrancar la aplicación web. Para Django, esto será normalmente el servidor de aplicaciones web Gunicorn (con un script .wsgi).</li>
+ <li><strong>wsgi.py</strong>: Configuración <a href="http://wsgi.readthedocs.io/en/latest/what.html">WSGI</a> para invocar a nuestra aplicación Django en el entorno Heroku.</li>
+</ul>
+
+<p>Los desarrolladores interactúan con Heroku usando una app/terminal cliente especial, algo muy parecido a un bash script de Unix. Esto permite subir código almacenado en un repositorio git, inspeccionar los procesos en ejecución, ver logs, establecer variables de configuración, y mucho más!.</p>
+
+<p>Para conseguir nuestra aplicación para trabajar en Heroku, necesitaremos colocar nuestra aplicación web Django dentro de un repositorio git, añadir los archivos anteriores, integrar con una base de datos add-on, y hacer cambios para manejar correctamente los archivos estáticos.</p>
+
+<p>Una vez hecho todo eso, podemos crear una cuenta Heroku, obtener el cliente Heroku, y usarlo para instalar nuestro sitio web.</p>
+
+<div class="note">
+<p><strong>Nota: </strong>Las instrucciones indicadas abajo reflejan la forma de trabajar con Heroku en el momento de la redacción. Si Heroku cambia sus procesos de forma significativa, podrías preferir, en su lugar, revisar su documentación de instalación: <a href="https://devcenter.heroku.com/articles/getting-started-with-python#introduction">Getting Started on Heroku with Django</a>.</p>
+</div>
+
+<p>Con esto ya tienes una visión general de lo que necesitas para empezar (vea <a href="https://devcenter.heroku.com/articles/how-heroku-works">How Heroku works</a> para tener una guía más exhaustiva).</p>
+
+<h3 id="Creando_un_repositorio_de_aplicación_en_Github">Creando un repositorio de aplicación en Github</h3>
+
+<p>Heroku está estrechamente integrado con el sistema de control de versiones de código fuente <strong>git</strong>, usándolo para subir/sincronizar cualquier cambio que hagas en los sistemas activos. Esto se hace añadiendo un nuevo repositorio "remoto" heroku denominado <em>heroku</em> que apunta a un repositorio para tu fuente en la nube Heroku. Durante el desarrollo usas git para almacenar los cambios en tu repositorio "maestro". Cuando quieras desplegar tu sitio, sincronizas tus cambios con el repositorio Heroku.</p>
+
+<div class="note">
+<p><strong>Nota:</strong> Si estás acostumbrado a seguir buenas prácticas de desarrollo de software, probablemente ya estás usando git o algún otro sistema SCM. Si ya dispones de un repositorio git, podrás saltarte este paso.</p>
+</div>
+
+<p>Existen muchas formas de trabajar con git, pero una de las más sencillas es crear en primer lugar una cuenta en <a href="https://github.com/">Github</a>, crear allí el repositorio, y a continuación sincronizarlo localmente:</p>
+
+<ol>
+ <li>Visita <a href="https://github.com/">https://github.com/</a> y crea una cuenta.</li>
+ <li>Una vez conectado, haz click en el enlace  <strong>+</strong> de la barra de tareas superior y selecciona <strong>New repository</strong>.</li>
+ <li>Rellena todos los campos de este formulario. Aunque no son obligatorios, es muy recomendable que los rellenes todos.
+ <ul>
+ <li>Introduce el nombre del nuevo repositorio (por ejemplo, <em>django_local_library</em>), y una descripción (por ejemplo "Sitio web de la Biblioteca Local escrita en Django").</li>
+ <li>Selecciona <strong>Python</strong> en la lista de selección <em>Add .gitignore.</em></li>
+ <li>Selecciona tu licencia en la lista de selección <em>Add license</em>.</li>
+ <li>Marca <strong>Initialize this repository with a README</strong>.</li>
+ </ul>
+ </li>
+ <li>Pulsa <strong>Create repository</strong>.</li>
+ <li>Haz click en el botón verde "<strong>Clone or download</strong>" en la página de tu nuevo repositorio.</li>
+ <li>Copia el valor de la URL del campo de texto situado dentro de la caja de diálogo que aparece (debería decir algo como: <strong>https://github.com/<em>&lt;your_git_user_id&gt;</em>/django_local_library.git</strong>).</li>
+</ol>
+
+<p>Ahora que el repositorio ("repo") ha sido creado, querremos clonarlo en nuestra computadora local:</p>
+
+<ol>
+ <li>Instala <em>git</em> para tu computadora local (puedes encontrar versiones para distintas plataformas <a href="https://git-scm.com/downloads">here</a>).</li>
+ <li>Abre una ventana/terminal de comandos y clona tu repositorio usando la URL que copiaste anteriormente:
+ <pre class="brush: bash notranslate">git clone https://github.com/<strong><em>&lt;your_git_user_id&gt;</em></strong>/django_local_library.git
+</pre>
+ Esto creará el repositorio debajo del punto actual.</li>
+ <li>Navega dentro del nuevo repositorio.
+ <pre class="brush: bash notranslate">cd django_local_library.git</pre>
+ </li>
+</ol>
+
+<p>El paso final es copiar en él tu aplicación y a continuación añadir los archivos a tu repositorio usando git:</p>
+
+<ol>
+ <li>Copia tu aplicación Django en esta carpeta (todos los archivos que estén al mismo nivel que <strong>manage.py</strong> y por debajo, <strong>no</strong> su carpeta locallibrary contenedora). </li>
+ <li>Abre el archivo <strong>.gitignore</strong>, copia las siguientes líneas al final del mismo, y guárdalo (este archivo se utiliza para identificar los archivos que, por defecto, no deberían subirse a git).
+ <pre class="notranslate"># Text backup files
+*.bak
+
+#Database
+*.sqlite3</pre>
+ </li>
+ <li>Abre una ventana/terminal de comandos y utiliza el comando <code>add</code> para añadir todos los archivos a git.
+ <pre class="brush: bash notranslate">git add -A
+</pre>
+ </li>
+ <li>Utiliza el comando status para comprobar que todos los archivos que vas a añadir son correctos (quieres incluir ficheros fuentes, no binarios, archivos temporales, etc). Debería tener un aspecto similar a la lista siguiente.
+ <pre class="notranslate">&gt; git status
+On branch master
+Your branch is up-to-date with 'origin/master'.
+Changes to be committed:
+  (use "git reset HEAD &lt;file&gt;..." to unstage)
+
+        modified:   .gitignore
+        new file:   catalog/__init__.py
+ ...
+        new file:   catalog/migrations/0001_initial.py
+ ...
+        new file:   templates/registration/password_reset_form.html</pre>
+ </li>
+ <li>Si estás conforme, consolida tus archivos en el repositorio local:
+ <pre class="brush: bash notranslate">git commit -m "First version of application moved into github"</pre>
+ </li>
+ <li>A continuación, sincroniza tu repositorio local con el sitio web Github, usando lo siguiente:
+ <pre class="notranslate">git push origin master</pre>
+ </li>
+</ol>
+
+<p>Una vez completada esta operación, deberías poder regresar a la página de Github donde creaste tu repositorio, refrescar la página, y comprobar que tu toda tu aplicación ha sido ya cargada. Puedes continuar actualizando tu repositorio según vayan cambiando los archivos, usando este ciclo add/commit/push.</p>
+
+<div class="note">
+<p><strong>Consejo:</strong> Este es un buen momento para hacer una copia de seguridad de tu proyecto "simple" — algunos de los cambios que vamos a ir haciendo en las siguientes secciones podrían ser útiles para el despliegue en cualquier plataforma (o para el desarrollo), pero otros no.</p>
+
+<p>La <em>mejor </em>manera de hacer esto es usar <em>git</em> para gestionar tus revisiones. Con <em>git</em> puedes no solo volver a una versión anterior en particular, sino que puedes mantener ésta en una "rama" separada de tus cambios en producción, y seleccionar determinados cambios a trasladar entre las ramas de producción y desarrollo. <a href="https://help.github.com/articles/good-resources-for-learning-git-and-github/">Learning Git</a> merece la pena el esfuerzo, pero queda fuera del alcance de este tema.</p>
+
+<p>La forma <em>más fácil </em>de hacer ésto es simplemente copiar tus archivos en otra ubicación. Usa la manera que más se ajuste a tus conocimientos de git!</p>
+</div>
+
+<h3 id="Actualizar_la_app_para_Heroku">Actualizar la app para Heroku</h3>
+
+<p>Esta sección explica los cambios que necesitaras hacer a nuestra aplicación <em>LocalLibrary</em> para ponerla a funcionar en Heroku. Mientras que las instrucciones disponibles en <a href="https://devcenter.heroku.com/articles/getting-started-with-python#introduction">Getting Started on Heroku with Django</a> de Heroku asumen que también vas a utilizar el cliente Heroku para ejecutar el entorno de desarrollo local, los cambios que aquí se reflejan son compatibles con el servidor de desarrollo Django existente y las formas de funcionamiento que ya hemos aprendido.</p>
+
+<h4 id="Procfile">Procfile</h4>
+
+<p>Crea el archivo <code>Procfile</code> (sin extensión) en la carpeta raíz de tu repositorio GitHub para declarar los tipos de procesos de la aplicación y los puntos de entrada. Copia en él el texto siguiente:</p>
+
+<pre class="notranslate">web: gunicorn locallibrary.wsgi --log-file -</pre>
+
+<p>La palabra "<code>web:</code>" le dice a Heroku que se trata de una web dyno y puede ser enviada a través del tráfico HTTP. El proceso a arrancar en este dyno es <em>gunicorn</em>, un servidor de aplicaciones web popular recomendado por Heroku. Arrancamos Gunicorn usando la información de configuración que se encuentra en el módulo <code>locallibrary.wsgi</code> (creado con nuestro esqueleto de aplicación: <strong>/locallibrary/wsgi.py</strong>).</p>
+
+<h4 id="Gunicorn">Gunicorn</h4>
+
+<p><a href="http://gunicorn.org/">Gunicorn</a> es el servidor HTTP recomendado para usar con Django en Heroku (tal como se indicaba en el Procfile anterior). Es un servidor HTTP puro-Python para aplicaciones WSGI que puede ejecutar múltiples procesos Python concurrentes dentro de un único dyno (para obtener más información, véase <a href="https://devcenter.heroku.com/articles/python-gunicorn">Deploying Python applications with Gunicorn</a>).</p>
+
+<p>Aunque no necesitaremos <em>Gunicorn</em> para servir nuestra aplicación LocalLibrary durante el desarrollo, lo instalaremos de manera que sean parte de nuestros <a href="#requirements">requerimientos</a> de Heroku para instalar en el servidor remoto.</p>
+
+<p>Instala <em>Gunicorn</em> localmente usando <em>pip</em> en la línea de comandos (que instalamos en <a href="/en-US/docs/Learn/Server-side/Django/development_environment">setting up the development environment</a>):</p>
+
+<pre class="brush: bash notranslate">pip3 install gunicorn
+</pre>
+
+<h4 id="Configuración_de_la_Base_de_Datos">Configuración de la Base de Datos</h4>
+
+<p>No podemos usar la base de datos por defecto SQLite en Heroku dado que está basada-en-fichero, y sería borrada del sistema de archivos <em>efímero</em> cada que se reiniciara la aplicación (normalmente una vez al día, y cada vez que la aplicación o sus variables de configuración fueran modificadas).</p>
+
+<p>El mecanismo de Herocu para gestionar esta situación es usar una <a href="https://elements.heroku.com/addons#data-stores">database add-on</a> y configurar la aplicación web utilizando información de una <a href="https://devcenter.heroku.com/articles/config-vars">variable de configuración</a> del entorno, establecida por la add-on. Existen numerosas opciones de bases de datos, pero nosotros utilizaremos el <a href="https://devcenter.heroku.com/articles/heroku-postgres-plans#plan-tiers">nivel hobby tier</a> de la base de datos <em>Heroku postgres</em> ya que es gratuita, soportada por Django, e incorporada en nuestra nuevas apps Heroku al usar el nivel gratuito plan dyno hobby.</p>
+
+<p>La información de conexión a la base de datos es proporcionada a la web dyno usando una variable de configuración denominada <code>DATABASE_URL</code>. En lugar de codificar esta información en Django, Heroku recomienda que los desarrolladores utilicen el paquete <a href="https://warehouse.python.org/project/dj-database-url/">dj-database-url</a> para extraer la variable de entorno <code>DATABASE_URL</code> y automáticamente convertirla al formato de configuración deseado por Django. Además para instalar el paquete <em>dj-database-url</em> necesitaremos también instalar <a href="http://initd.org/psycopg/">psycopg2</a>, ya que Django lo necesita para interactuar con la base de datos Postgres.</p>
+
+<h5 id="dj-database-url_Configuración_de_base_de_datos_de_Django_a_partir_de_una_variable_de_entorno">dj-database-url (Configuración de base de datos de  Django a partir de una variable de entorno)</h5>
+
+<p>Instala <em>dj-database-url</em> a nivel local para que se convierta en parte de nuestros <a href="#requirements">requerimientos</a> para instalar Heroku en el servidor remoto:</p>
+
+<pre class="notranslate">$ pip3 install dj-database-url
+</pre>
+
+<h5 id="settings.py">settings.py</h5>
+
+<p>Abre <strong>/locallibrary/settings.py</strong> y copia la siguiente configuración al final del archivo:</p>
+
+<pre class="notranslate"># Heroku: Update database configuration from $DATABASE_URL.
+import dj_database_url
+db_from_env = dj_database_url.config(conn_max_age=500)
+DATABASES['default'].update(db_from_env)</pre>
+
+<div class="note">
+<p><strong>Nota:</strong></p>
+
+<ul>
+ <li>Nosotros seguiremos utilizando SQLite durante el desarrollo porque la variable de entorno <code>DATABASE_URL</code> no será fijada en nuestra computadora de desarrollo.</li>
+ <li>El valor <code>conn_max_age=500</code> hace que la conexión sea persistente, lo que es más eficiente que recrear la conexión en cada ciclo de petición. No obstante, esto es opcional y puede ser eliminado si es necesario.</li>
+</ul>
+</div>
+
+<h5 id="psycopg2_soporte_a_la_base_de_datos_Python_Postgres">psycopg2 (soporte a la base de datos Python Postgres)</h5>
+
+<p>Django necesita <em>psycopg2</em> para trabajar con las bases de datos Postgres y tú necesitarás añadir esto a los <a href="#requirements">requirements.txt</a> para que Heroku lo instale en el servidor remoto (como se expone más adelante en la sección de requerimientos).</p>
+
+<p>Django utilizará la base de datos SQLite en modo local por defecto, porque la variable de entorno <code>DATABASE_URL</code> no está establecida en nuestro entorno local. Si quieres cambiar a Postgres completamente y usar nuestra base de datos del nivel gratuito de Heroku tanto en desarrollo como en producción, puedes hacerlo. Por ejemplo, para instalar psycopg2 y sus dependencias localmente en un sistema basado en Linux, usarías los siguientes comandos bash/terminal:</p>
+
+<pre class="brush: bash notranslate"><code>sudo apt-get install python-pip python-dev libpq-dev postgresql postgresql-contrib</code>
+pip3 install psycopg2
+</pre>
+
+<p>Puedes encontrar instrucciones de instalación para el resto de plataformas en <a href="http://initd.org/psycopg/docs/install.html">psycopg2 website</a>.</p>
+
+<p>No obstante, esto no es necesario — no necesitas tener activa PostGreSQL en el equipo local, en tanto que se lo indicas a Heroku como requerimiento, en <code>requirements.txt</code> (ver a continuación).</p>
+
+<h4 id="Sirviendo_ficheros_estáticos_en_producción">Sirviendo ficheros estáticos en producción</h4>
+
+<p>Durante el desarrollo utilizábamos Django y el servidor web de desarrollo de Django para servir nuestros ficheros estáticos (CSS, JavaScript, etc). En un entorno de producción normalmente se sirven los ficheros estáticos desde una red de entrega de contenidos (CDN, Content Delivery Network) o desde el servidor web.</p>
+
+<div class="note">
+<p><strong>Nota:</strong> Servir ficheros estáticos vía Django/aplicación web es ineficiente ya que las peticiones tienen que pasar por código adicional innecesario (Django), en vez de ser gestionados directamente por el servidor web o una CDN completamente independiente. Si bien esto no tiene relevancia en el uso local durante el desarrollo, el uso de este mecanismo en producción tiene un significativo impacto de rendimiento. </p>
+</div>
+
+<p>Para facilitar el alojamiento de archivos estáticos de forma separada de la aplicación web Django, Django proporciona la herramienta <em>collectstatic</em> para recoger estos archivos para el despliegue (hay una variable de configuración que define de dónde se deben recopliar los archivos cuando se ejecuta <em>collectstatic</em>). Las plantillas Django hacen referencia a la localización de almacenamiento de los archivos estáticos en relación a una variable de configuración (<code>STATIC_URL</code>), por tanto, esto puede modificarse se los archivos estáticos son movidos a otro host/servidor.</p>
+
+<p>Las variables de configuración más relevantes son:</p>
+
+<ul>
+ <li><code>STATIC_URL</code>:  Es la localización URL base desde la cual se servirán los archivos estáticos, por ejemplo en una CDN. Se usa para variables de plantilla estáticas a las que se acceden en nuestra plantilla base (ver <a href="/en-US/docs/Learn/Server-side/Django/Home_page">Django Tutorial Part 5: Creating our home page</a>).</li>
+ <li><code>STATIC_ROOT</code>: Es la ruta absoluta a un directorio en el que la herramienta "collectstatic" de Django reunirá todos los archivos estáticos referenciados en nuestras plantillas. Una vez recopilados, podrán ser cargados como un grupo a donde hayan de ser alojados.</li>
+ <li><code>STATICFILES_DIRS</code>: Relaciona directorios adicionales en los que la herramienta collestatic de Django debería buscar archivos estáticos.</li>
+</ul>
+
+<h5 id="settings.py_2">settings.py</h5>
+
+<p>Abra <strong>/locallibrary/settings.py</strong> y copie la configuración siguiente al final del archivo. La variable <code>BASE_DIR</code> debería haber sido ya definida en tu fichero (la variable <code>STATIC_URL</code> puede haber sido ya definida dentro del archivo cuando fue creado. Puesto que no provocará ningún fallo, podrías borrar la referencias duplicadas).</p>
+
+<pre class="notranslate"># Static files (CSS, JavaScript, Images)
+# https://docs.djangoproject.com/en/1.10/howto/static-files/
+
+# The absolute path to the directory where collectstatic will collect static files for deployment.
+STATIC_ROOT = os.path.join(BASE_DIR, 'staticfiles')
+
+# The URL to use when referring to static files (where they will be served from)
+STATIC_URL = '/static/'
+</pre>
+
+<p>Nosotros haremos el servicio de ficheros utilizando una librería denominada <a href="https://warehouse.python.org/project/whitenoise/">WhiteNoise</a>, que instalaremos y configuraremos en la siguiente sección.</p>
+
+<p>Para más información, vea <a href="https://devcenter.heroku.com/articles/django-assets">Django and Static Assets</a> (Heroku docs).</p>
+
+<h4 id="Whitenoise">Whitenoise</h4>
+
+<p>Hay muchas formas de servir ficheros estáticos en producción (ya vimos los ajustes Django relevantes en las secciones previas). Heroku recomienda usar el proyecto <a href="https://warehouse.python.org/project/whitenoise/">WhiteNoise</a> para servir objetos estáticos directamente desde Gunicorn en producción.</p>
+
+<div class="note">
+<p><strong>Nota: </strong>Heroku llama automáticamente a <em>collectstatic</em> y prepara tus ficheros estáticos para ser usados por WhiteNoise después de que se cargue tu aplicación. Revisa la documentación <a href="https://warehouse.python.org/project/whitenoise/">WhiteNoise</a>, en la que se explica cómo funciona y por qué la implementación es un método para servir estos ficheros relativamente eficiente.</p>
+</div>
+
+<p>Los pasos para instalar <em>WhiteNoise</em> para usarlo dentro del proyecto son:</p>
+
+<h5 id="WhiteNoise">WhiteNoise</h5>
+
+<p>Instala WhiteNoise localmente usando el siguiente comando:</p>
+
+<pre class="notranslate">$ pip3 install whitenoise
+</pre>
+
+<h5 id="settings.py_3">settings.py</h5>
+
+<p>Para instalar <em>WhiteNoise</em> en tu aplicación Django, abre <strong>/locallibrary/settings.py</strong>, busca la opción <code>MIDDLEWARE</code> y añade <code>WhiteNoiseMiddleware</code> cerca de la parte superior de la lista, justo debajo de <code>SecurityMiddleware</code>:</p>
+
+<pre class="notranslate">MIDDLEWARE = [
+    'django.middleware.security.SecurityMiddleware',
+ <strong>'whitenoise.middleware.WhiteNoiseMiddleware',</strong>
+    'django.contrib.sessions.middleware.SessionMiddleware',
+    'django.middleware.common.CommonMiddleware',
+    'django.middleware.csrf.CsrfViewMiddleware',
+    'django.contrib.auth.middleware.AuthenticationMiddleware',
+    'django.contrib.messages.middleware.MessageMiddleware',
+    'django.middleware.clickjacking.XFrameOptionsMiddleware',
+]
+</pre>
+
+<p>Opcionalmente, puedes reducir el tamaño de los ficheros estáticos al ser servidos (lo que lo hace más eficiente). Añade lo siguiente al final de <strong>/locallibrary/settings.py</strong>:</p>
+
+<pre class="notranslate"># Simplified static file serving.
+# https://warehouse.python.org/project/whitenoise/
+STATICFILES_STORAGE = 'whitenoise.storage.CompressedManifestStaticFilesStorage'
+</pre>
+
+<h4 id="Requerimientos">Requerimientos</h4>
+
+<p>Los requerimientos Python de tu aplicación web deben ser almacenados en un archivo <strong>requirements.txt</strong> en la carpeta raíz de tu repositorio. Heroku los instalará cuando reconstruya tu entorno. Puedes crear este archivo utilizando <em>pip</em> en la línea de comandos (ejecuta los siguiente en la raíz del repositorio):</p>
+
+<pre class="brush: bash notranslate">pip3 freeze &gt; requirements.txt</pre>
+
+<p>Después de instalar todas las dependencias anteriores, tu archivo <strong>requirements.txt</strong> debería tener <em>al menos</em> estos objetos (aunque los números de versión pueden ser diferentes). Por favor, borra cualquier otra dependencia no listada abajo, a menos que la hayas añadido explícitamente para esta aplicación.</p>
+
+<pre class="notranslate">dj-database-url==0.4.1
+Django==1.10.2
+gunicorn==19.6.0
+<strong>psycopg2==2.6.2</strong>
+whitenoise==3.2.2
+</pre>
+
+<div class="note">
+<p>Asegúrate de que existe una línea <strong>psycopg2</strong> como la que se ve arriba! Incluso aunque no lo hayas instalado localmente, deberías añadirla a <strong>requirements.txt</strong>.</p>
+</div>
+
+<h4 id="Runtime">Runtime</h4>
+
+<p>El archivo <strong>runtime.txt</strong>, si ha sido definido, le dice a Heroku que lenguaje de programación usar. Crea el archivo en el raíz del repositorio y añade el siguiente texto:</p>
+
+<pre class="notranslate">python-3.5.2</pre>
+
+<div class="note">
+<p><strong>Nota:</strong> Heroku sólo soporta un número pequeño de <a href="https://devcenter.heroku.com/articles/python-support#supported-python-runtimes">Python runtimes</a>. Tú puedes especificar valores de runtime de Python 3, pero en el momento de esta redacción la versión anterior será soportada como definida.</p>
+</div>
+
+<h4 id="Guardar_los_cambios_en_Github_y_volver_a_probar.">Guardar los cambios en Github y volver a probar.</h4>
+
+<p>A continuacion, guardemos nuestros cambios en Github. En el terminal (dentro de nuestro respositorio), introduce los comandos siguientes:</p>
+
+<pre class="brush: python notranslate">git add -A
+git commit -m "Added files and changes required for deployment to heroku"
+git push origin master</pre>
+
+<p>Antes de continuar, probemos de nuevo nuestro sitio localmente y asegurémonos de que no ha sido afectado por ninguno de los cambios anteriores. Pon en marcha el servidor web de desarrollo de la forma habitual y comprueba que el sitio aún funciona como esperas en tu navegador.</p>
+
+<pre class="brush: bash notranslate">python3 manage.py runserver</pre>
+
+<p>Ya deberíamos estar preparados para empezar a desplegar LocalLibrary en Heroku.</p>
+
+<h3 id="Hazte_con_una_cuenta_Heroku">Hazte con una cuenta Heroku</h3>
+
+<p>Para empezar a usar Heroku necesitarás en primer lugar crear una cuenta:</p>
+
+<ul>
+ <li>Ve a <a href="https://www.heroku.com/">www.heroku.com</a> y haz click en el botón <strong>SIGN UP FOR FREE</strong>.</li>
+ <li>Introduce tus datos y pulsa a continuación <strong>CREATE FREE ACCOUNT</strong>. Se te pedirá que compruebes que has recibido un email de registro.</li>
+ <li>Haz click en el enlace de activación de la cuenta que aparece en el email de registro. Serás llevado de vuelta a tu cuenta en el navegador web.</li>
+ <li>Introduce tu contraseña y haz click en <strong>SET PASSWORD AND LOGIN</strong>.</li>
+ <li>Ya estarás contectado y serás llevado al tablón de Heroku (Heroku dashboard): <a href="https://dashboard.heroku.com/apps">https://dashboard.heroku.com/apps</a>.</li>
+</ul>
+
+<h3 id="Instala_el_cliente">Instala el cliente</h3>
+
+<p>Descarga e instala el cliente Heroku siguiendo estas <a href="https://devcenter.heroku.com/articles/getting-started-with-python#set-up">instructiones para Heroku</a>.</p>
+
+<p>Una vez instalado el cliente, ya podrás ejecutar comandos. Por ejemplo, para mostrar ayuda en el cliente:</p>
+
+<pre class="brush: bash notranslate">heroku help
+</pre>
+
+<h3 id="Crea_y_sube_el_sitio_web">Crea y sube el sitio web</h3>
+
+<p>Para crear la app ejecutamos el comando "create" en el directorio raíz de nuestro repositorio. Esta operación crea un git remoto ("puntero hacia el repositorio remoto") denominado <em>heroku</em> en nuestro entorno git local.</p>
+
+<pre class="brush: bash notranslate">heroku create</pre>
+
+<div class="note">
+<p><strong>Nota:</strong> Puedes nombrar el remoto, si lo deseas, especificando un valor después de "create". Si no, obtendrás un nombre aleatorio. Este nombre es el que se utiliza en la URL por defecto.</p>
+</div>
+
+<p>Podemos a continuación "empujar" (push) nuestra aplicación hacia el respositorio Heroku como se muestra abajo. Este proceso subirá la aplicación, la empaquetará en un dyno, ejecutará collestatic, y arrancará el sitio.</p>
+
+<pre class="brush: bash notranslate">git push heroku master</pre>
+
+<p>Si tenemos suerte, la app ya estará "corriendo" en el sitio, pero no estará funcionando correctamente ya que no hemos colocado las tablas que usa nuestra aplicación. Para hacer esto necesitamos utilizar el comando <code>heroku run</code> y arrancar un "<a href="https://devcenter.heroku.com/articles/deploying-python#one-off-dynos">one off dyno</a>" para realizar una operación de migración. Introduce el siguiente comando en el terminal:</p>
+
+<pre class="brush: bash notranslate">heroku run python manage.py migrate</pre>
+
+<p>Vamos a necesitar también poder añadir libros y autores, así que vamos a crear nuestro superusuario de administración, de nuevo utilizando un "one-off dyno":</p>
+
+<pre class="brush: bash notranslate">heroku run python manage.py createsuperuser</pre>
+
+<p>Una vez llevado a cabo ésto, podremos ver el sitio. Debería funcionar, aunque no tendrá aún ningún libro. Para abrir el navegador hacia el nuevo sitio web, usa el comando:</p>
+
+<pre class="brush: bash notranslate">heroku open</pre>
+
+<p>Crea algunos libros en el sitio de administración, y comprueba que el sitio se comporta tal y como esperas.</p>
+
+<h3 id="Gestionando_addons">Gestionando addons</h3>
+
+<p>Puedes revisar los add-ons de tu app usando el comando <code>heroku addons</code>. Se listarán todos los addons, su nivel de precio y estado.</p>
+
+<pre class="brush: bash notranslate">&gt;heroku addons
+
+Add-on Plan Price State
+───────────────────────────────────────── ───────── ───── ───────
+heroku-postgresql (postgresql-flat-26536) hobby-dev free created
+ └─ as DATABASE</pre>
+
+<p>Aquí vemos que tenemos un único add-on, la base de datos postgres SQL. Es gratuito, y fue creado automáticamente cuando se creó la aplicación. Puedes abrir una página web en la que examinar con más detalle el add-on de la base de datos (o cualquier otro add-on) utilizando el siguiente comando:</p>
+
+<pre class="brush: bash notranslate">heroku addons:open heroku-postgresql
+</pre>
+
+<p>Otros comandos te permiten crear, destruir, subir o bajar de versión de los addons (con una sintaxis similar a la de abrir). Para más información, consulta <a href="https://devcenter.heroku.com/articles/managing-add-ons">Managing Add-ons</a> (Heroku docs).</p>
+
+<h3 id="Estableciendo_las_variables_de_configuración">Estableciendo las variables de configuración</h3>
+
+<p>Puedes revisar las variables de configuración para el sitio con el comando <code>heroku config</code>. Abajo puedes comprobar que solo tenemos una variable, <code>DATABASE_URL</code>, usada para configurar nuestra base de datos.</p>
+
+<pre class="brush: bash notranslate">&gt;heroku config
+
+=== locallibrary Config Vars
+DATABASE_URL: postgres://uzfnbcyxidzgrl:j2jkUFDF6OGGqxkgg7Hk3ilbZI@ec2-54-243-201-144.compute-1.amazonaws.com:5432/dbftm4qgh3kda3</pre>
+
+<p>Si recuerdas de la sección <a href="#Getting_your_website_ready_to_publish">Preparando tu sitio web para hacerlo público</a>, tenemos que establecer variables de entorno para <code>DJANGO_SECRET_KEY</code> y <code>DJANGO_DEBUG</code>. Vamos a hacerlo ahora.</p>
+
+<div class="note">
+<p><strong>Nota:</strong> La clave secreta tiene que ser verdaderamente secreta! Una forma de generar una nueva clave es crear un nuevo proyecto Django (<code>django-admin startproject nombredeproyecto</code>) y obtener la clave generada para tí de su archivo <strong>settings.py</strong>.</p>
+</div>
+
+<p>Establecemos el valor de la variable <code>DJANGO_SECRET_KEY</code> con el comando <code>config:set</code> (como se muestra abajo). Recuerda usar tu propia clave secreta!</p>
+
+<pre class="brush: bash notranslate">&gt;heroku config:set DJANGO_SECRET_KEY=eu09(ilk6@4sfdofb=b_2ht@vad*$ehh9-)3u_83+y%(+phh&amp;=
+
+Setting DJANGO_SECRET_KEY and restarting locallibrary... done, v7
+DJANGO_SECRET_KEY: eu09(ilk6@4sfdofb=b_2ht@vad*$ehh9-)3u_83+y%(+phh
+</pre>
+
+<p>De forma similar, establecemos <code>DJANGO_DEBUG</code>:</p>
+
+<pre class="brush: bash notranslate">&gt;heroku config:set <code>DJANGO_DEBUG=''
+
+Setting DJANGO_DEBUG and restarting locallibrary... done, v8</code></pre>
+
+<p>Si visitas ahora el sitio recibirás un error "Bad request", porque es <em>obligatorio</em> establecer <a href="https://docs.djangoproject.com/en/1.10/ref/settings/#allowed-hosts">ALLOWED_HOSTS</a> si tienes establecido <code>DEBUG=False</code> (como medida de seguridad). Abre <strong>/locallibrary/settings.py</strong> y cambia el valor de <code>ALLOWED_HOSTS</code> para incluir la url base de tu app (por ejemplo, 'locallibrary1234.herokuapp.com') y la URL que usas normalmente en tu servidor de desarrollo local.</p>
+
+<pre class="brush: python notranslate">ALLOWED_HOSTS = ['&lt;your app URL without the https:// prefix&gt;.herokuapp.com','127.0.0.1']
+# For example:
+# ALLOWED_HOSTS = ['fathomless-scrubland-30645.herokuapp.com','127.0.0.1']
+</pre>
+
+<p>A continuación guarda los cambios y consolídalos en tu repo Github y en Heroku.</p>
+
+<pre class="brush: bash notranslate">git add -A
+git commit -m 'Update ALLOWED_HOSTS with site and development server URL'
+git push origin master
+git push heroku master</pre>
+
+<div class="note">
+<p>Una vez completada la actualización del sitio en Heroku, introduce una URL que no exista (por ejemplo, <strong>/catalog/doesnotexist/</strong>). Antes se habría mostrado una página de depuración detallada, pero ahora deberías simplemente ver una página de "Not Found".</p>
+</div>
+
+<h3 id="Depuración">Depuración</h3>
+
+<p>El cliente Heroku proporciona algunas herramientas para la depuración:</p>
+
+<pre class="brush: bash notranslate">heroku logs # Show current logs
+heroku logs --tail # Show current logs and keep updating with any new results
+heroku config:set DEBUG_COLLECTSTATIC=1 # Add additional logging for collectstatic (this tool is run automatically during a build)
+heroku ps #Display dyno status
+</pre>
+
+<p>Si necesitas más información de la que te proporcionan estas herramientas, tendrás que investigar en <a href="https://docs.djangoproject.com/en/1.10/topics/logging/">Django Logging</a>.</p>
+
+<ul>
+</ul>
+
+<h2 id="Resumen">Resumen</h2>
+
+<p>Has llegado al final de este tutorial sobre la instalación de apps Django en producción, así como de la serie de tutoriales sobre el trabajo con Django. Esperamos que los hayas encontrado útiles. Puedes encontrar una versión completa del <a href="https://github.com/mdn/django-locallibrary-tutorial">código fuente en Github aquí</a>.<br>
+ <br>
+ El siguiente paso sería leer nuestros últimos artículos, y finalmente completar la evaluación.</p>
+
+<h2 id="Ver_también">Ver también</h2>
+
+<ul>
+ <li><a href="https://docs.djangoproject.com/en/1.10/howto/deployment/">Deploying Django</a> (Django docs)
+
+ <ul>
+ <li><a href="https://docs.djangoproject.com/en/1.10/howto/deployment/checklist/">Deployment checklist</a> (Django docs)</li>
+ <li><a href="https://docs.djangoproject.com/en/1.10/howto/static-files/deployment/">Deploying static files</a> (Django docs)</li>
+ <li><a href="https://docs.djangoproject.com/en/1.10/howto/deployment/wsgi/">How to deploy with WSGI</a> (Django docs)</li>
+ <li><a href="https://docs.djangoproject.com/en/1.10/howto/deployment/wsgi/modwsgi/">How to use Django with Apache and mod_wsgi</a> (Django docs)</li>
+ <li><a href="https://docs.djangoproject.com/en/1.10/howto/deployment/wsgi/gunicorn/">How to use Django with Gunicorn</a> (Django docs)</li>
+ </ul>
+ </li>
+ <li>Heroku
+ <ul>
+ <li><a href="https://devcenter.heroku.com/articles/django-app-configuration">Configuring Django apps for Heroku</a> (Heroku docs)</li>
+ <li><a href="https://devcenter.heroku.com/articles/getting-started-with-python#introduction">Getting Started on Heroku with Django</a> (Heroku docs)</li>
+ <li><a href="https://devcenter.heroku.com/articles/django-assets">Django and Static Assets</a> (Heroku docs)</li>
+ <li><a href="https://devcenter.heroku.com/articles/python-concurrency-and-database-connections">Concurrency and Database Connections in Django</a> (Heroku docs)</li>
+ <li><a href="https://devcenter.heroku.com/articles/how-heroku-works">How Heroku works</a> (Heroku docs)</li>
+ <li><a href="https://devcenter.heroku.com/articles/dynos">Dynos and the Dyno Manager</a> (Heroku docs)</li>
+ <li><a href="https://devcenter.heroku.com/articles/config-vars">Configuration and Config Vars</a> (Heroku docs)</li>
+ <li><a href="https://devcenter.heroku.com/articles/limits">Limits</a> (Heroku docs)</li>
+ <li><a href="https://devcenter.heroku.com/articles/python-gunicorn">Deploying Python applications with Gunicorn</a> (Heroku docs)</li>
+ <li><a href="https://devcenter.heroku.com/articles/deploying-python">Deploying Python and Django apps on Heroku</a> (Heroku docs)</li>
+ <li><a href="https://devcenter.heroku.com/search?q=django">Other Heroku Django docs</a></li>
+ </ul>
+ </li>
+ <li>Digital Ocean
+ <ul>
+ <li><a href="https://www.digitalocean.com/community/tutorials/how-to-serve-django-applications-with-uwsgi-and-nginx-on-ubuntu-16-04">How To Serve Django Applications with uWSGI and Nginx on Ubuntu 16.04</a></li>
+ <li><a href="https://www.digitalocean.com/community/tutorials?q=django">Other Digital Ocean Django community docs</a></li>
+ </ul>
+ </li>
+</ul>
+
+<p>{{PreviousMenuNext("Learn/Server-side/Django/Testing", "Learn/Server-side/Django/web_application_security", "Learn/Server-side/Django")}}</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>
diff --git a/files/es/learn/server-side/django/development_environment/index.html b/files/es/learn/server-side/django/development_environment/index.html
new file mode 100644
index 0000000000..cf47cdefa9
--- /dev/null
+++ b/files/es/learn/server-side/django/development_environment/index.html
@@ -0,0 +1,421 @@
+---
+title: Puesta en marcha de un entorno de desarrollo Django
+slug: Learn/Server-side/Django/development_environment
+tags:
+ - Aprendizaje
+ - Codificación de scripts
+ - Entorno de Desarrollo
+ - Principiante
+ - Python
+ - django
+ - instalación
+ - introducción
+translation_of: Learn/Server-side/Django/development_environment
+---
+<div>{{LearnSidebar}}</div>
+
+<div>{{PreviousMenuNext("Learn/Server-side/Django/Introduction", "Learn/Server-side/Django/Tutorial_local_library_website", "Learn/Server-side/Django")}}</div>
+
+<p class="summary">Ahora que sabes para qué se utiliza Django, te enseñaremos cómo configurar y probar un entorno de desarrollo Django en Windows, Linux (Ubuntu), y Mac OS X — cualquiera que sea el sistema operativo común que estés utilizando, este artículo te dará lo que necesitas para ser capaz de empezar a desarrollar aplicaciones Django.</p>
+
+<table class="learn-box standard-table">
+ <tbody>
+ <tr>
+ <th scope="row">Pre-requisitos:</th>
+ <td>
+ <p>Saber como abrir un terminal / linea de comandos. Saber como instalar paquetes de software en el sistema operativo de tu computadora de desarrollo.</p>
+ </td>
+ </tr>
+ <tr>
+ <th scope="row">Objetivo:</th>
+ <td>Tener funcionando un entorno de desarrollo Django (1.10) en tu computadora.</td>
+ </tr>
+ </tbody>
+</table>
+
+<h2 id="Visión_general_del_entorno_de_desarrollo_Django">Visión general del entorno de desarrollo Django</h2>
+
+<p>Django hace muy fácil configurar tu computadora de manera que puedas empezar a desarrollar aplicaciones web. Esta sección explica qué consigues con el entorno de desarrollo y proporciona una visión general de algunas de tus opciones de puesta en marcha y configuración. El resto del artículo explica el método <em>recomendado</em> de instalación del entorno de desarrollo de Django en Ubuntu, Mac OS X, y Windows, y cómo puedes probarlo.</p>
+
+<h3 id="¿Qué_es_el_entorno_de_desarrollo_Django">¿Qué es el entorno de desarrollo Django?</h3>
+
+<p>El entorno de desarrollo es una instalación de Django en tu computadora local que puedes usar para desarrollar y probar apps Django antes de desplegarlas al entorno de producción.</p>
+
+<p>Las principales herramientas que el mismo Django proporciona son un conjunto de scripts de Python para crear y trabajar con proyectos Django, junto con un simple <em>servidor web de desarrollo</em> que puedes usar para probar de forma local (es decir en tu computadora, no en un servidor web externo) aplicaciones web Django con el explorador web de tu computadora.</p>
+
+<p>Hay otras herramientas periféricas, que forman parte del entorno de desarrollo, que no cubriremos aquí. Estas incluyen cosas como un <a href="/en-US/docs/Learn/Common_questions/Available_text_editors">editor de textos</a> o IDE para editar código, una herramienta de gestión del control de fuentes como <a href="https://git-scm.com/">Git</a> para gestionar con seguridad las diferentes versiones de tu código. Asumimos que tienes ya un editor de textos instalado.</p>
+
+<h3 id="¿Cuáles_son_las_opciones_de_puesta_en_marcha_de_Django">¿Cuáles son las opciones de puesta en marcha de Django?</h3>
+
+<p>Django es extremadamente flexible en términos de cómo y dónde puede instalarse y configurarse. Django puede ser:</p>
+
+<ul>
+ <li>instalado en diferentes sistemas operativos.</li>
+ <li>ser usado con Python 3 y Python 2.</li>
+ <li>instalado desde las fuentes, desde el Python Package Index (PyPi) y en muchos casos desde la aplicación de gestión de paquetes de la computadora.</li>
+ <li>configurado para usar una de entre varias bases de datos, que pueden también necesitar ser instaladas y configuradas por separado.</li>
+ <li>ejecutarse en el entorno Python del sistema principal o dentro de entornos virtuales Python separados.</li>
+</ul>
+
+<p>Cada una de estas opciones requieren configuraciones y puesta en marcha ligeramente diferentes. Las siguientes subsecciones explican algunas de tus opciones. En el resto del artículo te mostraremos como ajustar Django en un pequeño número de sistemas operativos, y se supondrá ese ajuste a lo largo del resto del módulo.</p>
+
+<div class="note">
+<p><strong>Nota</strong>: La documentación oficial de Django cubre otras posibles opciones de instalación. Enlazamos a los <a href="#furtherreading">documentos apropiados más abajo</a>.</p>
+</div>
+
+<h4 id="¿Qué_sistemas_operativos_están_soportados">¿Qué sistemas operativos están soportados?</h4>
+
+<p>Las aplicaciones web Django pueden ejecutarse en casi cualquier máquina donde pueda funcionar el lenguaje de programación Python: Windows, Mac OS X, Linux/Unix, Solaris, por nombrar sólo unos pocos. Casi cualquier computadora debería tener el rendimiento necesario para ejecutar Django durante el desarrollo.</p>
+
+<p>En este artículo proporcionamos instrucciones para Windows, Mac OS X, y Linux/Unix.</p>
+
+<h4 id="¿Qué_versión_de_Python_deberías_usar">¿Qué versión de Python deberías usar?</h4>
+
+<p>Django se ejecuta por encima de Python, y puede uarse tanto con Python 2 o con Python 3 (o ambos). Cuando estés seleccionando una versión deberías tener en cuenta que:</p>
+
+<ul>
+ <li>Python 2 es una versión tradicional del lenguaje que no va a tener más características nuevas pero que tiene disponible para los desarrolladores, un enorme repositorio de bibliotecas de terceros de alta calidad (algunas de las cuales no están disponibles en Python 3).</li>
+ <li>Python 3 es una actualización de Python 2 que, aunque similar, es más consistente y fácil de usar. Python 3 también es el futuro de Python, y continúa su evolución.</li>
+ <li>También es posible soportar ambas versiones usando bibliotecas (ej. <a href="http://pythonhosted.org/six/">six</a>), aunque no sin un esfuerzo adicional de desarrollo.</li>
+</ul>
+
+<ul>
+</ul>
+
+<div class="note">
+<p><strong>Nota</strong>: Historicamente Python 2 era la única elección realista, porque muy pocas bibliotecas de terceros estaban disponibles para Python 3. La tendencia actual es que la mayoría de paquetes nuevos y populares del <a href="https://pypi.python.org/pypi">Python Package Index</a> (PyPi) soporten ambas versiones de Python. Aunque todavía haya muchos paquetes que sólo están disponibles para Python 2, elegir Python 3 es actualmente una opción muy popular.</p>
+</div>
+
+<p>Te recomendamos que uses la última versión de Python 3 a menos que el sitio dependa de bibliotecas de terceros que sólo están disponibles para Python 2.</p>
+
+<p>Este artículo te explicará como instalar un entorno para Python 3 (el ajuste equivalente para Python 2 sería muy similar).</p>
+
+<h4 id="¿Dónde_puedo_descagarme_Django">¿Dónde puedo descagarme Django?</h4>
+
+<p>Hay tres lugares para descargar Django:</p>
+
+<ul>
+ <li>El Python Package Repository (PyPi), usando la herramienta <em>pip</em>. Este es el mejor modo de obtener la última veersión estable de Django.</li>
+ <li>Usar una versión del gestor de paquetes de tu computadora. Las distribuciones de Django que se empaquetan con los sistemas operativos ofrecen un mecanismo de instalación ya familiar. Ten en cuenta sin embargo que la versión empaquetada puede ser bastante antigua, y sólo puede ser instalada en el entorno de Python del sistema (que puede no ser el que tu quieras).</li>
+ <li>Instalar desde la fuente. Puedes obtener y descargar la versión con el último grito de Python partiendo de las fuentes. Esto no es lo recomendable para principiantes, pero es necesario cuando estás listo para empezar a contribuir codificando el propio Django.</li>
+</ul>
+
+<p>Este artículo te muestra como instalar Django desde PyPi, para conseguir la última versión estable.</p>
+
+<h4 id="¿Qué_base_de_datos">¿Qué base de datos?</h4>
+
+<p>Django soporta cuatro bases de datos importantes (PostgreSQL, MySQL, Oracle y SQLite), y hay bibliotecas comunitarias que proporcionan varios niveles de soporte para otras bases de datos populares SQL y NOSQL. Te recomendamos que elijas la misma base de datos tanto para la producción como para el desarrollo (aunque Django abstrae muchas de las diferencias entre las bases usando su Object-Relational Mapper (ORM), hay todavía <a href="https://docs.djangoproject.com/en/1.10/ref/databases/">problemas potenciales</a> que es mejor evitar).</p>
+
+<p>Durante este artículo (y la mayoría de este módulo) usaremos la base de datos <em>SQLite</em>, que almacena sus datos en un fichero. SQLite está pensado para ser usado como base ligera y no puede soportar un alto nivel de concurrencia. Es sin embargo una excelente elección para aplicaciones que son principalmente de sólo lectura.</p>
+
+<div class="note">
+<p><strong>Nota</strong>: Django está configurado para usar SQLite por defecto cuando comienzas tu proyecto de sitio web usando las herramientas estándard (<em>django-admin</em>). Es una gran elección cuando estás empezando porque no requiere configuración o puesta en marcha adicional. </p>
+</div>
+
+<h4 id="¿Instalar_Python_en_un_entorno_de_sistema_o_virtual">¿Instalar Python en un entorno de sistema o virtual?</h4>
+
+<p>Cuando instalas Python3 obtienes un único entorno global que es compartido con todo el código Python3. Si bien puedes instalar los paquetes que te gusten en el entorno, sólo puedes instalar al mismo tiempo una versión en particular de cada paquete.</p>
+
+<div class="note">
+<p><strong>Nota</strong>: Las aplicaciones Python instaladas en el entorno global pueden entrar en conflicto potencialmente unas con otras (ej. si dependen de diferentes versiones del mismo paquete). </p>
+</div>
+
+<p>Si instalas Django dentro del entorno por defecto/global sólo podrás apuntar a una sóla versión de Django en la computadora. Esto puede ser un problema si quieres crear nuevos sitios (usando la última versión de Django) pero manteniendo los sitios web que dependen de versiones más antiguas.</p>
+
+<div class="note"></div>
+
+<p>Como resultado, los desarrolladores experimentados de Python/Django normalmente ejecutan las aplicaciones Python dentro de <em>entornos virtuales Python</em> independientes. De esta forma se habilitan múltiples entornos Django diferentes en la misma computadora. !El mismo equipo de desarrollo Django recomienda que uses entornos Python virtuales!</p>
+
+<div class="note"></div>
+
+<p>Este módulo da por supuesto que has instalado Django en un entorno virtual, y te mostraremos cómo hacerlo más abajo.</p>
+
+<h2 id="Instalación_de_Python_3">Instalación de Python 3</h2>
+
+<p>Para poder usar Django tendrás que instalar Python en tu sistema operativo. Si estás usando <em>Python 3</em> necesitarás la herramienta <a href="https://pypi.python.org/pypi">Python Package Index</a> — <em>pip3</em> — que se usa para gestionar (instalar, actualizar y eliminar) los paquetes/bibliotecas Python usados por Django y tus otras aplicaciones Python.</p>
+
+<p>Esta sección explica brevemente como puedes comprobar qué versiones de Python están presentes, e instalar nuevas versiones cuando lo necesites, en Ubuntu Linux 16.04, Mac OS X, y Windows 10.</p>
+
+<div class="note">
+<p><strong>Nota</strong>: Dependiendo de tu plataforma, podrías también ser capaz de instalar Python/pip desde la propia aplicación de gestión de paquetes de tu sistema o vía otros mecanismos. Para la mayoría de las plataformas puedes descargar los ficheros de instalación requeridos desde <a href="https://www.python.org/downloads/">https://www.python.org/downloads/</a> e instalarlos usando el método apropiado específico de la plataforma.</p>
+</div>
+
+<h3 id="Ubuntu_16.04">Ubuntu 16.04</h3>
+
+<p>Ubuntu Linux incluye Python 3 por defecto. Puedes confirmarlo ejecutando el siguiente comando en una terminal:</p>
+
+<pre class="brush: bash"><span style="line-height: 1.5;">python3 -V
+ Python 3.5.2</span></pre>
+
+<p>Sin embargo la herramienta Python Package Index que necesitarás para instalar paquetes de Python 3 (incluido Django) <strong>No</strong> está disponible por defecto. Puedes instalar pip3 en un terminal bash usando:</p>
+
+<pre class="brush: bash">sudo apt-get install python3-pip
+</pre>
+
+<h3 id="Mac_OS_X">Mac OS X</h3>
+
+<p>Mac OS X "El Capitan" no incluye Python 3. Puedes confirmarlo ejecutando los siguientes comandos en un terminal bash:</p>
+
+<pre class="brush: bash"><span style="line-height: 1.5;">python3 -V
+ </span>-bash: python3: command not found</pre>
+
+<p>Puedes instalar fácilmente Python 3 (junto con la herramienta <em>pip3</em>) desde <a href="https://www.python.org/">python.org</a>:</p>
+
+<ol>
+ <li>Descarga el instalador requerido:
+ <ol>
+ <li>Vete a <a href="https://www.python.org/downloads/">https://www.python.org/downloads/</a></li>
+ <li>Selecciona el botón <strong>Descarga Python 3.5.2</strong> (el número exacto de versión menor puede ser diferente).</li>
+ </ol>
+ </li>
+ <li>Localiza el fichero usando <em>Finder</em>, haz doble-click sobre el fichero del paquete. Pincha siguiente en las ventanas de instalación.</li>
+</ol>
+
+<p>Puedes confirmar ahora una instalación satisfactoria comprobando <em>Python 3</em> como se muestra a continuación:</p>
+
+<pre class="brush: bash"><span style="line-height: 1.5;">python3 -V
+ Python 3.5.2</span>0
+</pre>
+
+<p>Puedes igualmente comprobar que <em>pip3</em> está instalado listando los paquetes disponibles:</p>
+
+<pre class="brush: bash">pip3 list</pre>
+
+<h3 id="Windows_10">Windows 10</h3>
+
+<p>Windows no incluye Python por defecto, pero puedes instalarlo fácilmente (junto con la herramienta <em>pip3</em>) desde <a href="https://www.python.org/">python.org</a>:</p>
+
+<ol>
+ <li>Descarga el instalador requerido:
+ <ol>
+ <li>Vete a <a href="https://www.python.org/downloads/">https://www.python.org/downloads/</a></li>
+ <li>Selecciona el botón de <strong>Descarga Python 3.6.3</strong> (la versión menor exacta puede ser diferente).</li>
+ </ol>
+ </li>
+ <li>Instala Python haciendo doble-click en el fichero descargado y pulsando siguiente en las ventanas de instalación</li>
+</ol>
+
+<p>Puedes verificar a continuación que Python 3 se instaló correctamente entrando el siguiente texto en una ventana de comandos:</p>
+
+<pre class="brush: bash"><span style="line-height: 1.5;">py -3 -V
+ Python 3.6.3</span>
+</pre>
+
+<p>El instalador de Windows incorpora <em>pip3</em> (el gestor de paquetes de Python) por defecto. Puedes listar paquetes como se muestra a continuación:</p>
+
+<pre class="brush: bash"><span style="line-height: 1.5;">pip3 list</span>
+</pre>
+
+<div class="note">
+<p><strong>Nota</strong>: El instalador debería poner en marcha todo lo que necesitas para que el comando de arriba funcione. Si por el contrario obtienes un mensaje de que Python no puede ser encontrado, puede que necesites añadirlo al path de tu sistema.</p>
+</div>
+
+<h2 id="Uso_de_Django_dentro_de_un_entorno_virtual_de_Python">Uso de Django dentro de un entorno virtual de Python</h2>
+
+<p>Las bibliotecas que usaremos para crear nuestros entornos virtuales están en <a href="https://virtualenvwrapper.readthedocs.io/en/latest/index.html">virtualenvwrapper</a> (Linux and Mac OS X) y <a href="https://pypi.python.org/pypi/virtualenvwrapper-win">virtualenvwrapper-win</a> (Windows), que utilizan a su vez la herramienta <a href="https://developer.mozilla.org/en-US/docs/Python/Virtualenv">virtualenv</a>. Las herramientas wrapper crean una interfaz consistente para la gestión de interfaces en todas las plataformas.</p>
+
+<h3 id="Instalación_del_software_del_entorno_virtual">Instalación del software del entorno virtual</h3>
+
+<h4 id="Puesta_en_marcha_del_entorno_virtual_en_Ubuntu">Puesta en marcha del entorno virtual en Ubuntu</h4>
+
+<p>Después de instalar Python y pip puedes instalar <em>virtualenvwrapper</em> (que incluye <em>virtualenv</em>) usando <em>pip3</em> como se muestra.</p>
+
+<pre><code>sudo pip3 install virtualenvwrapper</code></pre>
+
+<p>A continuación añade las siguientes líneas al final del fichero de inicio de tu shell (éste es un fichero oculto llamado <strong>.bashrc</strong> que se encuentra en tu directorio de inicio del usuario). Ésto ajusta la localización de donde deberían vivir los entornos virtuales, la localización de los directorios de tus proyectos de desarrollo, y la localización del script instalado con este paquete:</p>
+
+<pre><code>export WORKON_HOME=$HOME/.virtualenvs
+export VIRTUALENVWRAPPER_PYTHON=/usr/bin/python3
+export PROJECT_HOME=$HOME/Devel
+source /usr/local/bin/virtualenvwrapper.sh</code></pre>
+
+<p>A continuación volver a recargar el fichero de inicio ejecutando el siguiente comando en el terminal:</p>
+
+<pre><code>source ~/.bashrc</code></pre>
+
+<p>En este punto deberías ver un puñado de scripts empezando a ejecutarse como se muestra a continuación:</p>
+
+<pre><code>virtualenvwrapper.user_scripts creating /home/ubuntu/.virtualenvs/premkproject
+virtualenvwrapper.user_scripts creating /home/ubuntu/.virtualenvs/postmkproject
+...
+virtualenvwrapper.user_scripts creating /home/ubuntu/.virtualenvs/preactivate
+virtualenvwrapper.user_scripts creating /home/ubuntu/.virtualenvs/postactivate
+virtualenvwrapper.user_scripts creating /home/ubuntu/.virtualenvs/get_env_details</code></pre>
+
+<p>Ahora puedes crear un nuevo entorno virtual con el comando <code>mkvirtualenv</code>.</p>
+
+<h4 id="Puesta_en_marcha_del_entorno_virtual_en_Mac_OS_X">Puesta en marcha del entorno virtual en Mac OS X</h4>
+
+<p>El ajuste de virtualenvwrapper en Mac OS X es casi idéntico a como es en Ubuntu. </p>
+
+<p>Instalar <em>virtualenvwrapper</em> (y <em>virtualenv</em> incluido en el paquete) usando <em>pip</em> como se muestra a continuación.</p>
+
+<pre><code>sudo pip3 install virtualenvwrapper</code></pre>
+
+<p>A continuación añadir las siguientes líneas al final del fichero de inicio de tu shell. Son las mismas líneas que para Ubuntu, pero el fichero de inicio se llama de forma diferente <strong>.bash_profile</strong> y está oculto en tu directorio de inicio.</p>
+
+<pre><code>export WORKON_HOME=$HOME/.virtualenvs
+export VIRTUALENVWRAPPER_PYTHON=/usr/bin/python3
+export PROJECT_HOME=$HOME/Devel
+source /usr/local/bin/virtualenvwrapper.sh</code></pre>
+
+<p><strong>Nota</strong>: Si no puedes encontrar <strong>.bash-profile</strong> para editar en el finder, puedes también abrirlo usando nano.</p>
+
+<p>Los comandos parecen algo como lo siguiente.</p>
+
+<pre><code>cd ~ # Navegar a mi directorio de inicio
+ls -la # Listar el contenido del directorio. Deberias ver .bash_profile
+nano .bash_profile # Abrir el fichero en el editor de texto nano, en el terminal
+# Avanzar hast el final del fichero, y copiar y pegar las lineas de arrriba
+# Usar Ctrl+X para salir de nano, Elegir Y para guardar el fichero.</code></pre>
+
+<p>A continuación recargar el fichero de inicio realizando la siguiente llamada en el terminal:</p>
+
+<pre><code>source ~/.bash_profile</code></pre>
+
+<p>En este punto deberías ver un puñado de scripts empezando a ejecutarse (los mismos scripts que para la instalación en Ubuntu). Deberías ser ahora capaz de crear un nuevo entorno virtual con el comado <code>mkvirtualenv</code>.</p>
+
+<h4 id="Puesta_en_marcha_del_entorno_virtual_en_Windows_10">Puesta en marcha del entorno virtual en Windows 10</h4>
+
+<p>Instalar <a href="https://pypi.python.org/pypi/virtualenvwrapper-win">virtualenvwrapper-win</a> es incluso más simple que poner en marcha <em>virtualenvwrapper</em> porque no necesitas configurar donde almacena la herramienta la información del entorno (hay un valor por defecto). Todo lo que necesitas hacer es ejecutar el siguiente comando en la consola de comandos en línea:</p>
+
+<pre><code>pip3 install virtualenvwrapper-win</code></pre>
+
+<p>Y a continuación ya puedes crear un nuevo entorno virtual con <code>mkvirtualenv</code></p>
+
+<h3 id="Creación_de_un_entorno_virtual">Creación de un entorno virtual</h3>
+
+<p>Una vez que hayas instalado <em>virtualenvwrapper</em> o <em>virtualenvwrapper-win</em> trabajar con entornos virtuales es muy similar en todas las plataformas.</p>
+
+<p>Ahora puedes crear un nuevo entorno virtual con el comando <code>mkvirtualenv</code>. A medida que se ejecuta este comando verás que se va poniendo en marcha el entorno (lo que verás es ligeramente específico de la plataforma). Cuando se completa el comando el nuevo entorno virtual estará activo — podrás comprobarlo porque el comienzo del prompt será el nombre del entorno entre paréntesis (como se muestra abajo).</p>
+
+<pre><code>$ mkvirtualenv my_django_environment
+
+Running virtualenv with interpreter /usr/bin/python3
+...
+virtualenvwrapper.user_scripts creating /home/ubuntu/.virtualenvs/t_env7/bin/get_env_details
+(my_django_environment) ubuntu@ubuntu:~$</code>
+</pre>
+
+<p>Una vez que estás dentro del entorno virutal puedes instalar Django e iniciar el desarrollo.</p>
+
+<div class="note">
+<p><strong>Nota</strong>: De ahora en adelante en este artículo (y por ende en el módulo) asume por favor que todos los comandos se ejecutan en un entorno virtual Python como el que acabamos de poner en marcha arriba.</p>
+</div>
+
+<h3 id="Uso_de_un_entorno_virtual">Uso de un entorno virtual</h3>
+
+<p>Hay sólo otros pocos comandos útiles que deberías conocer (hay más en la documentación de la herramienta, pero éstos son los que usarás de forma habitual:</p>
+
+<ul>
+ <li><code>deactivate</code> — Salir del entorno virutal Python actual</li>
+ <li><code>workon</code> — Listar los entornos virtuales disponibles</li>
+ <li><code>workon name_of_environment</code> — Activar el entorno virtual Python especificado</li>
+ <li><code>rmvirtualenv name_of_environment</code> — Borrar el entorno especificado.</li>
+</ul>
+
+
+
+<h2 id="Instalación_de_Django">Instalación de Django</h2>
+
+<p>Una vez que has creado el entorno virtual, y realizado la llamada <code>workon</code>  para entrar en él, puedes usar <em>pip3 </em>para instalar Django. </p>
+
+<pre class="brush: bash">pip3 install django
+</pre>
+
+<p>Puedes comprobar que está instalado Django ejecutando el siguiente comando (esto sólo comprueba que Python puede encontrar el módulo Django):</p>
+
+<pre class="brush: bash"># Linux/Mac OS X
+python3 -m django --version
+ 1.11.7
+
+# Windows
+py -3 -m django --version
+ 1.11.7
+</pre>
+
+<div class="note">
+<p><strong>Nota</strong>: En Windows se lanzan scripts <em>Python 3</em> añadiendo como prefijo del comando con <code>py -3</code>, mientras que en Linux/Mac OSX, el comando es <code>python3</code>.</p>
+</div>
+
+<div class="warning">
+<p><strong>Importante</strong>: El resto de este <strong>modulo </strong>usa, para invocar Python 3, el comando <em>Linux</em> (<code>python3</code>) . Si estás trabajando en <em>Windows   </em>simplemente reemplazar este prefijo con: <code>py -3</code></p>
+</div>
+
+<h2 id="Comprobación_de_tu_instalación">Comprobación de tu instalación</h2>
+
+<p>La prueba de arriba funciona, pero no es muy divertida. Una comprobación más interesante es crear un esqueleto de proyecto y ver si funciona. Para hacer ésto, navega primero en tu consola de comandos/terminal a donde quieras almacenar tus aplicaciones Django. Crea una carpeta para la comprobación de tu sitio y navega a ella.</p>
+
+<pre class="brush: bash">mkdir django_test
+cd django_test
+</pre>
+
+<p>Puedes crear a continuación un nuevo esqueleto de sitio llamado "<em>mytestsite</em>"  usando la herramienta <strong>django-admin</strong> como se muestra a continuación. Después de crear el sitio puedes navegar a la carpeta donde encontrarás el script principal para la gestión de proyectos, llamado <strong>manage.py</strong>.</p>
+
+<pre class="brush: bash">django-admin startproject mytestsite
+cd mytestsite</pre>
+
+<p>Podemos arrancar el <em>servidor web de desarrollo</em> desde esta carpeta usando <strong>manage.py</strong> y el comando <code>runserver</code>, como se muestra.</p>
+
+<pre class="brush: bash">$ <strong>python3 manage.py runserver </strong>
+Performing system checks...
+
+System check identified no issues (0 silenced).
+
+You have 13 unapplied migration(s). Your project may not work properly until you apply the migrations for app(s): admin, auth, contenttypes, sessions.
+Run 'python manage.py migrate' to apply them.
+
+September 19, 2016 - 23:31:14
+Django version 1.10.1, using settings 'mysite.settings'
+Starting development server at http://127.0.0.1:8000/
+Quit the server with CONTROL-C.
+</pre>
+
+<div class="note">
+<p><strong>Nota</strong>: El comando anterior muestra el comando Linux/Mac OS X. En este punto ¡puedes ignorar las advertencias sobre "13 unapplied migration(s)"!</p>
+</div>
+
+<p>Una vez que tengas funcionando el servidor puedes ver el sitio navegando a la siguiente URL en tu explorador web local : <code>http://127.0.0.1:8000/</code>. Deberías ver un sitio parecido a este:</p>
+
+<p><img alt="The home page of the skeleton Django app." src="https://mdn.mozillademos.org/files/13893/Django_Skeleton_Website_Homepage.png" style="border-style: solid; border-width: 1px; display: block; height: 544px; margin: 0px auto; width: 865px;"></p>
+
+<ul>
+</ul>
+
+<h2 id="Sumario">Sumario</h2>
+
+<p>Ahora tienes levantado y funcionando en tu computadora tu entorno de desarrollo Django .</p>
+
+<p>En la sección de pruebas viste brevemente cómo crear un nuevo sitio web Django usando <code>django-admin startproject</code>, y hacerlo funcionar en tu explorador usando el servidor web de desarrollo (<code><strong>python3 manage.py runserver</strong></code>).</p>
+
+<p>En el siguiente artículo expandimos este proceso, construyendo una aplicación web simple pero completa.</p>
+
+<h2 id="Ver_también">Ver también</h2>
+
+<ul>
+ <li><a href="https://docs.djangoproject.com/es/2.0/intro/install/">Guía de instalación rápida</a> (Django docs)</li>
+ <li><a href="https://docs.djangoproject.com/es/2.0/topics/install/">Cómo instalar Django — Guía Completa</a> (Django docs) - incluye información sobre cómo borrar Django</li>
+ <li><a href="https://docs.djangoproject.com/es/2.0/howto/windows/">Cómo instalar Django en Windows</a> (Django docs)</li>
+</ul>
+
+<p>{{PreviousMenuNext("Learn/Server-side/Django/Introduction", "Learn/Server-side/Django/Tutorial_local_library_website", "Learn/Server-side/Django")}}</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>
diff --git a/files/es/learn/server-side/django/django_assessment_blog/index.html b/files/es/learn/server-side/django/django_assessment_blog/index.html
new file mode 100644
index 0000000000..cb5ac7ad88
--- /dev/null
+++ b/files/es/learn/server-side/django/django_assessment_blog/index.html
@@ -0,0 +1,307 @@
+---
+title: 'Evaluación: DIY Django mini blog'
+slug: Learn/Server-side/Django/django_assessment_blog
+translation_of: Learn/Server-side/Django/django_assessment_blog
+---
+<div>{{LearnSidebar}}</div>
+
+<div>{{PreviousMenu("Learn/Server-side/Django/web_application_security", "Learn/Server-side/Django")}}</div>
+
+<p class="summary">En esta evaluación usarás el conocimiento de Django que has adquirido en el módulo <a href="/es/docs/Learn/Server-side/Django" style="font-size: 1.25rem;">Framework Web Django (Python)</a> para crear un blog muy básico<span style="font-size: 1.25rem;">.</span></p>
+
+<table class="learn-box standard-table">
+ <tbody>
+ <tr>
+ <th scope="row">Requisitos Previos:</th>
+ <td>Before attempting this assessment you should have already worked through all the articles in this module.</td>
+ </tr>
+ <tr>
+ <th scope="row">Objetivo:</th>
+ <td>Comprender los fundamentos de Django , incluidos las configuraciones de URL , modelos, vistas, formularios y  templates.</td>
+ </tr>
+ </tbody>
+</table>
+
+<h2 id="Resumen_del_proyecto">Resumen del proyecto</h2>
+
+<p>Las paginas que necesitan ser mostradas, sus URLs, y otros requisitos son listados debajo: </p>
+
+<table class="standard-table">
+ <thead>
+ <tr>
+ <th scope="col">Pagina</th>
+ <th scope="col">URL</th>
+ <th scope="col">Requisitos</th>
+ </tr>
+ </thead>
+ <tbody>
+ <tr>
+ <td>Home page</td>
+ <td><code>/</code> and <code>/blog/</code></td>
+ <td>Una pagina inicial que describa el sitio</td>
+ </tr>
+ <tr>
+ <td>Lista de todos las publicaciones del blog</td>
+ <td><code>/blog/blogs/</code></td>
+ <td>
+ <p>Lista de todos las publicaciones del blog:</p>
+
+ <ul>
+ <li>Accessible to all users from a sidebar link.</li>
+ <li>List sorted by post date (newest to oldest).</li>
+ <li>List paginated in groups of 5 articles.</li>
+ <li>List items display the blog title, post date, and author.</li>
+ <li>Blog post names are linked to blog detail pages.</li>
+ <li>Blogger (author names) are linked to blog author detail pages.</li>
+ </ul>
+ </td>
+ </tr>
+ <tr>
+ <td>Blog autor (blogger) pagina de detalles</td>
+ <td><code>/blog/blogger/<em>&lt;author-id&gt;</em></code></td>
+ <td>
+ <p>Information for a specified author (by id) and list of their blog posts:</p>
+
+ <ul>
+ <li>Accessible to all users from author links in blog posts etc.</li>
+ <li>Contains some biographical information about the blogger/author.</li>
+ <li>List sorted by post date (newest to oldest).</li>
+ <li>Not paginated.</li>
+ <li>List items display just the blog post name and post date.</li>
+ <li>Blog post names are linked to blog detail pages.</li>
+ </ul>
+ </td>
+ </tr>
+ <tr>
+ <td>Blog post detail page</td>
+ <td><code>/blog/<em>&lt;blog-id&gt;</em></code></td>
+ <td>
+ <p>Blog post details.</p>
+
+ <ul>
+ <li>Accessible to all users from blog post lists.</li>
+ <li>Page contains the blog post: name, author, post date, and content.</li>
+ <li>Comments for the blog post should be displayed at bottom.</li>
+ <li>Comments should be sorted in order: oldest to most recent.</li>
+ <li>Contains link to add comments at end for logged in users (see Comment form page)</li>
+ <li>Blog posts and comments need only display plain text. There is no need to support any sort of HTML markup (e.g. links, images, bold/italic, etc).</li>
+ </ul>
+ </td>
+ </tr>
+ <tr>
+ <td>List of all bloggers</td>
+ <td><code>/blog/bloggers/</code></td>
+ <td>
+ <p>List of bloggers on system:</p>
+
+ <ul>
+ <li>Accessible to all users from site sidebar</li>
+ <li>Blogger names are linked to Blog author detail pages.</li>
+ </ul>
+ </td>
+ </tr>
+ <tr>
+ <td>Comment form page</td>
+ <td><code>/blog/<em>&lt;blog-id&gt;</em>/create</code></td>
+ <td>
+ <p>Create comment for blog post:</p>
+
+ <ul>
+ <li>Accessible to logged-in users (only) from link at bottom of blog post detail pages.</li>
+ <li>Displays form with description for entering comments (post date and blog is not editable).</li>
+ <li>After a comment has been posted, the page will redirect back to the associated blog post page.</li>
+ <li>Users cannot edit or delete their posts.</li>
+ <li>Logged out users will be directed to the login page to log in, before they can add comments. After logging in, they will be redirected back to the blog page they wanted to comment on.</li>
+ <li>Comment pages should include the name/link to the blogpost being commented on.</li>
+ </ul>
+ </td>
+ </tr>
+ <tr>
+ <td>User authentication pages</td>
+ <td><code>/accounts/<em>&lt;standard urls&gt;</em></code></td>
+ <td>
+ <p>Standard Django authentication pages for logging in, out and setting the password:</p>
+
+ <ul>
+ <li>Login/out should be accessible via sidebar links.</li>
+ </ul>
+ </td>
+ </tr>
+ <tr>
+ <td>Admin site</td>
+ <td><code>/admin/<em>&lt;standard urls&gt;</em></code></td>
+ <td>
+ <p>Admin site should be enabled to allow create/edit/delete of blog posts, blog authors and blog comments (this is the mechanism for bloggers to create new blog posts):</p>
+
+ <ul>
+ <li>Admin site blog posts records should display the list of associated comments inline (below each blog post).</li>
+ <li>Comment names in the Admin site are created by truncating the comment description to 75 characters.</li>
+ <li>Other types of records can use basic registration.</li>
+ </ul>
+ </td>
+ </tr>
+ </tbody>
+</table>
+
+<p>In addition you should write some basic tests to verify:</p>
+
+<ul>
+ <li>All model fields have the correct label and length.</li>
+ <li>All models have the expected object name (e.g.<code> __str__()</code> returns the expected value).</li>
+ <li>Models have the expected URL for individual Blog and Comment records (e.g. <code>get_absolute_url()</code> returns the expected URL).</li>
+ <li>The BlogListView (all-blog page) is accessible at the expected location (e.g. /blog/blogs)</li>
+ <li>The BlogListView (all-blog page) is accessible at the expected named url (e.g. 'blogs')</li>
+ <li>The BlogListView (all-blog page) uses the expected template (e.g. the default)</li>
+ <li>The BlogListView paginates records by 5 (at least on the first page)</li>
+</ul>
+
+<div class="note">
+<p><strong>Note</strong>: There are of course many other tests you can run. Use your discretion, but we'll expect you to do at least the tests above.</p>
+</div>
+
+<p>The following section shows <a href="#Screenshots">screenshots</a> of a site that implements the requirements above.</p>
+
+<h2 id="Screenshots">Screenshots</h2>
+
+<p>The following screenshot provide an example of what the finished program should output.</p>
+
+<h3 id="List_of_all_blog_posts">List of all blog posts</h3>
+
+<p>This displays the list of all blog posts (accessible from the "All blogs" link in the sidebar). Things to note:</p>
+
+<ul>
+ <li>The sidebar also lists the logged in user.</li>
+ <li>Individual blog posts and bloggers are accessible as links in the page.</li>
+ <li>Pagination is enabled (in groups of 5)</li>
+ <li>Ordering is newest to oldest.</li>
+</ul>
+
+<p><img alt="List of all blogs" src="https://mdn.mozillademos.org/files/14319/diyblog_allblogs.png" style="border-style: solid; border-width: 1px; display: block; height: 363px; margin: 0px auto; width: 986px;"></p>
+
+<h3 id="List_of_all_bloggers">List of all bloggers</h3>
+
+<p>This provides links to all bloggers, as linked from the "All bloggers" link in the sidebar. In this case we can see from the sidebar that no user is logged in.</p>
+
+<p><img alt="List of all bloggers" src="https://mdn.mozillademos.org/files/14321/diyblog_blog_allbloggers.png" style="border-style: solid; border-width: 1px; display: block; height: 256px; margin: 0px auto; width: 493px;"></p>
+
+<h3 id="Blog_detail_page">Blog detail page</h3>
+
+<p>This shows the detail page for a particular blog.</p>
+
+<p><img alt="Blog detail with add comment link" src="https://mdn.mozillademos.org/files/14323/diyblog_blog_detail_add_comment.png" style="border-style: solid; border-width: 1px; display: block; height: 640px; margin: 0px auto; width: 986px;"></p>
+
+<p>Note that the comments have a date <em>and</em> time, and are ordered from oldest to newest (opposite of blog ordering). At the end we have a link for accessing the form to add a new comment. If a user is not logged in we'd instead see a suggestion to log in.</p>
+
+<p><img alt="Comment link when not logged in" src="https://mdn.mozillademos.org/files/14325/diyblog_blog_detail_not_logged_in.png" style="border-style: solid; border-width: 1px; display: block; height: 129px; margin: 0px auto; width: 646px;"></p>
+
+<h3 id="Add_comment_form">Add comment form</h3>
+
+<p>This is the form to add comments. Note that we're logged in. When this succeeds we should be taken back to the associated blog post page.</p>
+
+<p><img alt="Add comment form" src="https://mdn.mozillademos.org/files/14329/diyblog_comment_form.png" style="border-style: solid; border-width: 1px; display: block; height: 385px; margin: 0px auto; width: 778px;"></p>
+
+<h3 id="Author_bio">Author bio</h3>
+
+<p>This displays bio information for a blogger along with their blog posts list.</p>
+
+<p><img alt="Blogger detail page" src="https://mdn.mozillademos.org/files/14327/diyblog_blogger_detail.png" style="border-style: solid; border-width: 1px; display: block; height: 379px; margin: 0px auto; width: 982px;"></p>
+
+<h2 id="Steps_to_complete">Steps to complete</h2>
+
+<p>The following sections describe what you need to do.</p>
+
+<ol>
+ <li>Create a skeleton project and web application for the site (as described in <a href="/en-US/docs/Learn/Server-side/Django/skeleton_website">Django Tutorial Part 2: Creating a skeleton website</a>). You might use 'diyblog' for the project name and 'blog' for the application name.</li>
+ <li>Create models for the Blog posts, Comments, and any other objects needed. When thinking about your design, remember:
+ <ul>
+ <li>Each comment will have only one blog, but a blog may have many comments.</li>
+ <li>Blog posts and comments must be sorted by post date.</li>
+ <li>Not every user will necessarily be a blog author though any user may be a commenter.</li>
+ <li>Blog authors must also include bio information.</li>
+ </ul>
+ </li>
+ <li>Run migrations for your new models and create a superuser.</li>
+ <li>Use the admin site to create some example blog posts and blog comments.</li>
+ <li>Create views, templates, and URL configurations for blog post and blogger list pages.</li>
+ <li>Create views, templates, and URL configurations for blog post and blogger detail pages.</li>
+ <li>Create a page with a form for adding new comments (remember to make this only available to logged in users!)</li>
+</ol>
+
+<h2 id="Hints_and_tips">Hints and tips</h2>
+
+<p>This project is very similar to the <a href="/en-US/docs/Learn/Server-side/Django/Tutorial_local_library_website">LocalLibrary</a> tutorial. You will be able to set up the skeleton, user login/logout behaviour, support for static files, views, URLs, forms, base templates and admin site configuration using almost all the same approaches.</p>
+
+<p>Some general hints:</p>
+
+<ol>
+ <li>The index page can be implemented as a basic function view and template (just like for the locallibrary).</li>
+ <li>The list view for blog posts and bloggers, and the detail view for blog posts can be created using the <a href="/en-US/docs/Learn/Server-side/Django/Generic_views">generic list and detail views</a>.</li>
+ <li>The list of blog posts for a particular author can be created by using a generic list Blog list view and filtering for blog object that match the specified author.
+ <ul>
+ <li>You will have to implement <code>get_queryset(self)</code> to do the filtering (much like in our library class <code>LoanedBooksAllListView</code>) and get the author information from the URL.</li>
+ <li>You will also need to pass the name of the author to the page in the context. To do this in a class-based view you need to implement <code>get_context_data()</code> (discussed below).</li>
+ </ul>
+ </li>
+ <li>The <em>add comment</em> form can be created using a function-based view (and associated model and form) or using a generic <code>CreateView</code>. If you use a <code>CreateView</code> (recommended) then:
+ <ul>
+ <li>You will also need to pass the name of the blog post to the comment page in the context (implement <code>get_context_data()</code> as discussed below).</li>
+ <li>The form should only display the comment "description" for user entry (date and associated blog post should not be editable). Since they won't be in the form itself, your code will need to set the comment's author in the<code> form_valid()</code> function so it can be saved into the model (<a href="https://docs.djangoproject.com/en/1.10/topics/class-based-views/generic-editing/#models-and-request-user">as described here</a> — Django docs). In that same function we set the associated blog. A possible implementation is shown below (<code>pk</code> is a blog id passed in from the URL/URL configuration).
+ <pre class="brush: python"> def form_valid(self, form):
+ """
+ Add author and associated blog to form data before setting it as valid (so it is saved to model)
+ """
+ #Add logged-in user as author of comment
+ form.instance.author = self.request.user
+ #Associate comment with blog based on passed id
+ form.instance.blog=get_object_or_404(Blog, pk = self.kwargs['pk'])
+ # Call super-class form validation behaviour
+ return super(BlogCommentCreate, self).form_valid(form)
+</pre>
+ </li>
+ <li>You will need to provide a success URL to redirect to after the form validates; this should be the original blog. To do this you will need to override <code>get_success_url()</code> and "reverse" the URL for the original blog. You can get the required blog ID using the <code>self.kwargs</code> attribute, as shown in the <code>form_valid()</code> method above.</li>
+ </ul>
+ </li>
+</ol>
+
+<p>We briefly talked about passing a context to the template in a class-based view in the <a href="https://developer.mozilla.org/en-US/docs/Learn/Server-side/Django/Generic_views#Overriding_methods_in_class-based_views">Django Tutorial Part 6: Generic list and detail views</a> topic. To do this you need to override <code>get_queryset()</code> (first getting the existing context, updating it with whatever additional variables you want to pass to the template, and then returning the updated context. For example, the code fragment below shows how you can add a blogger object to the context based on their <code>BlogAuthor</code> id.</p>
+
+<pre class="brush: python">class SomeView(generic.ListView):
+ ...
+
+ def get_context_data(self, **kwargs):
+        # Call the base implementation first to get a context
+        context = super(SomeView, self).get_context_data(**kwargs)
+        # Get the blogger object from the "pk" URL parameter and add it to the context
+        context['blogger'] = get_object_or_404(BlogAuthor, pk = self.kwargs['pk'])
+        return context
+</pre>
+
+<h2 id="Assessment">Assessment</h2>
+
+<p>The assessment for this task is <a href="https://github.com/mdn/django-diy-blog/blob/master/MarkingGuide.md">available on Github here</a>. This assessment is primarily based on how well your application meets the requirements we listed above, though there are some parts of the assessment that check your code uses appropriate models, and that you have written at least some test code. When you're done, you can check out our <a href="https://github.com/mdn/django-diy-blog">the finished example</a> which reflects a "full marks" project.</p>
+
+<p>Once you've completed this module you've also finished all the MDN content for learning basic Django server-side website programming! We hope you enjoyed this module and feel you have a good grasp of the basics!</p>
+
+<p>{{PreviousMenu("Learn/Server-side/Django/web_application_security", "Learn/Server-side/Django")}}</p>
+
+<p> </p>
+
+<h2 id="En_este_modulo">En este modulo</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>
diff --git a/files/es/learn/server-side/django/forms/index.html b/files/es/learn/server-side/django/forms/index.html
new file mode 100644
index 0000000000..4160e537b6
--- /dev/null
+++ b/files/es/learn/server-side/django/forms/index.html
@@ -0,0 +1,661 @@
+---
+title: 'Tutorial de Django Parte 9: Trabajo con formularios'
+slug: Learn/Server-side/Django/Forms
+translation_of: Learn/Server-side/Django/Forms
+---
+<div>{{LearnSidebar}}</div>
+
+<div>{{PreviousMenuNext("Learn/Server-side/Django/authentication", "Learn/Server-side/Django/Testing", "Learn/Server-side/Django")}}</div>
+
+<p class="summary">En este tutorial te mostraremos cómo trabajar con Formularios HTML en Django, y en particular, la forma más fácil de escribir formularios para crear, actualizar y borrar instancias de modelo. Como parte de esta demostración extenderemos el sitio web <a href="https://developer.mozilla.org/en-US/docs/Learn/Server-side/Django/Tutorial_local_library_website">LocalLibrary</a> de manera que los bibliotecarios puedan renovar libros, y crear, actualizar y borrar autores utilizando nuestros propios formularios (en vez de utilizar la aplicación de administración).</p>
+
+<table class="learn-box standard-table">
+ <tbody>
+ <tr>
+ <th scope="row">Requisitos previos:</th>
+ <td>Completar todos los temas de tutoriales anteriores, incluyendo <a href="/en-US/docs/Learn/Server-side/Django/authentication_and_sessions">Tutorial Django Parte 8: Autenticación y permisos de usuarios</a>.</td>
+ </tr>
+ <tr>
+ <th scope="row">Objectivo:</th>
+ <td>Comprender como escribir formularios para obtener informacion de los usuarios y actualizar la base de datos. Comprender cómo las vistas de edición basadas en clase genérica pueden simplificar enormemente la creación de formularios trabajando con un solo modelo.</td>
+ </tr>
+ </tbody>
+</table>
+
+<h2 id="Visión_General">Visión General</h2>
+
+<p>Un <a href="/en-US/docs/Web/Guide/HTML/Forms">Formulario HTML </a>es un conjunto de uno o más campos/widgets en una página web, que pueden ser usados para recolectar información de los usuarios para el envío a un servidor. Los formularios son un mecanismo flexible para recolectar datos de entrada porque <span id="result_box" lang="es"><span>son widgets adecuados para ingresar diferentes tipos de datos, incluyendo campos de texto, checkboxes, radio buttons, selector de fechas, etc. Los formularios son también una forma relativamente segura de compartir datos con el servidor, ya que permiten enviar información en peticiones </span></span><code>POST</code> <span class="short_text" id="result_box" lang="es"><span>con protección de falsificación de solicitud entre sitios.</span></span></p>
+
+<p>Si bien nosotros aún no hemos creado ningún formulario en este tutorial todavia, ya lo hemos encontrado en el sitio de administración de Django; por ejemplo, la captura de pantalla  de abajo muestra un formulario para editar uno de nuestros modelos de <a href="/en-US/docs/Learn/Server-side/Django/Models">Libro</a>, compuesto de un número de listas de selección y editores de texto.</p>
+
+<p><img alt="Admin Site - Book Add" src="https://mdn.mozillademos.org/files/13979/admin_book_add.png" style="border-style: solid; border-width: 1px; display: block; margin: 0px auto;"></p>
+
+<p>Trabajar con formularios puede ser complicado! Los desarrolladores deben de escribir código HTML para el formulario, validar y adecuadamente limpiar los datos ingresados en el servidor (y posiblemente también en el browser o navegador), volver a publicar el formulario con mensajes de error para informar a los usuarios de cualquier campo invalido, manejar los datos cuando hayan sido enviados exitosamente y finalmente, responder al usuario de alguna manera, para indicar el éxito de la operación.  Django Forms  elimina mucho del trabajo de todos estos pasos, al proporcionar un marco de trabajo que le permite definir formularios y sus campos a travéz de programación y luego, utilizar estos objetos para generar el código HTML del formulario y manejar gran parte de la validación y la interacción del usuario.</p>
+
+<p>En este tutorial vamos a mostrarle algunas de las formas de crear y trabajar con formularios y en particular, cómo las vistas genéricas de edición de formularios pueden significativamente reducir la cantidad del trabajo necesario para crear formularios para manejar sus modelos. En el camino iremos extendiendo nuestra aplicación <em>LocalLibrary</em>  por agregar un formulario para permitir a los  bibliotecarios renovar libros de la biblioteca y crearemos páginas para crear, editar o eliminar libros y autores (reproduciendo una versión básica del formulario mostrado arriba para editar libros).</p>
+
+<h2 id="Formularios_HTML">Formularios HTML</h2>
+
+<p>Primero, una breve revisión de <a href="/en-US/docs/Learn/HTML/Forms">Formularios HTML</a>. Considere un  simple formulario HTML, con un solo campo de texto para entrar el nombre de algun "equipo" y su etiqueta asociada:</p>
+
+<p><img alt="Simple name field example in HTML form" src="https://mdn.mozillademos.org/files/14117/form_example_name_field.png" style="border-style: solid; border-width: 1px; display: block; height: 44px; margin: 0px auto; width: 399px;"></p>
+
+<p>El formulario es definido en HTML como una colección de elementos dentro de las etiquetas  <code>&lt;form&gt;...&lt;/form&gt;</code>, conteniendo por lo menos un elemento de entrada - <code>input</code> de tipo enviar - <code>type="submit"</code>.</p>
+
+<pre class="brush: html notranslate">&lt;form action="/team_name_url/" method="post"&gt;
+    &lt;label for="team_name"&gt;Enter name: &lt;/label&gt;
+    &lt;input id="team_name" type="text" name="name_field" value="Default name for team."&gt;
+    &lt;input type="submit" value="OK"&gt;
+&lt;/form&gt;</pre>
+
+<p>Si bien acá solo tenemos un campo de texto para ingresar el nombre del equipo, un formulario puede tener cualquier número de otros elementos de entrada y sus etiquetas asociadas. El tipo del atributo del campo - <code>type</code>  define que clase de widget será mostrado. El nombre - <code>name</code> y el identificador - <code>id</code> del campo son usados para identificar el campo en JavaScript/CSS/HTML, mientrras que el valor - <code>value</code> define el valor inicial para el campo cuando este se muestra por primera vez. La etiqueta del equipo correspondiente es especificada utilizando la etiqueta - <code style="font-style: normal; font-weight: normal;">label</code> (consulte "Enter name" arriba), con un campo  <code style="font-style: normal; font-weight: normal;">for</code>  que contiene el valor de identificación  <code style="font-style: normal; font-weight: normal;">id</code> de la entrada asociada <code style="font-style: normal; font-weight: normal;">input</code>.</p>
+
+<p>La entrada de envío - <code>submit</code> se mostrará como un botón (de forma predeterminada)  que el usuario puede presionar para cargar los datos en todos los demás elementos de entrada en el formulario al servidor (en este caso, solo el nombre del equipo - <code>team_name</code>). Los atributos del formulario definen el metodo -  <code>method</code> de HTTP usado para enviar los datos y el destino de los datos al servidor (<code>action</code>):</p>
+
+<ul>
+ <li><code>action</code>: El recurso URL - resource/URL donde los datos serán enviados para su procesamiento cuando se envíe el formulario. Si esto no se establece (o se deja como una cadena vacía), entonces el formulario será enviado de regreso al URL de la página actual.</li>
+ <li><code>method</code>: El método utilizado por HTTP para enviar los datos: <em>post</em> o <em>get</em>.
+ <ul>
+ <li>El método  <code>POST</code> siempre debe usarse si los datos enviados van a resultar en un cambio en la base de datos del servidor porque esto puede ser mas resistente a un ataque de solicitud de falsificación entre sitios (cross-site forgery request attacks).</li>
+ <li>El método <code>GET</code> unicamente debe usarse para cuando los formularios no cambian datos de usuario (por ejemplo, un formulario de búsqueda). También, este es recomendado para cuando desee guardar, marcar o compartir el URL.</li>
+ </ul>
+ </li>
+</ul>
+
+<p>El rol del servidor es primero procesar el estado inicial del formulario ya sea conteniendo campos en blanco o completados previamente con valores inciales. Después de que el usuario presiona el botón de Enviar, el servidor recibirá los datos del formulario con valores del navegador web y deberá validar  la información. Si el formulario contiene datos inválidos, el servidor deberá desplegar el formulario de nuevo, esta vez con datos ingresados por el usuario en campos "válidos" y mensajes para describir el problema en los campos con valor inválidos. Una vez el servidor recibe una petición con todos los datos de formulario válidos, este puede realizar una acción apropiada (por ejemplo, guardando los datos, regresando el resultado de una búsqueda, cargando un archivo, etc.) y luego notificar al usuario.</p>
+
+<p>Como puede imaginar, crear el código HTML, validar los datos retornados, redesplegar los datos ingresados con errores reportados si fuera necesario y realizar las operaciones deseadas sobre los datos válidos puede todo tomar bastante esfuerzo para "hacerlo bien". Django hace esto mucho más fácil por quitar parte del trabajo pesado y código repetitivo!</p>
+
+<h2 id="Proceso_del_manejo_de_formularios_de_Django">Proceso del manejo de formularios de Django</h2>
+
+<p>El manejo de formularios de Django utiliza las mismas técnicas que aprendimos en tutoriales anteriores (para mostrar información sobre nuestros modelos):  la vista recibe una solicitud, realiza cualquier acción requerida incluyendo leer datos de los modelos, luego generar y devolver una página HTML (de una platilla, en la que pasamos un <em>contexto </em>conteniendo los datos a ser desplegados).  Lo que hace las cosas más complicadas  es que el servidor también necesita poder procesar los datos proporcionados por el usuario y volver a mostrar la página si hay algún error.</p>
+
+<p>A continuación se muestra un diagram de flujo del proceso de cómo Django maneja las solicitudes de formulario, comenzando con una solicitud de una página que contiene un formulario (mostrado en verde).</p>
+
+<p><img alt="Updated form handling process doc." src="https://mdn.mozillademos.org/files/14205/Form%20Handling%20-%20Standard.png" style="display: block; height: 569px; margin: 0px auto; width: 800px;"></p>
+
+<p>Basado en el diagrama de anterior, las principales pasos que  hace el proceso del manejo de formularios de Django son:</p>
+
+<ol>
+ <li>Mostrar el formulario predeterminado la primera vez que es solicitado por el usuario.
+ <ul>
+ <li>El formulario puede contener campos en blanco (por ejemplo, si está creando un registro nuevo), o puede estar rellenado previamente con valores iniciales (por ejemplo, si está modificando un registro o si tiene valores iniciales predeterminados útiles).</li>
+ <li>El formulario se conoce como no vinculado en este punto porque no esta asociado con ningún dato ingresado por el usuario (aunque pueda tener valores iniciales).</li>
+ </ul>
+ </li>
+ <li>Recibir datos de una solicitud de envío y vincularlo al formulario.
+ <ul>
+ <li>La vinculacion de datos al formulario significa que los datos ingresados por el usuario y cualquier error están disponibles cuando necesitamos volver a desplegar el formulario. </li>
+ </ul>
+ </li>
+ <li>Limpiar y validar los datos. Clean and validate the data.
+ <ul>
+ <li>La limpieza de los datos realiza una sanitización de la entrada (por ejemplo, remover caracteres no válidos que podrían ser usados para enviar contenido malicioso al servidor) y convertirlos en tipos consistente de Python.</li>
+ <li>La validación verifica que los valores sean apropiados para el campo (por ejemplo, que esten en el rango correcto de fechas, no sean demasiado cortos ni demasiado largos, etc.)</li>
+ </ul>
+ </li>
+ <li>Si algún dato es no válido, volver a mostrar el formulario, esta vez con cualquier valor rellenado por el usuario y los mensajes de error para los campos con problemas.</li>
+ <li>Si todos los datos son válidos, realizar las acciones requeridas (por ejemplo, guardar los datos, enviar un correo electrónico, devolver el resultado de una búsqueda, cargar un archivo, etc)</li>
+ <li>Una vez todas las acciones se hayan completado, redirijir al usuario a otra página</li>
+</ol>
+
+<p>Django provee una serie de herramientas y enfoques para ayudarlo con las tareas detalladas anteriormente. La más fundamental es la clase <code>Form</code>, la cuál simplifica la generación de formularios HTML y la limpieza y validación de datos. En la siguiente sección describimos cómo funcionan los formularios usando el ejemplo práctico de una página para permitir a los bibliotecarios renovar libros.</p>
+
+<div class="note">
+<p><strong>Nota:</strong>  Comprender como se usa la clase <code>Form</code>  lo ayudará cuando analicemos las clases de marco de formulario de más "alto nivel" de Django.</p>
+</div>
+
+<h2 id="Renew-book_form_usando_un_Form_y_la_funcion_view">Renew-book form usando un Form y la funcion view</h2>
+
+<p>A continuación, vamos a añadir una página que permita a los bibilotecarios renovar los libros prestados. Para hacer esto crearemos un formulario que permita a los usuarios introducir una fecha. Rellenaremos el campo con un valor inicial de 3 semanas desde la fecha actual (el periodo de préstamo normal), y añadiremos alguna validación para asegurar que el bibilotecario no pueda introducir una fecha pasada o una demasiado lejana en el futuro. Cuando se haya introducido una fecha válida, la escribiremos sobre el campo <code>BookInstance.due_back</code> del registro actual.</p>
+
+<p>El ejemplo utilizará una vista basada en funciones y una clase <code>Form</code>. Las próximas secciones explican como los formularios funcionan, y los cambios que necesitas realizar para continuar adelante con nuestro proyecto <em>LocalLibrary</em>.</p>
+
+<h3 id="Clase_Form">Clase Form</h3>
+
+<p>La clase Form es el corazón del sistema de manejo de formularios de Django. Especifica los campos en el formulario, su diseño, widgets de visualización, etiquetas, valores iniciales, valores válidos y (una vez validados) los mensajes de error asociados con campos no válidos. La clase también proporciona métodos para renderizarse en plantillas usando formatos predefinidos (tablas, listas, etc.) o para obtener el valor de cualquier elemento (permitiendo el renderizado manual de grano fino).</p>
+
+<h4 id="Declarando_un_Form">Declarando un Form</h4>
+
+<p>La sintaxis de declaración para un formulario es muy similar a la de declarar un modelo, y comparte los mismos tipos de campo (y algunos parámetros similares). Esto tiene sentido porque en ambos casos debemos asegurarnos de que cada campo maneja los tipos correctos de datos, está restringido a datos válidos y tiene una descripción para la visualización / documentación.</p>
+
+<p>Para crear un formulario (<code>Form</code>) es necesario importar la libreria f<code>orms</code>, derivada de la clase <code>Form</code>, y tambien declarar los campos del formulario. A continuación se muestra una clase de formulario muy básica para nuestro formulario de renovación de libros de la biblioteca:</p>
+
+<pre class="brush: python notranslate">from django import forms
+
+class RenewBookForm(forms.Form):
+    renewal_date = forms.DateField(help_text="Enter a date between now and 4 weeks (default 3).")
+</pre>
+
+<h4 id="Campos_del_Form">Campos del Form</h4>
+
+<p>En este caso, tenemos un único <code><a href="https://docs.djangoproject.com/en/1.10/ref/forms/fields/#datefield">DateField</a></code> para ingresar la fecha de renovación que se mostrará en HTML con un valor en blanco, la etiqueta predeterminada "Fecha de renovación:" y algún texto de uso útil: "Ingrese una fecha entre ahora y 4 semanas (valor predeterminado 3 semanas)." Como no se especifica ninguno de los otros argumentos opcionales, el campo aceptará fechas utilizando los input_formats: AAAA-MM-DD (2016-11-06), MM / DD / AAAA (26/02/2016), MM / DD / AA ( 25/10/16), y se representará con el widget predeterminado: DateInput.</p>
+
+<p>Hay muchos otros tipos de campos de formulario, que reconocerá en gran medida por su similitud con las clases de campo de modelo equivalentes: <a href="https://docs.djangoproject.com/en/1.10/ref/forms/fields/#booleanfield"><code>BooleanField</code></a>, <a href="https://docs.djangoproject.com/en/1.10/ref/forms/fields/#charfield"><code>CharField</code></a>, <a href="https://docs.djangoproject.com/en/1.10/ref/forms/fields/#choicefield"><code>ChoiceField</code></a>, <a href="https://docs.djangoproject.com/en/1.10/ref/forms/fields/#typedchoicefield"><code>TypedChoiceField</code></a>, <a href="https://docs.djangoproject.com/en/1.10/ref/forms/fields/#datefield"><code>DateField</code></a>, <a href="https://docs.djangoproject.com/en/1.10/ref/forms/fields/#datetimefield"><code>DateTimeField</code></a>, <a href="https://docs.djangoproject.com/en/1.10/ref/forms/fields/#decimalfield"><code>DecimalField</code></a>, <a href="https://docs.djangoproject.com/en/1.10/ref/forms/fields/#durationfield"><code>DurationField</code></a>, <a href="https://docs.djangoproject.com/en/1.10/ref/forms/fields/#emailfield"><code>EmailField</code></a>, <a href="https://docs.djangoproject.com/en/1.10/ref/forms/fields/#filefield"><code>FileField</code></a>, <a href="https://docs.djangoproject.com/en/1.10/ref/forms/fields/#filepathfield"><code>FilePathField</code></a>, <a href="https://docs.djangoproject.com/en/1.10/ref/forms/fields/#floatfield"><code>FloatField</code></a>, <a href="https://docs.djangoproject.com/en/1.10/ref/forms/fields/#imagefield"><code>ImageField</code></a>, <a href="https://docs.djangoproject.com/en/1.10/ref/forms/fields/#integerfield"><code>IntegerField</code></a>, <a href="https://docs.djangoproject.com/en/1.10/ref/forms/fields/#genericipaddressfield"><code>GenericIPAddressField</code></a>, <a href="https://docs.djangoproject.com/en/1.10/ref/forms/fields/#multiplechoicefield"><code>MultipleChoiceField</code></a>, <a href="https://docs.djangoproject.com/en/1.10/ref/forms/fields/#typedmultiplechoicefield"><code>TypedMultipleChoiceField</code></a>, <a href="https://docs.djangoproject.com/en/1.10/ref/forms/fields/#nullbooleanfield"><code>NullBooleanField</code></a>, <a href="https://docs.djangoproject.com/en/1.10/ref/forms/fields/#regexfield"><code>RegexField</code></a>, <a href="https://docs.djangoproject.com/en/1.10/ref/forms/fields/#slugfield"><code>SlugField</code></a>, <a href="https://docs.djangoproject.com/en/1.10/ref/forms/fields/#timefield"><code>TimeField</code></a>, <a href="https://docs.djangoproject.com/en/1.10/ref/forms/fields/#urlfield"><code>URLField</code></a>, <a href="https://docs.djangoproject.com/en/1.10/ref/forms/fields/#uuidfield"><code>UUIDField</code></a>, <a href="https://docs.djangoproject.com/en/1.10/ref/forms/fields/#combofield"><code>ComboField</code></a>, <a href="https://docs.djangoproject.com/en/1.10/ref/forms/fields/#multivaluefield"><code>MultiValueField</code></a>, <a href="https://docs.djangoproject.com/en/1.10/ref/forms/fields/#splitdatetimefield"><code>SplitDateTimeField</code></a>, <a href="https://docs.djangoproject.com/en/1.10/ref/forms/fields/#modelmultiplechoicefield"><code>ModelMultipleChoiceField</code></a>, <a href="https://docs.djangoproject.com/en/1.10/ref/forms/fields/#modelchoicefield"><code>ModelChoiceField</code></a>​​​​.</p>
+
+<p>Los argumentos que son comunes a la mayoría de los campos se enumeran a continuación (estos tienen valores predeterminados sensibles):</p>
+
+<ul>
+ <li><a href="https://docs.djangoproject.com/en/1.10/ref/forms/fields/#required">required</a>: Si es <code>True</code>, el campo no se puede dejar en blanco o dar un valor  <code>None</code>. Los Campos son obligatorios por defecto, tambien puedes establecer <code>required=False</code> para permitir valores en blanco en el formulario.</li>
+ <li><a href="https://docs.djangoproject.com/en/1.10/ref/forms/fields/#label">label</a>: label es usado cuando renderizamos el campo en  HTML. Si <a href="https://docs.djangoproject.com/en/1.10/ref/forms/fields/#label">label</a> no es especificado entonces Django crearía uno a partir del nombre del campo al poner en mayúscula la primera letra y reemplazar los guiones bajos por espacios (por ejemplo. <em>Renewal date</em>).</li>
+ <li><a href="https://docs.djangoproject.com/en/1.10/ref/forms/fields/#label-suffix">label_suffix</a>: <span class="tlid-translation translation" lang="es"><span title="">Por defecto, se muestran dos puntos después de la etiqueta</span></span> (ejemplo. Renewal date<strong>:</strong>). Este argumento le permite especificar como sufijo diferente que contiene otros caracteres.</li>
+ <li><a href="https://docs.djangoproject.com/en/1.10/ref/forms/fields/#initial">initial</a>: El valor inicial para el campo cuando es mostrado en el formulario.</li>
+ <li><a href="https://docs.djangoproject.com/en/1.10/ref/forms/fields/#widget">widget</a>: <span class="tlid-translation translation" lang="es"><span title="">El widget de visualización para usar</span></span>.</li>
+ <li><a href="https://docs.djangoproject.com/en/1.10/ref/forms/fields/#help-text">help_text</a> (como se ve en el ejemplo anterior): texto adicional que se puede mostrar en formularios para explicar cómo usar el campo.</li>
+ <li><a href="https://docs.djangoproject.com/en/1.10/ref/forms/fields/#error-messages">error_messages</a>: Una lista de mensajes de error para el campo. Puede reemplazarlos con sus propios mensajes si es necesario.</li>
+ <li><a href="https://docs.djangoproject.com/en/1.10/ref/forms/fields/#validators">validators</a>: Una lista de funciones que se invocarán en el campo cuando se valide.</li>
+ <li><a href="https://docs.djangoproject.com/en/1.10/ref/forms/fields/#localize">localize</a>: Permite la localización de la entrada de datos del formulario (consulte el enlace para obtener más información).</li>
+ <li><a href="https://docs.djangoproject.com/en/1.10/ref/forms/fields/#disabled">disabled</a>: El campo se muestra pero su valor no se puede editar si esto es <code>True</code>. Por defecto es <code>False</code>.</li>
+</ul>
+
+<h4 id="Validación">Validación</h4>
+
+<p>Django proporciona numerosos lugares donde puede validar sus datos. La forma más fácil de validar un solo campo es anular el método <code>clean_<strong>&lt;fieldname&gt;</strong>()</code> para el campo que desea verificar. Entonces, por ejemplo, podemos validar lo ingresado <code>renewal_date</code> los valores son entre ahora y 4 semanas mediante la implementación <code>clean_<strong>renewal_date</strong>() </code>como se muestra abajo.</p>
+
+<pre class="brush: python notranslate">from django import forms
+
+<strong>from django.core.exceptions import ValidationError
+from django.utils.translation import ugettext_lazy as _
+import datetime #for checking renewal date range.
+</strong>
+class RenewBookForm(forms.Form):
+    renewal_date = forms.DateField(help_text="Enter a date between now and 4 weeks (default 3).")
+
+<strong>    def clean_renewal_date(self):
+        data = self.cleaned_data['renewal_date']
+
+        #Check date is not in past.
+        if data &lt; datetime.date.today():
+            raise ValidationError(_('Invalid date - renewal in past'))
+
+        #Check date is in range librarian allowed to change (+4 weeks).
+        if data &gt; datetime.date.today() + datetime.timedelta(weeks=4):
+            raise ValidationError(_('Invalid date - renewal more than 4 weeks ahead'))
+
+        # Remember to always return the cleaned data.
+        return data</strong></pre>
+
+<p>Hay dos cosas importantes a tener en cuenta. El primero es que obtenemos nuestros datos usando <code>self.cleaned_data['renewal_date']</code> y que devolvemos estos datos si los cambiamos o no al final de la función. Este paso nos permite "limpiar" y desinfectar los datos de entrada potencialmente insegura utilizando los validadores predeterminados, y convertirlos al tipo estándar correcto para los datos (en este caso, un objeto Python  <code>datetime.datetime</code>).</p>
+
+<p>El segundo punto es que si un valor cae fuera de nuestro rango, elevamos un <code>ValidationError</code>, especificando el texto de error que queremos mostrar en el formulario si se ingresa un valor no válido. El ejemplo anterior también envuelve este texto en uno de las <a href="https://docs.djangoproject.com/en/1.10/topics/i18n/translation/">funciones de traduccion de Django</a> <code>ugettext_lazy()</code> (importado como <code>_()</code>), lo cual es una buena práctica si desea traducir su sitio más tarde.</p>
+
+<div class="note">
+<p><strong>Nota:</strong> Existen muchos otros métodos y ejemplos para validar formularios en <a href="https://docs.djangoproject.com/en/1.10/ref/forms/validation/">Validacion de Formularios  y campos</a> (Django docs). Por ejemplo, en los casos en que tiene varios campos que dependen unos de otros, puede anular la función <a href="https://docs.djangoproject.com/en/1.10/ref/forms/api/#django.forms.Form.clean">Form.clean()</a> function y colocar un <code>ValidationError</code>.</p>
+</div>
+
+<p>¡Eso es todo lo que necesitamos para el formulario en este ejemplo!</p>
+
+<h4 id="Copia_el_Formulario">Copia el Formulario</h4>
+
+<p>Crea y abre el archivo <strong>locallibrary/catalog/forms.py</strong> y copie el listado completo del código del bloque anterior en él.</p>
+
+<h3 id="Configuracion_del_URL">Configuracion del URL</h3>
+
+<p>Antes de crear nuestra vista, agreguemos una configuración de URL para la página de renovar libros. Copie la siguiente configuración en la parte inferior de  <strong>locallibrary/catalog/urls.py</strong>.</p>
+
+<pre class="brush: python notranslate">urlpatterns += [
+ url(r'^book/(?P&lt;pk&gt;[-\w]+)/renew/$', views.renew_book_librarian, name='renew-book-librarian'),
+]</pre>
+
+<p>La configuración de URL redirigirá las URL con el formato  <strong>/catalog/book/<em>&lt;bookinstance id&gt;</em>/renew/</strong> a la función llamada <code>renew_book_librarian()</code> en <strong>views.py</strong>, y envia el id de <code>BookInstance</code> como parametro llamado <code>pk</code>.</p>
+
+<div class="note">
+<p><strong>Nota</strong>: Podemos nombrar nuestros datos de URL capturados "pk" como queramos, porque tenemos un control completo sobre la función de vista (no estamos usando una clase de vista de detalles genérica que espere parámetros con un nombre determinado). sin embargo <code>pk</code>, abreviatura de "primary key", es una convención razonable de usar!</p>
+</div>
+
+<h3 id="View">View</h3>
+
+<p>Como se discutió en el <a href="#django_form_handling_process">proceso de manejo de formularios de Django</a> arriba, la vista debe presentar el formulario predeterminado cuando se llama por primera vez y luego volver a representarlo con mensajes de error si los datos no son válidos, o procesar los datos y redirigirlos a una nueva página si los datos son válidos. Para realizar estas diferentes acciones, la vista debe poder saber si se está llamando por primera vez para presentar el formulario predeterminado, o una vez posterior para validar los datos.</p>
+
+<p>Para formularios que usan una solicitud <code>POST</code>  para enviar información al servidor, el patrón más común es que la vista pruebe con el tipo de solicitud  <code>POST</code>  (<code>if request.method == 'POST':</code>) para identificar las solicitudes de validación de formularios y <code>GET</code> (usando una condición <code>else</code> ) para identificar la solicitud de creación de formulario inicial. Si desea enviar sus datos utilizando una solicitud <code>GET</code>  entonces, un enfoque típico para identificar si esta es la primera invocación de vista o posterior es leer los datos del formulario (por ejemplo, leer un valor oculto en el formulario).</p>
+
+<p>El proceso de renovacion de un libro escribira cambios en nuestra base de datos , entonces por convencion usaremos una peticion de tipo <code>POST</code>. El siguiente fragmento de código muestra el patrón (muy estándar) para este tipo de vista de funciones. </p>
+
+<pre class="brush: python notranslate">from django.shortcuts import get_object_or_404
+from django.http import HttpResponseRedirect
+from django.core.urlresolvers import reverse
+import datetime
+
+from .forms import RenewBookForm
+
+def renew_book_librarian(request, pk):
+    book_inst=get_object_or_404(BookInstance, pk = pk)
+
+    # If this is a POST request then process the Form data
+<strong>    if request.method == 'POST':</strong>
+
+        # Create a form instance and populate it with data from the request (binding):
+        form = RenewBookForm(request.POST)
+
+        # Check if the form is valid:
+        <strong>if form.is_valid():</strong>
+            # process the data in form.cleaned_data as required (here we just write it to the model due_back field)
+            book_inst.due_back = form.cleaned_data['renewal_date']
+            book_inst.save()
+
+            # redirect to a new URL:
+            return HttpResponseRedirect(reverse('all-borrowed') )
+
+    # If this is a GET (or any other method) create the default form.
+<strong>    else:</strong>
+        proposed_renewal_date = datetime.date.today() + datetime.timedelta(weeks=3)
+        form = RenewBookForm(initial={'renewal_date': proposed_renewal_date,})
+
+    return render(request, 'catalog/book_renew_librarian.html', {'form': form, 'bookinst':book_inst})</pre>
+
+<p>Primero importamos nuestro formulario (<code>RenewBookForm</code>) y una serie de otros objetos / métodos útiles utilizados en el cuerpo de la función de vista:</p>
+
+<ul>
+ <li><code><a href="https://docs.djangoproject.com/en/1.10/topics/http/shortcuts/#get-object-or-404">get_object_or_404()</a></code>: Devuelve un objeto especificado de un modelo en función de su valor de clave principal y genera una excepción <code>Http404</code> (not found) si el registro no existe. </li>
+ <li><code><a href="https://docs.djangoproject.com/en/1.10/ref/request-response/#django.http.HttpResponseRedirect">HttpResponseRedirect</a></code>: Esto crea una redirección a una URL especificada (HTTP status code 302). </li>
+ <li><code><a href="https://docs.djangoproject.com/en/1.10/ref/urlresolvers/#django.urls.reverse">reverse()</a></code>: Esto genera una URL a partir de un nombre de configuración de URL y un conjunto de argumentos. Es el equivalente en Python de la etiqueta <code>url</code> que hemos estado usando en nuestras plantillas.</li>
+ <li><code><a href="https://docs.python.org/3/library/datetime.html">datetime</a></code>: Una libreria de Python para manejar las fechas y horas (dates and time). </li>
+</ul>
+
+<p>En la vista, primero usamos el argumento <code>pk</code> argument en  <code>get_object_or_404()</code> para obtener el actual <code>BookInstance</code> (si esto no existe, la vista se cerrará inmediatamente y la página mostrará un error "no encontrado"). Si no se trata de una solicitud POST (manejada por la cláusula else), creamos el formulario predeterminado que pasa un valor inicial (initial) para el campo renewal_date (como se muestra en negrita a continuación, esto es 3 semanas desde la fecha actual)..</p>
+
+<pre class="brush: python notranslate"> book_inst=get_object_or_404(BookInstance, pk = pk)
+
+    # If this is a GET (or any other method) create the default form
+    <strong>else:</strong>
+        proposed_renewal_date = datetime.date.today() + datetime.timedelta(<strong>weeks=3</strong>)
+        <strong>form = RenewBookForm(initial={'</strong>renewal_date<strong>': </strong>proposed_renewal_date<strong>,})</strong>
+
+    return render(request, 'catalog/book_renew_librarian.html', {'form': form, 'bookinst':book_inst})</pre>
+
+<p>Después de crear el formulario, llamamos <code>render()</code> para crear la página HTML, especificando la plantilla y un contexto que contiene nuestro formulario. En este caso, el contexto también contiene nuestro <code>BookInstance</code>, que usaremos en la plantilla para proporcionar información sobre el libro que estamos renovando.</p>
+
+<p>Sin embargo, si esto es una solicitud <code>POST</code>, entonces crearemos nuestro objeto <code>form</code> y llenarlo con datos de la solicitud. Este proceso se llama "enlace" (binding) y nos permite validar el formulario. Luego verificamos si el formulario es válido, que ejecuta todo el código de validación en todos los campos, incluido el código genérico para verificar que nuestro campo de fecha sea realmente una fecha válida y nuestra funcion del formulario <code>clean_renewal_date()</code> chequea la fecha que tenga un rango correcto. </p>
+
+<pre class="brush: python notranslate">    book_inst=get_object_or_404(BookInstance, pk = pk)
+
+    # If this is a POST request then process the Form data
+    if request.method == 'POST':
+
+        # Create a form instance and populate it with data from the request (binding):
+<strong>        form = RenewBookForm(request.POST)</strong>
+
+        # Check if the form is valid:
+        if form.is_valid():
+            # process the data in form.cleaned_data as required (here we just write it to the model due_back field)
+            book_inst.due_back = form.cleaned_data['renewal_date']
+            book_inst.save()
+
+            # redirect to a new URL:
+            return HttpResponseRedirect(reverse('all-borrowed') )
+
+    return render(request, 'catalog/book_renew_librarian.html', {'form': form, 'bookinst':book_inst})</pre>
+
+<p>Si el formulario no es válido llamamos <code>render()</code> de nuevo, pero esta vez el valor del formulario pasado en el contexto incluirá mensajes de error.</p>
+
+<p>Si el formulario es válido, entonces podemos comenzar a usar los datos, accediendo a ellos a través del atributo <code>form.cleaned_data</code> (ejemplo <code>data = form.cleaned_data['renewal_date']</code>). Aquí solo guardamos los datos en el valor <code>due_back</code> asociado al objeto <code>BookInstance</code>.</p>
+
+<div class="warning">
+<p><strong>Importante</strong>: Si bien también puede acceder a los datos del formulario directamente a través de la solicitud (por ejemplo <code>request.POST['renewal_date']</code> o <code>request.GET['renewal_date']</code> (si se esta usando una solicitud GET) esto NO es recomendable. Los datos limpios se desinfectan, validan y convierten en tipos compatibles con Python.</p>
+</div>
+
+<p>El paso final en la parte de manejo de formularios de la vista es redirigir a otra página, generalmente una página de "éxito". En este caso usamos <code>HttpResponseRedirect</code> y <code>reverse()</code> para redirigir a la vista llamada  <code>'all-borrowed'</code>(esto fue creado como el "desafío" en <a href="/en-US/docs/Learn/Server-side/Django/authentication_and_sessions#Challenge_yourself">Django Tutorial Part 8: User authentication and permissions</a>).Si no creó esa página, considere redirigir a la página de inicio en la URL '/').</p>
+
+<p>Eso es todo lo necesario para el manejo del formulario en sí, pero aún debemos restringir el acceso a la vista a los bibliotecarios. Probablemente deberíamos crear un nuevo permiso en <code>BookInstance</code> ("<code>can_renew</code>"),pero para simplificar las cosas aquí solo usamos la funcion decorator <code>@permission_required</code>  con nuestro existente permiso <code>can_mark_returned</code>.</p>
+
+<p>La vista final es, por lo tanto, como se muestra a continuación. Copie esto en la parte inferior de <strong>locallibrary/catalog/views.py</strong>.</p>
+
+<pre class="notranslate"><strong>from django.contrib.auth.decorators import permission_required</strong>
+
+from django.shortcuts import get_object_or_404
+from django.http import HttpResponseRedirect
+from django.url import reverse
+import datetime
+
+from .forms import RenewBookForm
+
+<strong>@permission_required('catalog.<code>can_mark_returned</code>')</strong>
+def renew_book_librarian(request, pk):
+    """
+    View function for renewing a specific BookInstance by librarian
+    """
+    book_inst=get_object_or_404(BookInstance, pk = pk)
+
+    # If this is a POST request then process the Form data
+    if request.method == 'POST':
+
+        # Create a form instance and populate it with data from the request (binding):
+        form = RenewBookForm(request.POST)
+
+        # Check if the form is valid:
+        if form.is_valid():
+            # process the data in form.cleaned_data as required (here we just write it to the model due_back field)
+            book_inst.due_back = form.cleaned_data['renewal_date']
+            book_inst.save()
+
+            # redirect to a new URL:
+            return HttpResponseRedirect(reverse('all-borrowed') )
+
+    # If this is a GET (or any other method) create the default form.
+    else:
+        proposed_renewal_date = datetime.date.today() + datetime.timedelta(weeks=3)
+        form = RenewBookForm(initial={'renewal_date': proposed_renewal_date,})
+
+    return render(request, 'catalog/book_renew_librarian.html', {'form': form, 'bookinst':book_inst})
+</pre>
+
+<h3 id="La_plantilla">La plantilla</h3>
+
+<p>Crea la plantilla html referenciada en la vista dentro del directorio (<strong>/catalog/templates/catalog/book_renew_librarian.html</strong>) y copia el codigo a continuacion dentro del archivo que creaste:</p>
+
+<pre class="brush: html notranslate">{% extends "base_generic.html" %}
+{% block content %}
+
+ &lt;h1&gt;Renew: \{{bookinst.book.title}}&lt;/h1&gt;
+ &lt;p&gt;Borrower: \{{bookinst.borrower}}&lt;/p&gt;
+ &lt;p{% if bookinst.is_overdue %} class="text-danger"{% endif %}&gt;Due date: \{{bookinst.due_back}}&lt;/p&gt;
+
+<strong> &lt;form action="" method="post"&gt;
+ {% csrf_token %}
+ &lt;table&gt;
+ \{{ form }}
+  &lt;/table&gt;
+ &lt;input type="submit" value="Submit" /&gt;
+ &lt;/form&gt;</strong>
+
+{% endblock %}</pre>
+
+<p>La mayor parte de esto será completamente familiar de los tutoriales anteriores. Extendemos la plantilla base y luego redefinimos el bloque de contenido. Podemos hacer referencia<code>\{{bookinst}}</code> (y sus variables) porque se pasó al objeto de contexto en la funci'on <code>render()</code>,y los usamos para colocar el título del libro, el prestatario y la fecha de vencimiento original.</p>
+
+<p>El código del formulario es relativamente simple. Primero declaramos las etiquetas del <code>form</code>,especificando dónde se debe enviar el formulario (<code>action</code>) y el  <code>metodo</code> para enviar los datos (en este caso, una "POST HTTP"), si recuerda el <a href="#HTML_forms">HTML Forms</a> resumen en la parte superior de la página, un espacio vacío <code>action</code>como se muestra, significa que los datos del formulario se volverán a publicar en la URL actual de la página (¡que es lo que queremos!). Dentro de las etiquetas definimos la enntrada (input) <code>submit</code>, que un usuario puede presionar para enviar los datos. Esto <code>{% csrf_token %}</code> es agregado justo dentro de las etiquetas de formulario es parte de la protección de falsificación entre sitios de Django.</p>
+
+<div class="note">
+<p><strong>Nota:</strong> Agregue el {% csrf_token%} a cada plantilla de Django que cree que use POST para enviar datos. Esto reducirá la posibilidad de que usuarios malintencionados secuestran formularios.</p>
+</div>
+
+<p>Todo lo que queda es la variable de la plantilla  <code>\{{form}}</code>, <span class="tlid-translation translation" lang="es"><span title="">que pasamos a la plantilla en el diccionario de contexto.</span> <span title="">Quizás, como era de esperar, cuando se usa como se muestra, proporciona la representación predeterminada de todos los campos del formulario, incluidas sus etiquetas, widgets y texto de ayuda; la representación es la que se muestra a continuación:</span></span></p>
+
+<pre class="brush: html notranslate">&lt;tr&gt;
+  &lt;th&gt;&lt;label for="id_renewal_date"&gt;Renewal date:&lt;/label&gt;&lt;/th&gt;
+  &lt;td&gt;
+  &lt;input id="id_renewal_date" name="renewal_date" type="text" value="2016-11-08" required /&gt;
+  &lt;br /&gt;
+  &lt;span class="helptext"&gt;Enter date between now and 4 weeks (default 3 weeks).&lt;/span&gt;
+  &lt;/td&gt;
+&lt;/tr&gt;
+</pre>
+
+<div class="note">
+<p><strong>Nota:</strong> <span class="tlid-translation translation" lang="es"><span title="">Quizás no sea obvio porque solo tenemos un campo, pero de forma predeterminada cada campo se define en su propia fila de tabla (razón por la cual la variable está dentro de la etiqueta</span></span> <code>table  </code>arriba).​​​​Esta misma representación (rendering) se proporciona si hace referencia a la variable de plantilla <code>\{{ form.as_table }}</code>.</p>
+</div>
+
+<p>Si tuviera que ingresar una fecha no válida, también obtendría una lista de los errores que se muestran en la página (en negrita a continuación).</p>
+
+<pre class="brush: html notranslate">&lt;tr&gt;
+  &lt;th&gt;&lt;label for="id_renewal_date"&gt;Renewal date:&lt;/label&gt;&lt;/th&gt;
+  &lt;td&gt;
+<strong>  &lt;ul class="errorlist"&gt;
+  &lt;li&gt;Invalid date - renewal in past&lt;/li&gt;
+  &lt;/ul&gt;</strong>
+  &lt;input id="id_renewal_date" name="renewal_date" type="text" value="2015-11-08" required /&gt;
+  &lt;br /&gt;
+  &lt;span class="helptext"&gt;Enter date between now and 4 weeks (default 3 weeks).&lt;/span&gt;
+ &lt;/td&gt;
+&lt;/tr&gt;</pre>
+
+<h4 id="Otras_formas_de_usar_variable_de_la_plantilla_de_formulario">Otras formas de usar variable de la plantilla de formulario</h4>
+
+<p>Usando <code>\{{form}}</code> como se muestra arriba, cada campo se representa como una fila de la tabla. También puede representar cada campo como un elemento de la lista (usando <code>\{{form.as_ul}}</code> ) o como un parrafo (usando <code>\{{form.as_p}}</code>).</p>
+
+<p>Lo que es aún más genial es que puede tener un control completo sobre la representación de cada parte del formulario, indexando sus propiedades mediante la notación de puntos. Entonces, por ejemplo, podemos acceder a una serie de elementos separados de los campos de <code>renewal_date</code>-</p>
+
+<ul>
+ <li><code>\{{form.renewal_date}}:</code> The whole field.</li>
+ <li><code>\{{form.renewal_date.errors}}</code>: The list of errors.</li>
+ <li><code>\{{form.renewal_date.id_for_label}}</code>: The id of the label.</li>
+ <li><code>\{{form.renewal_date.help_text}}</code>: The field help text.</li>
+ <li>etc!</li>
+</ul>
+
+<p>Para obtener más ejemplos de cómo reproducir manualmente los formularios en plantillas y recorrer dinámicamente los campos de la plantilla, vea <a href="https://docs.djangoproject.com/en/1.10/topics/forms/#rendering-fields-manually">Working with forms &gt; Rendering fields manually</a> (Django docs).</p>
+
+<h3 id="Probando_la_página">Probando la página</h3>
+
+<p>Si aceptó el "desafío" en <a href="/en-US/docs/Learn/Server-side/Django/authentication_and_sessions#Challenge_yourself">Django Tutorial Part 8: User authentication and permissions</a> tendrá una lista de todos los libros prestados en la biblioteca, que solo es visible para el personal de la biblioteca. Podemos agregar un enlace a nuestra página de renovación al lado de cada artículo usando el código de plantilla a continuación.</p>
+
+<pre class="brush: html notranslate">{% if perms.catalog.can_mark_returned %}- &lt;a href="{% url 'renew-book-librarian' bookinst.id %}"&gt;Renew&lt;/a&gt; {% endif %}</pre>
+
+<div class="note">
+<p><strong>Nota</strong>: Recuerde que su inicio de sesión de prueba deberá tener el permiso "<code>catalog.can_mark_returned</code>" para acceder a la página de renovar el libro (quizás use su cuenta de superusuario).</p>
+</div>
+
+<p>Alternativamente, puede construir manualmente una URL de prueba como esta — <a href="http://127.0.0.1:8000/catalog/book/&lt;bookinstance id>/renew/">http://127.0.0.1:8000/catalog/book/<em>&lt;bookinstance_id&gt;</em>/renew/</a> (se puede obtener un ID de instancia de libro válido navegando a la página de detalles de un libro en su biblioteca y copiando el campo <code>id</code>).</p>
+
+<h3 id="Como_se_ve">Como se ve?</h3>
+
+<p>Si tiene éxito, el formulario predeterminado se verá así:</p>
+
+<p><img alt="" src="https://mdn.mozillademos.org/files/14209/forms_example_renew_default.png" style="border-style: solid; border-width: 1px; display: block; height: 292px; margin: 0px auto; width: 680px;"></p>
+
+<p>El formulario con un valor no válido ingresado se verá así:</p>
+
+<p><img alt="" src="https://mdn.mozillademos.org/files/14211/forms_example_renew_invalid.png" style="border-style: solid; border-width: 1px; display: block; height: 290px; margin: 0px auto; width: 658px;"></p>
+
+<p>La lista de todos los libros con enlaces renovados se verá así:</p>
+
+<p><img alt="" src="https://mdn.mozillademos.org/files/14207/forms_example_renew_allbooks.png" style="border-style: solid; border-width: 1px; display: block; height: 256px; margin: 0px auto; width: 613px;"></p>
+
+<h2 id="ModelForms">ModelForms</h2>
+
+<p>Crear una clase <code>Form</code> utilizando el enfoque descrito anteriormente es muy flexible, lo que le permite crear cualquier tipo de página de formulario que desee y asociarla con cualquier modelo o modelos.</p>
+
+<p>Sin embargo, si solo necesita un formulario para asignar los campos de un solo modelo, entonces su modelo ya definirá la mayor parte de la información que necesita en su formulario: campos, etiquetas, texto de ayuda, etc. En lugar de recrear las definiciones de modelo en su formulario , es más fácil usar una clase auxiliar <a href="https://docs.djangoproject.com/en/1.10/topics/forms/modelforms/">ModelForm</a> para crear el formulario a partir de su modelo. El  <code>ModelForm</code> puede usarse dentro de sus vistas exactamente de la misma manera que un ordinario <code>Form</code>.</p>
+
+<p>Un <code>ModelForm</code> que contiene el mismo campo que nuestro original <code>RenewBookForm</code>se muestra a continuación. Todo lo que necesita hacer para crear el formulario es agregar <code>class Meta</code> with the associated <code>model</code> (<code>BookInstance</code>) y una lista de los campos del  modelo <code>fields</code>para incluir en el formulario (puede incluir todos los campos usando <code>fields = '__all__'</code>, o puedes usar <code>exclude</code> (en vez de <code>fields</code>) para especificar los campos que no se incluirán del modelo).</p>
+
+<pre class="brush: python notranslate">from django.forms import ModelForm
+from .models import BookInstance
+
+class RenewBookModelForm(ModelForm):
+<strong> class Meta:
+ model = BookInstance
+ fields = ['due_back',]</strong>
+</pre>
+
+<div class="note">
+<p><strong>Nota</strong>: Esto podría no parece mucho más simple que simplemente usar un <code>Form</code>(y no es en este caso, porque solo tenemos un campo). Sin embargo, si tiene muchos campos, puede reducir la cantidad de código de manera bastante significativa.</p>
+</div>
+
+<p>El resto de la información proviene de las definiciones de campo del modelo (por ejemplo, etiquetas, widgets, texto de ayuda, mensajes de error). Si estos no son del todo correctos, entonces podemos anularlos en nuestro <code>class Meta</code>, especificando un diccionario que contiene el campo a cambiar y su nuevo valor. Por ejemplo, en este formulario podríamos querer una etiqueta para nuestro campo de "Fecha de renovación" (en lugar del valor predeterminado basado en el nombre del campo: Fecha de vencimiento), y también queremos que nuestro texto de ayuda sea específico para este caso de uso. El <code>Meta</code> a continuación le muestra cómo anular estos campos, y puede configurar de manera similar <code>widgets</code> y <code>error_messages</code> si los valores predeterminados no son suficientes.</p>
+
+<pre class="brush: python notranslate">class Meta:
+ model = BookInstance
+ fields = ['due_back',]
+<strong> labels = { 'due_back': _('Renewal date'), }
+ help_texts = { 'due_back': _('Enter a date between now and 4 weeks (default 3).'), } </strong>
+</pre>
+
+<p>Para agregar validación, puede usar el mismo enfoque que para un normal <code>Form</code> — define una función llamada <code>clean_<em>field_name</em>()</code> y coloca (raise)  <code>ValidationError</code> excepciones para valores no válidos. La única diferencia con respecto a nuestro formulario original es que el campo modelo se llama <code>due_back</code> y no "<code>renewal_date</code>".</p>
+
+<pre class="brush: python notranslate">from django.forms import ModelForm
+from .models import BookInstance
+
+class RenewBookModelForm(ModelForm):
+<strong>    def clean_due_back(self):
+       data = self.cleaned_data['due_back']
+
+  #Check date is not in past.
+       if data &lt; datetime.date.today():
+           raise ValidationError(_('Invalid date - renewal in past'))
+
+       #Check date is in range librarian allowed to change (+4 weeks)
+       if data &gt; datetime.date.today() + datetime.timedelta(weeks=4):
+           raise ValidationError(_('Invalid date - renewal more than 4 weeks ahead'))
+
+       # Remember to always return the cleaned data.
+       return data
+</strong>
+ class Meta:
+ model = BookInstance
+ fields = ['due_back',]
+ labels = { 'due_back': _('Renewal date'), }
+ help_texts = { 'due_back': _('Enter a date between now and 4 weeks (default 3).'), }
+</pre>
+
+<p>La clase <code>RenewBookModelForm</code> a continuación es ahora funcionalmente equivalente a nuestro original <code>RenewBookForm</code>. Puede importarlo y usarlo donde quiera que lo use actualmente <code>RenewBookForm</code>.</p>
+
+<h2 id="Vistas_de_edición_genéricas">Vistas de edición genéricas</h2>
+
+<p>El algoritmo de manejo de formularios que utilizamos en nuestro ejemplo de vista de funciones anterior representa un patrón extremadamente común en las vistas de edición de formularios. Django extrae gran parte de esta "plantilla" para ti, para crear vistas de edición genéricas ( <a href="https://docs.djangoproject.com/en/1.10/ref/class-based-views/generic-editing/">generic editing views</a> ) para crear, editar y eliminar vistas basadas en modelos. No solo manejan el comportamiento de "vista", sino que crean automáticamente la clase de formulario (un <code>ModelForm</code>) para tu modelo.</p>
+
+<div class="note">
+<p><strong>Nota: </strong>Además de las vistas de edición descritas aquí, también hay una clase <a href="https://docs.djangoproject.com/en/1.10/ref/class-based-views/generic-editing/#formview">FormView</a> , que se encuentra en algún lugar entre nuestra vista de función y las otras vistas genéricas en términos de "flexibilidad" frente a "esfuerzo de codificación". Usando <code>FormView</code> tu necesitas crear el <code>Form</code>, pero no tiene que implementar todo el patrón estándar de manejo de formularios. En su lugar, solo tiene que proporcionar una implementación de la función que se llamará una vez que se sepa que el envío es válido.</p>
+</div>
+
+<p>En esta sección, vamos a usar vistas de edición genéricas para crear páginas para agregar funcionalidad para crear, editar y eliminar registros de <code>Author</code> de nuestra libreria — Proporcionar efectivamente una reimplementación básica de partes del sitio de administración (esto podría ser útil si necesita ofrecer la funcionalidad de administrador de una manera más flexible que puede proporcionar el sitio de administrador).</p>
+
+<h3 id="Views">Views</h3>
+
+<p>Abre el archivo de vistas (<strong>locallibrary/catalog/views.py</strong>) y agregue el siguiente bloque de código al final:</p>
+
+<pre class="brush: python notranslate">from django.views.generic.edit import CreateView, UpdateView, DeleteView
+from django.urls import reverse_lazy
+from .models import Author
+
+class AuthorCreate(CreateView):
+ model = Author
+ fields = '__all__'
+ initial={'date_of_death':'05/01/2018',}
+
+class AuthorUpdate(UpdateView):
+ model = Author
+ fields = ['first_name','last_name','date_of_birth','date_of_death']
+
+class AuthorDelete(DeleteView):
+ model = Author
+ success_url = reverse_lazy('authors')</pre>
+
+<p>Como puede ver, para crear las vistas de las que necesita derivar <code>CreateView</code>, <code>UpdateView</code>, y <code>DeleteView</code> (respectivamente) y luego definir el modelo asociado.</p>
+
+<p>Para los casos de "crear" y "actualizar", también debe especificar los campos para mostrar en el formulario (utilizando la misma sintaxis que para <code>ModelForm</code>). En este caso, mostramos la sintaxis para mostrar "todos" los campos y cómo puede enumerarlos individualmente. También puede especificar valores iniciales para cada uno de los campos utilizando un diccionario de pares nombre_campo / valor (aquí establecemos arbitrariamente la fecha de fallecimiento para fines de demostración; ¡es posible que desee eliminar eso!). Por defecto, estas vistas redirigirán en caso de éxito a una página que muestre el elemento del modelo recién creado / editado, que en nuestro caso será la vista detallada del autor que creamos en un tutorial anterior. Puede especificar una ubicación alternativa de redireccionamiento declarando explícitamente el parámetro <code>success_url</code> (como hecho en la clase <code>AuthorDelete</code> ).</p>
+
+<p>La clase <code>AuthorDelete</code> no necesita mostrar ninguno de los campos, por lo que no es necesario especificarlos. Sin embargo, debe especificar el <code>success_url</code>, porque no hay un valor predeterminado obvio para que Django lo use. En este caso usamos la función <code><a href="https://docs.djangoproject.com/en/1.10/ref/urlresolvers/#reverse-lazy">reverse_lazy()</a></code> para redirigir a nuestra lista de autores después de que un autor ha sido eliminado — <code>reverse_lazy()</code> is a lazily executed version of <code>reverse()</code>, se usa aquí porque estamos proporcionando una URL a un atributo de vista basado en clases.</p>
+
+<h3 id="Templates_-_Plantillas">Templates - Plantillas</h3>
+
+<p>Las vistas "create" y "update" utilizan la misma plantilla de forma predeterminada, que se nombrará después de su model: <em>model_name</em><strong>_form.html</strong> (puedes cambiar el sufijo a algo diferente a <strong>_form</strong> usando el campo  <code>template_name_suffix</code>en tu vista, ejemplo: <code>template_name_suffix = '_other_suffix'</code>)</p>
+
+<p>Crea la siguiente plantilla <strong>locallibrary/catalog/templates/catalog/author_form.html </strong> y copia el siguiente texto:</p>
+
+<pre class="brush: html notranslate">{% extends "base_generic.html" %}
+
+{% block content %}
+
+&lt;form action="" method="post"&gt;
+ {% csrf_token %}
+ &lt;table&gt;
+ \{{ form.as_table }}
+ &lt;/table&gt;
+ &lt;input type="submit" value="Submit" /&gt;
+
+&lt;/form&gt;
+{% endblock %}</pre>
+
+<p>Esto es similar a nuestros formularios anteriores y representa los campos usando una tabla. Tenga en cuenta también cómo declaramos nuevamente<code>{% csrf_token %}</code> para garantizar que nuestros formularios sean resistentes a los ataques CSRF.</p>
+
+<p>La vista "delete" espera encontrar una plantilla con el formato <em>model_name</em><strong>_confirm_delete.html</strong> (de nuevo, puedes cambiar el sufijo usando <code>template_name_suffix</code> en tu vista). Crea la siguiente plantilla <strong>locallibrary/catalog/templates/catalog/author_confirm_delete.html</strong> y copia en el texto a continuación.</p>
+
+<pre class="brush: html notranslate">{% extends "base_generic.html" %}
+
+{% block content %}
+
+&lt;h1&gt;Delete Author&lt;/h1&gt;
+
+&lt;p&gt;Are you sure you want to delete the author: \{{ author }}?&lt;/p&gt;
+
+&lt;form action="" method="POST"&gt;
+ {% csrf_token %}
+ &lt;input type="submit" action="" value="Yes, delete." /&gt;
+&lt;/form&gt;
+
+{% endblock %}
+</pre>
+
+<h3 id="Configuración_de_URL">Configuración de URL</h3>
+
+<p>Abra el archivo de configuración de URL (<strong>locallibrary/catalog/urls.py</strong>) y agregue la siguiente configuración al final del archivo:</p>
+
+<pre class="brush: python notranslate">urlpatterns += [
+ url(r'^author/create/$', views.AuthorCreate.as_view(), name='author_create'),
+ url(r'^author/(?P&lt;pk&gt;\d+)/update/$', views.AuthorUpdate.as_view(), name='author_update'),
+ url(r'^author/(?P&lt;pk&gt;\d+)/delete/$', views.AuthorDelete.as_view(), name='author_delete'),
+]</pre>
+
+<p>¡No hay nada particularmente nuevo aquí! Puede ver que las vistas son clases y, por lo tanto, deben llamarse a través de <code>.as_view()</code>,y deberías poder reconocer los patrones de URL en cada caso. Debemos usar <code>pk</code> como el nombre de nuestro valor de clave principal (primary key) capturado, ya que este es el nombre del parámetro esperado por las clases de vista.</p>
+
+<p>Las páginas de crear, actualiza y eliminar autor ahora estan listas para probar (no nos molestaremos en engancharlas en la barra lateral del sitio en este caso, aunque puede hacerlo si lo desea).</p>
+
+<div class="note">
+<p><strong>Nota</strong>:¡Los usuarios observadores habrán notado que no hicimos nada para evitar que usuarios no autorizados accedan a las páginas! Lo dejamos como un ejercicio para usted (pista: puede usar el <code>PermissionRequiredMixin</code> y cree un nuevo permiso o reutilice nuestro permiso <code>can_mark_returned</code> ).</p>
+</div>
+
+<h3 id="Probando_la_página_2">Probando la página</h3>
+
+<p>Primero inicie sesión en el sitio con una cuenta que tenga los permisos que haya decidido que se necesitan para acceder a las páginas de edición del autor.</p>
+
+<p>Luego navegue a la página de creación del autor: <a href="http://127.0.0.1:8000/catalog/author/create/">http://127.0.0.1:8000/catalog/author/create/</a>, que debería verse como la captura de pantalla a continuación.</p>
+
+<p><img alt="Form Example: Create Author" src="https://mdn.mozillademos.org/files/14223/forms_example_create_author.png" style="border-style: solid; border-width: 1px; display: block; height: 184px; margin: 0px auto; width: 645px;"></p>
+
+<p>Ingrese los valores para los campos y luego presione <strong>Submit</strong> para guardar el registro del autor. Ahora debería ser llevado a una vista detallada para su nuevo autor, con una URL de algo como <em>http://127.0.0.1:8000/catalog/author/10</em>.</p>
+
+<p>Puede probar la edición de registros agregando <em>/update/</em> hasta el final de la vista detallada URL (e.g. <em>http://127.0.0.1:8000/catalog/author/10/update/</em>) — no mostramos una captura de pantalla, porque se parece a la página "create".</p>
+
+<p>Por último, podemos eliminar el autor, agregando eliminar (delete) al final de la vista detallada del autor URL (ejemplo. <em>http://127.0.0.1:8000/catalog/author/10/delete/</em>). Django debería mostrar la página de eliminación que se muestra a continuación. pulse <strong>Yes, delete.</strong> para eliminar el registro y ser llevado a la lista de todos los autores.</p>
+
+<p><img alt="" src="https://mdn.mozillademos.org/files/14221/forms_example_delete_author.png" style="border-style: solid; border-width: 1px; display: block; height: 194px; margin: 0px auto; width: 561px;"></p>
+
+<h2 id="Retarte_a_ti_mismo">Retarte a ti mismo</h2>
+
+<p>Crea algunos formularios para crear, editar y eliminar registros de <code>Book</code>.Puede usar exactamente la misma estructura de <code>Authors</code>. Si tu plantilla  <strong>book_form.html</strong> es solo una versión renombrada de la copia de la plantilla   <strong>author_form.html</strong>, entonces la nueva página "crear libro" se verá como la captura de pantalla a continuación:</p>
+
+<p><img alt="" src="https://mdn.mozillademos.org/files/14225/forms_example_create_book.png" style="border-style: solid; border-width: 1px; display: block; height: 521px; margin: 0px auto; width: 595px;"></p>
+
+<ul>
+</ul>
+
+<h2 id="Resumen">Resumen</h2>
+
+<p>¡Crear y manejar formularios puede ser un proceso complicado! Django lo hace mucho más fácil al proporcionar mecanismos programáticos para declarar, representar y validar formularios. Además, Django proporciona vistas genéricas de edición de formularios que pueden hacer casi todo el trabajo para definir páginas que pueden crear, editar y eliminar registros asociados con una sola instancia de modelo.</p>
+
+<p>Hay mucho más que se puede hacer con los formularios (consulte nuestra lista Vea también a continuación), pero ahora debe comprender cómo agregar formularios básicos y código de manejo de formularios a sus propios sitios web.</p>
+
+<h2 id="Ver_también">Ver también</h2>
+
+<ul>
+ <li><a href="https://docs.djangoproject.com/en/1.10/topics/forms/">Working with forms</a> (Django docs)</li>
+ <li><a href="https://docs.djangoproject.com/en/1.10/intro/tutorial04/#write-a-simple-form">Writing your first Django app, part 4 &gt; Writing a simple form</a> (Django docs)</li>
+ <li><a href="https://docs.djangoproject.com/en/1.10/ref/forms/api/">The Forms API</a> (Django docs)</li>
+ <li><a href="https://docs.djangoproject.com/en/1.10/ref/forms/fields/">Form fields</a> (Django docs) </li>
+ <li><a href="https://docs.djangoproject.com/en/1.10/ref/forms/validation/">Form and field validation</a> (Django docs)</li>
+ <li><a href="https://docs.djangoproject.com/en/1.10/topics/class-based-views/generic-editing/">Form handling with class-based views</a> (Django docs)</li>
+ <li><a href="https://docs.djangoproject.com/en/1.10/topics/forms/modelforms/">Creating forms from models</a> (Django docs)</li>
+ <li><a href="https://docs.djangoproject.com/en/1.10/ref/class-based-views/generic-editing/">Generic editing views</a> (Django docs)</li>
+</ul>
+
+<p>{{PreviousMenuNext("Learn/Server-side/Django/authentication", "Learn/Server-side/Django/Testing", "Learn/Server-side/Django")}}</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>
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>
diff --git a/files/es/learn/server-side/django/home_page/index.html b/files/es/learn/server-side/django/home_page/index.html
new file mode 100644
index 0000000000..4c849e8917
--- /dev/null
+++ b/files/es/learn/server-side/django/home_page/index.html
@@ -0,0 +1,403 @@
+---
+title: 'Tutorial de Django Parte 5: Creación de tu página de inicio'
+slug: Learn/Server-side/Django/Home_page
+translation_of: Learn/Server-side/Django/Home_page
+---
+<div>{{LearnSidebar}}</div>
+
+<div>{{PreviousMenuNext("Learn/Server-side/Django/Admin_site", "Learn/Server-side/Django/Generic_views", "Learn/Server-side/Django")}}</div>
+
+<p class="summary">Estamos listos ahora para añadir el código para mostrar nuestra primera página entera — una página de inicio del sitio web de la <a href="https://developer.mozilla.org/en-US/docs/Learn/Server-side/Django/Tutorial_local_library_website">BibliotecaLocal</a> que muestra cuántos registros tenemos de cada tipo de modelo y proporciona una barra lateral con enlaces de navegación a nuestras otras páginas. Por el camino ganaremos experiencia práctica en escritura básica de mapeos de URL y vistas, obtención de resgistros de la base de datos y uso de plantillas.</p>
+
+<table class="learn-box standard-table">
+ <tbody>
+ <tr>
+ <th scope="row">Pre-requisitos:</th>
+ <td>Lee la <a href="https://developer.mozilla.org/es/docs/Learn/Server-side/Django/Introducci%C3%B3n">Introducción a Django</a>. Completa los tópicos previos del tutorial (incluyendo <a href="https://developer.mozilla.org/es/docs/Learn/Server-side/Django/Admin_site">Tutorial de Django Parte 4: Sitio de administración de Django</a>).</td>
+ </tr>
+ <tr>
+ <th scope="row">Objetivo:</th>
+ <td>
+ <p>Entender cómo crear mapas url y vistas simples (sin información codificada en la URL), y cómo obtener información desde los modelos y crear plantillas.</p>
+ </td>
+ </tr>
+ </tbody>
+</table>
+
+<h2 id="Visión_General">Visión General</h2>
+
+<p>Ahora que hemos definindo nuestros modelos y hemos creado los primeros registros en la librería para trabajar, es hora de escribir código para presentar información a los usuarios. Lo primero que necesitamos es determinar que información queremos mostrar en nuestras páginas, y definir una URL apropiada hacia estos recursos. Vamos a necesitar crear el mapeador de URLs, las vistas y plantillas para mostrar estas páginas.</p>
+
+<p>El siguiente diagrama es un recordatorio del principal flujo de datos y cosas necesarias para ser implementadas cuando se maneja una respuesta/petición en HTTP.<br>
+ Los principales elementos que necesitamos crear son:</p>
+
+<ul>
+ <li>Mapeadores URL para reenviar las URLs admitidas (y cualquier información codificada en las URLs)  a las funciones de vista apropiadas.</li>
+ <li>Funciones de vista para obtener los datos solicitados desde los modelos, crear una página HTML que muestre los datos, y devolverlo al usuario para que lo vea en el navegador.</li>
+ <li>Plantillas usadas por las vistas para renderizar los datos.</li>
+</ul>
+
+<p><img alt="" src="https://mdn.mozillademos.org/files/13931/basic-django.png" style="display: block; margin: 0px auto;"></p>
+
+<p>Como verás en la siguiente sección, vamos a tener 5 páginas para mostrar, que es mucho que documentar en un artículo. Por lo tanto, en la mayor parte de este artículo nos concentraremos en mostrar como implementar solo la página de inicio (nos moverermos a otras páginas en un artículo subsecuente). Esto debe darte un buen entendimiento de extremo a extremo sobre como los mapeadores URL, vistas y modelos funcionan en la práctica.</p>
+
+<h2 id="Definiendo_el_recurso_URL">Definiendo el recurso URL</h2>
+
+<p>Como esta versión de <em>LocalLibrary</em>  es escencialmente solo de lectura para usuarios finales, debemos proveer una página de llegada para el sitio (una página de inicio), y páginas que <em>desplieguen</em> listas y vistas detalladas para libros y autores. </p>
+
+<p>Las URL que vamos a necesitar para nuestras páginas son:</p>
+
+<ul>
+ <li><code>catalog/</code> — La página home/index.</li>
+ <li><code>catalog/books/</code> — La lista de todos los libros.</li>
+ <li><code>catalog/authors/</code> — La lista de todos los autores.</li>
+ <li><code>catalog/book/<em>&lt;id&gt;</em></code> — La vista detallada para el libro específico con un campo de clave primaria de <code><em>&lt;id&gt;</em></code> (el valor por defecto). Así por ejemplo, <code>/catalog/book/3</code>, para el tercer libro añadido.</li>
+ <li><code>catalog/author/<em>&lt;id&gt;</em></code><em> </em>— La vista detallada para el autor específico con un campo de clave primaria llamada <em><code>&lt;id&gt;. </code></em>Así por ejemplo, <code>/catalog/author/11</code>, para el 11vo autor añadido.</li>
+</ul>
+
+<p>La tres primeras URLs son usadas para listar el índice, los libros y autores. Esto no codifica ninguna información adicional, y mientras los resultados retornados dependerán del contenido en la base de datos, las consultas que se ejecutan para obtener la información siempre serán las mismas.</p>
+
+<p>En contraste las 2 URLs finales son usadas para mostrar información detallada sobre un libro o autor específico — estas codifican la identidad de los ítemes a mostrar en la URL (mostrado arriba como <code><em>&lt;id&gt;</em></code> ). El mapeador URL puede extraer la información codificada y pasársela a la vista, donde se detarminará que información extraer de la base de datos. Al codificar la información en nuestra URL solo necesitamos un mapeador de URL, una vista, y un plantilla para manejar cada libro (o autor). </p>
+
+<div class="note">
+<p><strong>Nota</strong>: Django te permite construir tus URLs  de cualquier forma que quieras — puedes codificar información en el cuerpo de la URL como se muestra arriba o usando la obtención de  parámetros <code>GET</code>  de la URL(e.j. <code>/book/?id=6</code>). Culquier enfoque que uses, las URLs deben mantenerse limpias, lógicas y legibles (<a href="https://www.w3.org/Provider/Style/URI">observa el consejo del W3C aquí</a>).<br>
+ <br>
+ La documentación Django tiende a recomendar la codificación de información en el cuerpo de la URL, una práctica que ellos creen que promueve mejores diseños de URL.</p>
+</div>
+
+<p>Como discutimos en la introducción, el resto de este articulo describe como construimos la página index.</p>
+
+<h2 id="Creando_la_página_index">Creando la página index</h2>
+
+<p>La primera página que crearemos será la página index (<code>catalog/</code>). Esto desplegará un pequeño HTML estático, junto con algunos "contadores" calculados de diferentes registros en la base de datos. Para hacer este trabajo tendremos que crear un mapeador URL, una vista y una plantilla. </p>
+
+<div class="note">
+<p><strong>Nota</strong>: Vale la pena prestar un poco de atención extra en esta sección. La mayoría del contenido es común para todas las páginas.</p>
+</div>
+
+<h3 id="Mapeador_URL">Mapeador URL</h3>
+
+<p>Hemos creado un archivo básico <strong>/catalog/urls.py</strong> para nuestra aplicación catálogo cuando creamos el <a href="/en-US/docs/Learn/Server-side/Django/skeleton_website">esqueleto del sitio Web</a>. Las URLs de la aplicación catálogo fueron incluidas dentro del proyecto con un mapeador a <code>catalog/</code>, entonces las URLs  que llegan a este mapeador deben empezar con<code> catalog/</code> (el mapeador funciona sobre todos los string en la URL después de la barra diagonal).</p>
+
+<p>Abra <strong>urls.py</strong> y pegue la línea en negrita que aparece a continuación. </p>
+
+<pre class="brush: python">urlpatterns = [
+<strong> url(r'^$', views.index, name='index'),</strong>
+]</pre>
+
+<p>Esta función <code>url()</code> define un patrón URL (<code>r'^$'</code>),  y una función vista que será llamada si el patrón es detectado (<code>views.index</code> — una función llamada <code>index()</code> en <strong>views.py</strong>). El patrón URL es una <a href="https://docs.python.org/3/library/re.html">expresión regular de Python</a> (ER). Hablaremos un poco más sobre ERs más adelante en este tutorial, pero para este caso todo lo que necesitas saber es que en una ER de ^$  el patrón coincidirá con una cadena vacía  (^ es un marcador de inicio de cadena y  $ es un marcador de fin de cadena). </p>
+
+<div class="note">
+<p><strong>Nota: </strong>Nota que en  <strong>/locallibrary/locallibrary/urls.py</strong> </p>
+
+<pre><code>urlpatterns += [
+    url(r'^catalog/', include('catalog.urls')),
+]</code></pre>
+
+<p>La expresión regular en este caso no tienen un <code>$</code> (caracter asignado a fin-de-cadena) pero incluye una barra diagonal. Siempre cuando Django se encuentra con <code>include()</code> (<a href="https://docs.djangoproject.com/en/1.11/ref/urls/#django.conf.urls.include" title="django.conf.urls.include"><code>django.conf.urls.include()</code></a>), corta cualquier parte de la URL que coincida hasta este punto y envía el resto de la cadena para incluir la configuración URL para el siguiente procesamiento.</p>
+
+<p>La URL coincidente es en realidad <code>catalog/</code> + &lt;cadena vacía&gt; ( <code>/catalog/</code> es asumida ya que include() fue el método usado). Nuestra primera función vista  será llamada si recibimos una consulta HTTP con una URL de <code>/catalog/</code>.</p>
+</div>
+
+<p>La función  <code>url()</code> también especifica un parámetro <code>name</code>, que identifica de manera única  <em>este</em> mapeador de URL particular. Puedes usar este nombre para "revertir" el mapeador — para crear dinámicamente una URL que apunta al el recurso que el mapeador esta diseñado para manejar. Por ejemplo, con esto hecho ahora podemos enlazar nuestra página inicio creando el siguiente enlace en nuestra plantilla:</p>
+
+<pre class="brush: html">&lt;a href="<strong>{% url 'index' %}</strong>"&gt;Home&lt;/a&gt;.</pre>
+
+<div class="note">
+<p><strong>Nota</strong>: Por su puesto podemos codificar a fuerza bruta el link anterior (e.j. <code>&lt;a href="<strong>/catalog/</strong>"&gt;Home&lt;/a&gt;</code>), pero entonces si cambiamos el patrón para nuestra página de inicio (e.j. a <code>/catalog/index</code>) la plantilla no podrá seguir enlazando correctamente. Usar un mapeador de url es mucho más flexible y robusto!</p>
+</div>
+
+<h3 id="Vista_basada-en-funciones">Vista (basada-en-funciones)</h3>
+
+<p>Una vista es una función que procesa una consulta HTTP, trae datos desde la base de datos cuando los necesita, genera una página HTML renderizando estos datos unando una plantilla HTML, y luego retorna el HTML en una respuesta HTTP para ser mostrada al usuario. La vista del índice sigue este modelo — extrae información sobre cuantos <code>Book</code>, <code>BookInstance</code>,  <code>BookInstance</code> disponibles y registros <code>Author</code> tenemos en la base de datos, y los pasa a una plantilla para mostrarlos.</p>
+
+<p>Abre <strong>catalog/views.py</strong>, y nota que el archivo ya importa el atajo de la función <a href="https://docs.djangoproject.com/en/1.10/topics/http/shortcuts/#django.shortcuts.render">render()</a> que genera archivos HTML usando una plantilla y datos. </p>
+
+<pre class="brush: python">from django.shortcuts import render
+
+# Create your views here.
+</pre>
+
+<p>Copia el siguiente código al final del archivo. La primera linea importa las clases de los modelos que usaremos para acceder a los datos en todas nuestras vistas.</p>
+
+<pre class="brush: python">from .models import Book, Author, BookInstance, Genre
+
+def index(request):
+ """
+ Función vista para la página inicio del sitio.
+ """
+ # Genera contadores de algunos de los objetos principales
+ num_books=Book.objects.all().count()
+ num_instances=BookInstance.objects.all().count()
+ # Libros disponibles (status = 'a')
+ num_instances_available=BookInstance.objects.filter(status__exact='a').count()
+ num_authors=Author.objects.count() # El 'all()' esta implícito por defecto.
+
+ # Renderiza la plantilla HTML index.html con los datos en la variable contexto
+ return render(
+ request,
+ 'index.html',
+ context={'num_books':num_books,'num_instances':num_instances,'num_instances_available':num_instances_available,'num_authors':num_authors},
+ )</pre>
+
+<p>La primera parte de la función vista extrae contadores de registros usando el atributo <code>objects.all()</code> en las clases del modelo. Tambien obtiene una lista de los objetos <code>BookInstance</code>  que tienen un valor del campo status de  'a' (Disponible). Puedes encontrar un poco más sobre cómo acceder desde modelos en nuestro tutorial previo (<a href="/en-US/docs/Learn/Server-side/Django/Models#Searching_for_records">Django Tutorial Part 3: Usando modelos &gt; Buscando registros</a>).</p>
+
+<p>Al final de la función invocamos a la función <code>render()</code>  para crear y retornar una página  HTML como una respuesta (esta función atajo envuelve una serie, simplicando este caso de uso muy común). Esta recibe como parametros el objeto <code>request original</code> (una <code>ConsultaHttp</code>), una plantilla HTML con marcadores para los datos, y una variable de <code>contexto</code> (un diccionario Python que contiene los datos que serán insertados en esos marcadores). </p>
+
+<p>Hablaremos más sobre la plantilla y la variable de contexto en la siguiente sección; vamos a crear nuestra plantilla para así de hecho mostrarle algo al usuario!</p>
+
+<h3 id="Plantilla">Plantilla</h3>
+
+<p>Una plantilla es un archivo de texto  que determina la estructura o diseño de un archivo (como una página HTML), con marcadores usados para representar el contenido real. Django automaticamente buscará plantillas en un directorio llamado '<strong>templates</strong>' de su aplicación. Así por ejemplo, en la vista índice que acabamos de agregar, la función <code>render()</code> esperará poder encontrar el archivo <strong>/locallibrary/catalog/templates/<em>index.html</em></strong>, y entregará un error si el archivo no puede ser encontrado. Puede ver esto si guarda los cambios anteriores y vuelve a su navegador — accediendo a <code>127.0.0.1:8000</code> ahora le entregará un mensaje de error bastante intuitivo "TemplateDoesNotExist at /catalog/", más otros detalles.</p>
+
+<div class="note">
+<p><strong>Nota</strong>: Django buscará en una serie de lugares por plantillas, basandose en su archivo de configuraciones de proyectos (buscar en su aplicación instalada es una configuración por defecto!). Puede encontrar más sobre como Django encuentra plantillas y qué formatos de plantillas soporta <a href="https://docs.djangoproject.com/en/1.10/topics/templates/">Templates</a> (Django docs).</p>
+</div>
+
+<h4 id="Plantillas_extendidas">Plantillas extendidas</h4>
+
+<p>La plantilla índice va a necesitar marcado HTML estándar para la cabecera y el cuerpo, junto con secciones para navegar (a otras páginas en el sitio que todavía no hemos creado) y para mostrar algún texto introductorio y nuestros datos de libro. La mayoría de este texto (el HTML y la estructura de navegación) será el mismo para cada página en nuestro sitio. En lugar de obligar a los desarrolladores a duplicar este texto en cada página, el lenguaje de plantillas de Django le permite declarar una plantilla base y luego extenderla, reemplazando solo las porciones que son distintos para cada página específica. </p>
+
+<p>Por ejemplo, un plantilla base <strong>base_generic.html</strong> podría verse como el texto de abajo. Como puedes ver, este contiene algo de HTML "común"  y secciones para el título, barra lateral, y contendio marcados usando las etiquetas de plantillas llamadas <code>block</code> y <code>endblock</code>  (mostradas en negrita). Los bloques pueden estar vacíos, o tener contenido que será usado "por defecto" para páginas derivadas.</p>
+
+<div class="note">
+<p><strong>Nota</strong>: Las etiquetas de plantilla son como funciones que puede usar en una plantilla para recorrer listas, realizar operaciones condicionales basadas en el valor de una variable, etc. Además de las etiquetas de plantilla, la sintaxis de plantilla te permite referenciar variables de plantilla (que son pasadas en la plantilla desde la vista)  y usar  <em>filtros de plantilla</em>, que reformatean las variables (por ejemplo, establecer una cadena en minúsculas).</p>
+</div>
+
+<pre class="brush: html">&lt;!DOCTYPE html&gt;
+&lt;html lang="en"&gt;
+&lt;head&gt;
+ <strong>{% block title %}</strong>&lt;title&gt;Local Library&lt;/title&gt;<strong>{% endblock %}</strong>
+&lt;/head&gt;
+
+&lt;body&gt;
+ <strong>{% block sidebar %}</strong>&lt;!-- insert default navigation text for every page --&gt;<strong>{% endblock %}</strong>
+ <strong>{% block content %}</strong>&lt;!-- default content text (typically empty) --&gt;<strong>{% endblock %}</strong>
+&lt;/body&gt;
+&lt;/html&gt;
+</pre>
+
+<p>Cuando queremos definir una plantilla para una vista en particular, primero especificamos la plantila base  (con la etiqueta de plantilla  <code>extends</code> — vea el código siguiente). Si ahí hay alguna seccón que queremos reemplazar en la plantilla declaramos esto, usando secciones <code>block</code>/<code>endblock</code>  idénticas a las usadas en la plantilla base.</p>
+
+<p>Por ejemplo, el fragmento de código que sigue muestra como usar la etiqueta de plantilla <code>extends</code>, y sobrescribe el bloque <code>content</code>. El HTML final producido tendrá todo el HTML y la estructura defininda en la plantilla base (incluyendo el contenido por defecto que ha definido dentro del bloque <code>title</code>), pero con tu nuevo bloque <code>content</code> insertado en lugar del que venía por defecto.</p>
+
+<pre class="brush: html">{% extends "base_generic.html" %}
+
+{% block content %}
+&lt;h1&gt;Local Library Home&lt;/h1&gt;
+&lt;p&gt;Welcome to &lt;em&gt;LocalLibrary&lt;/em&gt;, a very basic Django website developed as a tutorial example on the Mozilla Developer Network.&lt;/p&gt;
+{% endblock %}</pre>
+
+<h4 id="La_plantilla_base_de_LocalLibrary">La plantilla base de LocalLibrary</h4>
+
+<p>La plantilla base que pensamos usar para el siito web <em>LocalLibrary </em>se muestra abajo. Como puedes ver, contiene algo de HTML y bloques definidos para <code>title</code>, <code>sidebar</code> y <code>content</code>. Tenemos un título por defecto (que podríamos querer cambiar) y una barra lateral por defecto con enlaces a listas de todos los libros y autores (que probablemente no querramos cambiar, pero hemos dejado abierta la posibilidad de hacerlo si es necesario, poniéndolo en un bloque).</p>
+
+<div class="note">
+<p><strong>Nota</strong>: También introducimos dos etiquetas de plantilla adicionales: <code>url</code> y <code>load static</code>. Se discute sobre ellas en secciones posteriores.</p>
+</div>
+
+<p>Crea un nuevo archivo -- <strong>/locallibrary/catalog/templates/<em>base_generic.html</em></strong> -- y pon en él el siguiente contenido:</p>
+
+<pre class="brush: html">&lt;!DOCTYPE html&gt;
+&lt;html lang="en"&gt;
+&lt;head&gt;
+
+ {% block title %}&lt;title&gt;Local Library&lt;/title&gt;{% endblock %}
+ &lt;meta charset="utf-8"&gt;
+ &lt;meta name="viewport" content="width=device-width, initial-scale=1"&gt;
+ &lt;link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css"&gt;
+ &lt;script src="https://ajax.googleapis.com/ajax/libs/jquery/1.12.4/jquery.min.js"&gt;&lt;/script&gt;
+ &lt;script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/js/bootstrap.min.js"&gt;&lt;/script&gt;
+
+ &lt;!-- Add additional CSS in static file --&gt;
+ {% load static %}
+ &lt;link rel="stylesheet" href="{% static 'css/styles.css' %}"&gt;
+&lt;/head&gt;
+
+&lt;body&gt;
+
+ &lt;div class="container-fluid"&gt;
+
+ &lt;div class="row"&gt;
+ &lt;div class="col-sm-2"&gt;
+ {% block sidebar %}
+ &lt;ul class="sidebar-nav"&gt;
+ &lt;li&gt;&lt;a href="{% url 'index' %}"&gt;Home&lt;/a&gt;&lt;/li&gt;
+ &lt;li&gt;&lt;a href=""&gt;All books&lt;/a&gt;&lt;/li&gt;
+ &lt;li&gt;&lt;a href=""&gt;All authors&lt;/a&gt;&lt;/li&gt;
+ &lt;/ul&gt;
+ {% endblock %}
+ &lt;/div&gt;
+ &lt;div class="col-sm-10 "&gt;
+ {% block content %}{% endblock %}
+ &lt;/div&gt;
+ &lt;/div&gt;
+
+ &lt;/div&gt;
+&lt;/body&gt;
+&lt;/html&gt;</pre>
+
+<p>La plantilla usa (e incluye) JavaScript y CSS desde <a href="http://getbootstrap.com/">Bootstrap</a> para mejorar el diseño y la presentación de la página HTML. Usar Bootstrap u otro framework web del lado del cliente es una manera rápida de crear una página atractiva que puede escalarse bien en diferentes tamaños de navegador, y también nos permite concentrarnos en la presentación de la página sin tener que entrar en ninguno de los detalles -- ¡queremos enfocarnos nada más en el código del lado del servidor aquí!</p>
+
+<p>La plantilla base también hace referencia a un archivo css local (<strong>styles.css</strong>) que brinda algo más de estilo. Crea <strong>/locallibrary/catalog/static/css/styles.css</strong> y pon en él el siguiente contenido:</p>
+
+<pre class="brush: css">.sidebar-nav {
+ margin-top: 20px;
+ padding: 0;
+ list-style: none;
+}</pre>
+
+<h4 id="La_plantilla_index">La plantilla index</h4>
+
+<p>Crea el archivo HTML <strong>/locallibrary/catalog/templates/<em>index.html</em></strong> y pon en él el contenido que se muestra abajo. Como puedes ver, extendemos nuestra plantilla base en la primera línea, y luego reemplazamos el bloque <code>content</code> por defecto con uno nuevo para esta plantilla.</p>
+
+<pre class="brush: html">{% extends "base_generic.html" %}
+
+{% block content %}
+&lt;h1&gt;Local Library Home&lt;/h1&gt;
+
+ &lt;p&gt;Welcome to &lt;em&gt;LocalLibrary&lt;/em&gt;, a very basic Django website developed as a tutorial example on the Mozilla Developer Network.&lt;/p&gt;
+
+&lt;h2&gt;Dynamic content&lt;/h2&gt;
+
+ &lt;p&gt;The library has the following record counts:&lt;/p&gt;
+ &lt;ul&gt;
+ &lt;li&gt;&lt;strong&gt;Books:&lt;/strong&gt; <strong>\{{ num_books }}</strong>&lt;/li&gt;
+ &lt;li&gt;&lt;strong&gt;Copies:&lt;/strong&gt; <strong>\{{ num_instances }}</strong>&lt;/li&gt;
+ &lt;li&gt;&lt;strong&gt;Copies available:&lt;/strong&gt; <strong>\{{ num_instances_available }}</strong>&lt;/li&gt;
+ &lt;li&gt;&lt;strong&gt;Authors:&lt;/strong&gt; <strong>\{{ num_authors }}</strong>&lt;/li&gt;
+ &lt;/ul&gt;
+
+{% endblock %}</pre>
+
+<p>En la sección <em>Dynamic content</em> hemos declarado marcadores de posición (<em>variables de plantilla</em>) para la información que quisimos incluir desde la vista. Las variables se marcan usando la sintaxis de "doble corchete" o "llaves" (ver lo que está en negrita arriba).</p>
+
+<div class="note">
+<p><strong>Nota:</strong> Puedes reconocer fácilmente si estás trabajando con variables de plantilla o con etiquetas de plantilla (funciones) porque las variables tienen llaves dobles (<code>\{{ num_books }}</code>) mientras que las etiquetas están encerradas entre llaves simples con signos de porcentaje (<code>{% extends "base_generic.html" %}</code>).</p>
+</div>
+
+<p>Lo importante de todo esto es que estas variables se nombran con las claves que enviamos dentro del diccionario <code>context</code> en la función <code>render()</code> de nuestra vista (mira abajo); estas variables serán reemplazadas por sus valores asociados cuando la plantilla sea renderizada.</p>
+
+<pre class="brush: python">return render(
+ request,
+ 'index.html',
+ context={'<strong>num_books</strong>':num_books,'<strong>num_instances</strong>':num_instances,'<strong>num_instances_available</strong>':num_instances_available,'<strong>num_authors</strong>':num_authors},
+)</pre>
+
+<h4 id="Referenciando_archivos_estáticos_en_las_plantillas">Referenciando archivos estáticos en las plantillas</h4>
+
+<p>Es probable que uses recursos estáticos en tu proyecto, incluyendo JavaScript, CSS e imágenes. Debido a que la ubicación de estos archivos podría ser desconocida (o podría cambiar), Django te permite especificar la ubicación de los mismos dentro de tus plantillas de forma relativa al parámetro global <code>STATIC_URL</code> (el sitio web esqueleto por defecto establece el valor de <code>STATIC_URL</code> a '<code>/static/</code>', pero puedes elegir alojar los archivos en una red de distribución de contenidos o en cualquier otro lugar).</p>
+
+<p>Dentro de la plantilla, primero llamas a la etiqueta de plantilla <code>load</code> especificando "static" para añadir esta biblioteca de plantilla (como se muestra abajo). Luego de que static se carga, puedes usar la etiqueta de plantilla <code>static</code> especificando la URL relativa del archivo de interés.</p>
+
+<pre class="brush: html"> &lt;!-- Add additional CSS in static file --&gt;
+{% load static %}
+&lt;link rel="stylesheet" href="{% static 'css/styles.css' %}"&gt;</pre>
+
+<p>Si quisieras podrías añadir una imagen a la página de forma similar. Por ejemplo:</p>
+
+<pre class="brush: html">{% load static %}
+&lt;img src="{% static 'catalog/images/local_library_model_uml.png' %}" alt="My image" style="width:555px;height:540px;"/&gt;
+</pre>
+
+<div class="note">
+<p><strong>Nota:</strong> Los cambios de arriba especifican dónde se localizan los archivos, pero Django no los sirve por defecto. Si bien habilitamos este servicio para el servidor web de desarrollo en el mapeador URL global (<strong>/locallibrary/locallibrary/urls.py</strong>) cuando <a href="https://www.google.com/url?sa=t&amp;rct=j&amp;q=&amp;esrc=s&amp;source=web&amp;cd=1&amp;cad=rja&amp;uact=8&amp;ved=0ahUKEwiq2o-V3PXbAhVM0FMKHcNzAkcQFggnMAA&amp;url=https%3A%2F%2Fdeveloper.mozilla.org%2Fes%2Fdocs%2FLearn%2FServer-side%2FDjango%2Fskeleton_website&amp;usg=AOvVaw2VIIkwGelK5OnECR-4u4sU">creamos el esqueleto del sitio web</a>, aún necesitarás configurar este servicio para producción. Hablaramos de esto más tarde.</p>
+</div>
+
+<p>Para mayor información sobre el trabajo con archivos estáticos revisa <a href="https://docs.djangoproject.com/en/1.10/howto/static-files/">Managing static files</a> (Django docs).</p>
+
+<h4 id="Enlazando_con_URLs">Enlazando con URLs</h4>
+
+<p>En la plantilla base de arriba se introdujo la etiqueta de plantilla <code>url</code>.</p>
+
+<pre class="brush: python">&lt;li&gt;&lt;a href="{% url 'index' %}"&gt;Home&lt;/a&gt;&lt;/li&gt;
+</pre>
+
+<p>Esta etiqueta toma el nombre de una función <code>url()</code> llamada en tu archivo <strong>urls.py,</strong> y valores para cualquier argumento que la vista asociada recibirá desde tal función, y devuelve una URL que puedes usar para enlazar con el recurso.</p>
+
+
+
+<h4 id="Configurando_adonde_buscar_las_plantillas">Configurando adonde buscar las plantillas</h4>
+
+<p>Para que Django encuentre los archivos de plantillas es necesario editar el archivo settings.py agregando el directorio donde creamos nuestras plantillas en el objeto TEMPLATES, como indica la linea en negrita a continuación:</p>
+
+<pre class="brush: python">TEMPLATES = [
+    {
+        'BACKEND': 'django.template.backends.django.DjangoTemplates',
+        'DIRS': [
+<strong>            os.path.join(BASE_DIR, 'templates'),
+</strong>        ],
+        'APP_DIRS': True,
+        'OPTIONS': {
+            'context_processors': [
+                'django.template.context_processors.debug',
+                'django.template.context_processors.request',
+                'django.contrib.auth.context_processors.auth',
+                'django.contrib.messages.context_processors.messages',
+            ],
+        },
+    },
+]</pre>
+
+<h2 id="¿Cómo_se_ve">¿Cómo se ve?</h2>
+
+<p>En este punto deberíamos haber creado todo lo necesario para desplegar la página index. Corre el servidor (<code>python3 manage.py runserver</code>) y dirige tu navegador a <a href="http://127.0.0.1:8000/">http://127.0.0.1:8000/</a>. Si todo se configuró correctamente, tu sitio debería verse similar a la siguiente captura de pantalla.</p>
+
+<p><img alt="Index page for LocalLibrary website" src="https://mdn.mozillademos.org/files/14045/index_page_ok.png" style="border-style: solid; border-width: 1px; display: block; height: 356px; margin: 0px auto; width: 874px;"></p>
+
+<div class="note">
+<p><strong>Nota:</strong> Aún no podrás usar los enlaces <strong>All books</strong> y <strong>All authors</strong> porque las URLs, vistas y plantillas para dichas páginas no se han definido (al momento solo hemos insertado marcadores de posición para esos enlaces en la plantilla <code>base_generic.html</code>).</p>
+</div>
+
+<h2 id="Rétate_a_tí_mismo">Rétate a tí mismo</h2>
+
+<p>Aquí hay un par de tareas para probar tu familiaridad con consultas a modelos, vistas y plantillas.</p>
+
+<ol>
+ <li>Declara un nuevo bloque <em>title</em> en la plantilla <em>index</em> y cambia el título de la página para coincidir con esta página en particular.</li>
+ <li>Modifica la vista para generar un conteo de géneros y otro de libros que contengan una palabra en particular (no sensible a mayúsculas y minúsculas) y luego añade estos campos a la plantilla.</li>
+</ol>
+
+<ul>
+</ul>
+
+<h2 id="Resumen">Resumen</h2>
+
+<p>Hemos creado la página de inicio para nuestro sitio -- una página HTML que despliega algunos conteos de registros de la base de datos y contiene enlaces a otras de nuestras páginas que aún nos faltan por crear. Sobre la marcha hemos adquirido mucha información fundamental sobre mapeadores URL, vistas, consultas a la base de datos usando nuestros modelos, cómo enviar información a una plantilla desde nuestra vista, y cómo crear y extender plantillas.</p>
+
+<p>En nuestro siguiente artículo nos basaremos en nuestro conocimiento para crear las otras cuatro páginas.</p>
+
+<h2 id="Mira_también">Mira también</h2>
+
+<ul>
+ <li><a href="https://docs.djangoproject.com/en/1.10/intro/tutorial03/">Escribiendo tu primera aplicación Django, parte 3: Vistas y Plantillas</a>  (Django docs)</li>
+ <li><a href="https://docs.djangoproject.com/en/1.10/topics/http/urls/">Despachador URL</a> (Django docs)</li>
+ <li><a href="https://docs.djangoproject.com/en/1.10/topics/http/views/">Funciones de vista</a> (DJango docs)</li>
+ <li><a href="https://docs.djangoproject.com/en/1.10/topics/templates/">Plantillas</a> (Django docs)</li>
+ <li><a href="https://docs.djangoproject.com/en/1.10/howto/static-files/">Administrando archivos estáticos</a> (Django docs)</li>
+ <li><a href="https://docs.djangoproject.com/en/1.10/topics/http/shortcuts/#django.shortcuts.render">Funciones atajo de Django</a> (Django docs)</li>
+</ul>
+
+<p>{{PreviousMenuNext("Learn/Server-side/Django/Admin_site", "Learn/Server-side/Django/Generic_views", "Learn/Server-side/Django")}}</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><a href="/en-US/docs/Learn/Server-side/Django/django_assessment_blog"> </a></li>
+</ul>
diff --git a/files/es/learn/server-side/django/index.html b/files/es/learn/server-side/django/index.html
new file mode 100644
index 0000000000..6a33f3e0be
--- /dev/null
+++ b/files/es/learn/server-side/django/index.html
@@ -0,0 +1,66 @@
+---
+title: Framework Web Django (Python)
+slug: Learn/Server-side/Django
+tags:
+ - Aprendizaje
+ - Codificación de scripts
+ - Principiante
+ - Programación lado servidor
+ - Python
+ - django
+ - introducción
+translation_of: Learn/Server-side/Django
+---
+<div>Django es un framework web extremadamente popular y completamente funcional, escrito en Python. El módulo muestra por qué Django es uno de los frameworks de servidores web más populares, cómo configurar un entorno de desarrollo y cómo empezar a usarlo para crear tus propias aplicaciones web.</div>
+
+<h2 id="Requisitos_Previos">Requisitos Previos</h2>
+
+<p>Antes de comenzar este módulo no es necesario tener ningún conocimiento de Django. Tendrás que entender que son la programación web de lado servidor y los frameworks web, idealmente leyendo los temas en nuestro módulo <a href="/en-US/docs/Learn/Server-side/First_steps">Primeros pasos en la programación de lado servidor de sitios web.</a></p>
+
+<p>Se recomienda un conocimiento general de los conceptos de programación y <a href="/en-US/docs/Glossary/Python">Python</a>, pero no es esencial para entender los conceptos básicos</p>
+
+<div class="note">
+<p><strong>Nota</strong>: Python es uno de los lenguajes de programación más fáciles de leer y entender para los principiantes. Dicho esto, si desea comprender mejor este módulo, hay numerosos libros y tutoriales gratuitos disponibles en Internet (los nuevos programadores pueden querer ver la página de <a href="https://wiki.python.org/moin/BeginnersGuide/NonProgrammers">Python for Non Programmers</a> en el wiki de python.org.</p>
+</div>
+
+<h2 id="Guías">Guías</h2>
+
+<dl>
+ <dt><a href="/es/docs/Learn/Server-side/Django/Introducci%C3%B3n">Introducción a Django</a></dt>
+ <dd>En este primer artículo de Django respondemos a la pregunta "¿Qué es Django?" y te dará una visión general de lo que hace a este framework web especial. Vamos a esbozar las principales características incluyendo algunas de las funciones avanzadas que no tendremos tiempo de cubrir en detalle en este módulo. También te mostraremos algunos de los principales bloques de construcción de una aplicación de Django, para darte una idea de lo que puedes hacer antes de que continúes configurándolo y comiences a jugar.</dd>
+ <dt><a href="https://developer.mozilla.org/es/docs/Learn/Server-side/Django/development_environment">Puesta en marcha de un entorno de desarrollo de Django</a></dt>
+ <dd>Ahora que sabes qué es y para qué sirve Django, te mostraremos cómo configurar y probar un entorno de desarrollo Django en Windows, Linux(Ubuntu), y Mac OS X. Cualquiera que sea el sistema operativo común que estés utilizando, este articulo deberia de ofrecerte lo necesario para iniciarte en el desarrollo de aplicaciones en Django.</dd>
+ <dt><a href="https://developer.mozilla.org/es/docs/Learn/Server-side/Django/Tutorial_local_library_website">Tutorial de Django: Sitio web "Biblioteca Local"</a></dt>
+ <dd>El primer articulo de nuestra serie de tutoriales explica lo que se aprenderá y proporciona una descripcion general del sitio web de ejemplo "Biblioteca Local" en el cual trabajaremos y evolucionaremos en articulos posteriores.</dd>
+ <dt><a href="/es/docs/Learn/Server-side/Django/skeleton_website">Tutorial de Django Parte 2: Creación del esqueleto de un sitio web</a></dt>
+ <dd>Este artículo muestra cómo crear el "esqueleto" de un proyecto de sitio web como base, que luego se puede rellenar con configuraciones, urls, modelos, vistas y plantillas específicas del sitio.</dd>
+ <dt><a href="/es/docs/Learn/Server-side/Django/Models">Tutorial de Django Parte 3: Uso de modelos</a></dt>
+ <dd>Este artículo muestra cómo definir modelos para el sitio web de la <em>Biblioteca Local</em> <span style='background-color: transparent; color: #333333; display: inline !important; float: none; font-family: "Open Sans",arial,x-locale-body,sans-serif; font-size: 16px; font-style: normal; font-variant: normal; font-weight: 400; letter-spacing: normal; text-align: left; text-decoration: none; text-indent: 0px; text-transform: none; white-space: normal;'>— los modelos representan las estructuras en las que queremos almacenar los datos de nuestra app, y también permiten a Django almacenar datos en una base por nosotros (y modificarlos más tarde). Se explica qué es un modelo, cómo se declara y alguno de los principales tipos de campo. También se muestra brevemente unas pocas de las múltiples formas con las que puedes acceder al modelo de datos.</span></dd>
+ <dt><a href="/es/docs/Learn/Server-side/Django/Admin_site">Tutorial de Django Parte 4: Sitio de Administración de Django</a></dt>
+ <dd>Ahora que hemos creado modelos para el sitio web de la <em>Biblioteca Local</em>, usaremos el sito de administración <span style='background-color: transparent; border-bottom-style: none; border-bottom-width: 0px; border-left-style: none; border-left-width: 0px; border-right-style: none; border-right-width: 0px; border-top-style: none; border-top-width: 0px; color: #333333; display: inline; float: none; font-family: "Open Sans",arial,x-locale-body,sans-serif; font-size: 16px; font-style: normal; font-variant: normal; font-weight: 400; letter-spacing: normal; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px; text-align: left; text-decoration: none; text-indent: 0px; text-transform: none; white-space: normal;'>de Django </span>para añadir algunos datos "reales" de libros. Primero te mostraremos cómo registrar los modelos con el sitio de administración, a continuación te mostraremos cómo iniciar sesión y crear algunos datos. Al final te mostramos algunas formas en las que puedes mejorar la presentación del sitio de administración.</dd>
+ <dt><a href="/es/docs/Learn/Server-side/Django/Home_page">Tutorial de Django Parte 5: Creación de tu página de inicio</a></dt>
+ <dd>Ahora estamos listos para añadir el código para presentar nuestra primera página completa — una página de inicio para la <em>Biblioteca Local</em> que muestra cuántos registros tenemos de cada tipo de modelo y proporciona una barra lateral con enlaces de navegación a otras páginas. A medida que avanzamos ganaremos experiencia práctica en la escritura de mapas URL y vistas, obtención de registros de la base de datos y la utilización de plantillas.</dd>
+ <dt><a href="/es/docs/Learn/Server-side/Django/Generic_views">Tutorial de Django Parte 6: Listas genéricas y vistas de detalle</a></dt>
+ <dd>Este tutorial extiende el sitio web de nuestra <em>Biblioteca Local</em> añadiendo listas y páginas de detalle de libros y autores. Aquí aprenderemos sobre vistas genéricas basadas en clases y se mostrará cómo pueden reducir la cantidad de código que tienes que escribir para casos de uso común. También nos adentraremos en la gestión de URL en gran detalle, mostrando cómo realizar un emparejamiento básico de patrones.</dd>
+ <dt><a href="/es/docs/Learn/Server-side/Django/Sessions">Tutorial de Django Parte 7: Framework de sesiones</a></dt>
+ <dd>Este tutorial extiende nuestro sitio web de <em>Biblioteca Local</em> añadiendo a nuestra página de inicio un contador de visitas basado en sesión. Este es un ejemplo relativamente simple, pero muestra de verdad cómo puedes usar el framework de sesión para proporcionar un comportamiento presistente para usuarios anónimos en tus propios sitios.</dd>
+ <dt><a href="/es/docs/Learn/Server-side/Django/Authentication">Tutorial de Django Parte 8: Autenticación y permisos de Usuario</a></dt>
+ <dd>En este tutorial te mostraremos cómo permitir a los usuarios iniciar sesión en tu sitio con sus propias cuentas y cómo controlar lo que pueden hacer y ver, basado en si han iniciado sesión o no y en sus <em>permisos</em>. Como parte de esta demostración extenderemos el sitio web de la <em>Biblioteca Local</em>, añadiendo páginas de inicio y cierre de sesión, y páginas específicas de usuario - y de personal - para ver libros que han sido prestados.</dd>
+ <dt><a href="/es/docs/Learn/Server-side/Django/Forms">Tutorial de Django Parte 9: Trabajo con formularios</a></dt>
+ <dd>En este tutorial te mostraremos cómo trabajar con <a href="/en-US/docs/Web/Guide/HTML/Forms">Formularios HTML</a> en Django, y en particular la forma más fácil de escribir formularios para crear, actualizar y borrar instancias de modelos. Como parte de esta demostración extenderemos el sitio web de la <em>Biblioteca Local</em> de manera que los bibliotecarios puedan renovar libros, y crear, actualizar y borrar autores usando sus propios formularios (en vez de usar la aplicación de administración).</dd>
+ <dt><a href="/es/docs/Learn/Server-side/Django/Testing">Tutorial de Django Parte 10: Probando una aplicación web Django</a></dt>
+ <dd>A medida que crecen los sitios web se vuelven más difíciles de probar a mano — no sólo hay más para probar, sino que además, a medida que las interacciones entre los componentes se vuelven más complejas, un pequeño cambio en un área puede suponer muchas pruebas adicionales para verificar su impacto en otras áreas. Una forma de mitigar estos problemas es escribir tests automatizados, que pueden ser ejecutados de manera fácil y fiable cada vez que hagas un cambio. Este tutorial muestra cómo automatizar la <em>unidad de pruebas</em> de tu sitio web usando el framework de pruebas de Django.</dd>
+ <dt><a href="/es/docs/Learn/Server-side/Django/Deployment">Tutorial de Django Parte 11: Desplegando Django a producción</a></dt>
+ <dd>Ahora que has creado (y probado) un impresionante sitio web para la <em>Biblioteca Local</em>, vas a querer instalarlo en un servidor web público de manera que pueda ser accedido por el personal y los miembros de la biblioteca a través de Internet. Este artículo proporciona una visión general de cómo podrías ir buscando un host para desplegar tu sitio web y lo que necesitas hacer para conseguir que tu sitio esté listo en producción.</dd>
+ <dt><a href="/es/docs/Learn/Server-side/Django/web_application_security">Seguridad de las aplicaciones web Django</a></dt>
+ <dd>Proteger los datos de los usuarios es una parte esencial de cualquier diseño de un sitio web. Previamente ya explicamos algunas de las amenazas de seguridad más comunes en el artículo <a href="https://developer.mozilla.org/en-US/docs/Web/Security">Seguridad Web</a> — este artículo proporciona una demostración práctica de cómo las protecciones integradas de Django gestionan estas amenazas.</dd>
+</dl>
+
+<h2 id="Evaluaciones">Evaluaciones</h2>
+
+<p>La siguiente evaluación comprobará tu comprensión de cómo crear un sitio web usando Django, como se describe en las guías listadas arriba.</p>
+
+<dl>
+ <dt><a href="/es/docs/Learn/Server-side/Django/django_assessment_blog">Mini Blog Django Hazlo tu mismo</a></dt>
+ <dd>En esta evaluación usarás algo del conocimiento que has aprendido en este módulo para crear tu propio blog.</dd>
+</dl>
diff --git a/files/es/learn/server-side/django/introducción/index.html b/files/es/learn/server-side/django/introducción/index.html
new file mode 100644
index 0000000000..484b311a2c
--- /dev/null
+++ b/files/es/learn/server-side/django/introducción/index.html
@@ -0,0 +1,282 @@
+---
+title: Introducción a Django
+slug: Learn/Server-side/Django/Introducción
+tags:
+ - Aprendizaje
+ - CodigoScript
+ - Principiante
+ - Programación lado servidor
+ - Python
+ - django
+ - introducción
+translation_of: Learn/Server-side/Django/Introduction
+---
+<div>{{LearnSidebar}}</div>
+
+<div>{{NextMenu("Learn/Server-side/Django/development_environment", "Learn/Server-side/Django")}}</div>
+
+<p class="summary">En este primer artículo de Django responderemos la pregunta ¿Qué es Django? y daremos una visión general de lo que hace que este framework sea tan especial. Vamos a delinear las características principales, incluidas algunas de las funcionalidades avanzadas que no tendremos tiempo de cubrir con detalle en este módulo.  Tambien mostraremos algunos de los componentes principales de una aplicación de Django. (aunque en este momento no cuentes con un entorno de desarrollo en el cual probarlo).</p>
+
+<table class="learn-box standard-table">
+ <tbody>
+ <tr>
+ <th scope="row">Pre requisitos:</th>
+ <td>Conocimientos basicos en informatica. Una comprensión general de <a href="https://developer.mozilla.org/en-US/docs/Learn/Server-side/First_steps">programación del lado del servidor</a>, y en particular de los mecanimos de <a href="/en-US/docs/Learn/Server-side/First_steps/Client-Server_overview">interacciones cliente-servidor en los sitios web</a>.</td>
+ </tr>
+ <tr>
+ <th scope="row">Objetivos:</th>
+ <td>Familiarizarse con lo que es Django, que funcionalidad proporciona y los componentes principales de una aplicación Django.</td>
+ </tr>
+ </tbody>
+</table>
+
+<h2 id="¿Qué_es_Django">¿Qué es Django?</h2>
+
+<p>Django es un framework web de alto nivel que permite el desarrollo rápido de sitios web seguros y mantenibles. Desarrollado por programadores experimentados, Django se encarga de gran parte de las complicaciones del desarrollo web, por lo que puedes concentrarte en escribir tu aplicación sin necesidad de reinventar la rueda. Es gratuito y de código abierto, tiene una comunidad próspera y activa, una gran documentación y muchas opciones de soporte gratuito y de pago.</p>
+
+<p>Django te ayuda a escribir software que es:</p>
+
+<dl>
+ <dt>Completo</dt>
+ <dd>Django sigue la filosofía "Baterías incluidas" y provee casi todo lo que los desarrolladores quisieran que tenga "de fábrica". Porque todo lo que necesitas es parte de un único "producto", todo funciona a la perfección, sigue principios de diseño consistentes y tiene una amplia y<a href="https://docs.djangoproject.com/en/1.10/"> actualizada documentación</a>.</dd>
+ <dt>Versátil</dt>
+ <dd>Django puede ser (y ha sido) usado para construir casi cualquier tipo de sitio web — desde sistemas manejadores de contenidos y wikis, hasta redes sociales y sitios de noticias. Puede funcionar con cualquier framework en el lado del cliente, y puede devolver contenido en casi cualquier formato (incluyendo HTML, RSS feeds, JSON, XML, etc). ¡El sitio que estás leyendo actualmente está basado en Django!<br>
+ <br>
+ Internamente, mientras ofrece opciones para casi cualquier funcionalidad que desees (distintos motores de base de datos , motores de plantillas, etc.), también puede ser extendido para usar otros componentes si es necesario.</dd>
+ <dt>Seguro</dt>
+ <dd>Django ayuda a los desarrolladores evitar varios errores comunes de seguridad al proveer un framework que ha sido diseñado para "hacer lo correcto" para proteger el sitio web automáticamente. Por ejemplo, Django, proporciona una manera segura de administrar cuentas de usuario y contraseñas, evitando así errores comunes como colocar informaciones de sesión en cookies donde es vulnerable (en lugar de eso las cookies solo contienen una clave y los datos se almacenan en la base de datos) o se almacenan directamente las contraseñas en un hash de contraseñas.<br>
+  <br>
+ <em>Un hash de contraseña es un valor de longitud fija creado al enviar la contraseña a una <a href="https://en.wikipedia.org/wiki/Cryptographic_hash_function">cryptographic hash function</a>. Django puede validar si la contraseña ingresada es correcta enviándola a través de una función hash y comparando la salida con el valor hash almacenado. Sin embargo debido a la naturaleza "unidireccional" de la función, incluso si un valor hash almacenado se ve comprometido es difícil para un atacante resolver la contraseña original.</em><br>
+ <br>
+ Django permite protección contra algunas vulnerabilidades de forma predeterminada, incluida la inyección SQL, scripts entre sitios, falsificación de solicitudes entre sitios y clickjacking (consulte <a href="https://developer.mozilla.org/en-US/docs/Learn/Server-side/First_steps/Website_security">Seguridad de sitios web</a> para obtener más detalles sobre dichos ataques).</dd>
+ <dt>Escalable</dt>
+ <dd>Django usa un componente basado en la arquitectura “<a href="https://en.wikipedia.org/wiki/Shared_nothing_architecture">shared-nothing</a>” (cada parte de la arquitectura es independiente de las otras, y por lo tanto puede ser reemplazado o cambiado si es necesario). Teniendo en cuenta una clara separación entre las diferentes partes significa que puede escalar para aumentar el tráfico al agregar hardware en cualquier nivel: servidores de cache, servidores de bases de datos o servidores de aplicación. Algunos de los sitios más concurridos han escalado a Django para satisfacer sus demandas (por ejemplo, Instagram y Disqus, por nombrar solo dos).</dd>
+ <dt>Mantenible</dt>
+ <dd>El código de Django está escrito usando principios y patrones de diseño para fomentar la creación de código mantenible y reutilizable. En particular, utiliza el principio No te repitas "Don't Repeat Yourself" (DRY) para que no exista una duplicación innecesaria, reduciendo la cantidad de código. Django también promueve la agrupación de la funcionalidad relacionada en "aplicaciones" reutilizables y en un nivel más bajo, agrupa código relacionado en módulos (siguiendo el patrón <a href="/en-US/Apps/Fundamentals/Modern_web_app_architecture/MVC_architecture">Model View Controller (MVC)</a>).</dd>
+ <dt>Portable</dt>
+ <dd>Django está escrito en Python, el cual se ejecuta en muchas plataformas. Lo que significa que no está sujeto a ninguna plataforma en particular, y puede ejecutar sus aplicaciones en muchas distribuciones de Linux, Windows y Mac OS X. Además, Django cuenta con el respaldo de muchos proveedores de alojamiento web, y que a menudo proporcionan una infraestructura específica y documentación para el alojamiento de sitios de Django.</dd>
+</dl>
+
+<h2 id="¿De_dónde_vino">¿De dónde vino?</h2>
+
+<p>Django fue desarrollado inicialmente entre 2003 y 2005 por un equipo que era responsable de crear y mantener sitios web de periódicos. Después de crear varios sitios, el equipo empezó a tener en cuenta y reutilizar muchos códigos y patrones de diseño comunes. Este código común se convirtió en un framework web genérico, que fue de código abierto, conocido como proyecto "Django" en julio de 2005.</p>
+
+<p>Django ha continuado creciendo y mejorando desde su primer hito, el lanzamiento de la versión (1.0) en septiembre de 2008, hasta el reciente lanzamiento de la versión 1.11 (2017). Cada lanzamiento ha añadido nuevas funcionalidades y solucionado errores, que van desde soporte de nuevos tipos de bases de datos, motores de plantillas, caching, hasta la adición de funciones genéricas y clases de visualización (que reducen la cantidad de código que los desarrolladores tiene que escribir para numerosas tareas de programación).</p>
+
+<div class="note">
+<p><strong>Nota</strong>: Consulte las <span style="line-height: 1.5;"><a href="https://docs.djangoproject.com/en/1.10/releases/">notas de lanzamiento</a></span><span style="line-height: 1.5;"> en el sitio web de Django para ver qué ha cambiado en las versiones recientes y cúanto trabajo se lleva a cabo para mejorar Django.</span></p>
+</div>
+
+<p>Django es ahora un próspero proyecto colaborativo de código abierto, con miles de usuarios y contribuidores. Mientras que todavía presenta algunas características que reflejan su origen, Django ha evolucionado en un framework versátil que es capaz de desarrollar cualquier tipo de sitio web.</p>
+
+<h2 id="¿Qué_tan_Popular_es_Django">¿Qué tan Popular es Django?</h2>
+
+<p>No hay una medida de popularidad definitiva y disponible de inmediato de "frameworks de lado servidor" (aunque sitios como <a href="http://hotframeworks.com/">Hot Frameworks</a> intentan evaluar la popularidad usando mecanismos como contar el número de proyectos en Github y preguntas en StackOverflow de cada plataforma). Una pregunta mejor es si Django es lo "suficientemente popular" para evitar los problemas de las plataformas menos populares. ¿Continúa evolucionando? ¿Puedes conseguir la ayuda que necesitas? ¿Hay alguna posibilidad de que consigas un trabajo pagado si aprendes Django?</p>
+
+<p>De acuerdo con el número de sitios que usan Django, el número de gente que contribuye al código base, y el número de gente que proporciona soporte tanto libre como pagado, podemos entonces decir que sí, !Django es un framework popular!</p>
+
+<p>Los sitios de alto nivel que usan Django incluyen: Disqus, Instagram, Knight Foundation, MacArthur Foundation, Mozilla, National Geographic, Open Knowledge Foundation, Pinterest y Open Stack (fuente: <a href="https://www.djangoproject.com/">Página de inicio de Django</a>).</p>
+
+<h2 id="¿Es_Django_dogmático">¿Es Django dogmático?</h2>
+
+<p>Los frameworks web frecuentemente se refieren a sí mismos como "dogmáticos" ("<em>opinionated</em>") o "no dogmáticos" ("<em>unopinionated</em>").</p>
+
+<p>Los frameworks dogmáticos son aquellos que opinan acerca de la "manera correcta" de gestionar cualquier tarea en particular. Ofrecen soporte para el desarrollo rápido en un <em>dominio en particular</em> (resolver problemas de un tipo en particular) porque la manera correcta de hacer cualquier cosa está generalmente bien comprendida y bien documentada.</p>
+
+<p>Sin embargo pueden ser menos flexibles para resolver problemas fuera de su dominio principal, y tienden a ofrecer menos opciones para elegir qué componentes y enfoques pueden usarse.</p>
+
+<p>Los framewoks no dogmáticos, por contra, tienen muchas menos restricciones sobre el modo mejor de unir componentes para alcanzar un objetivo, o incluso qué componentes deberían usarse. Hacen más fácil para los desarrolladores usar las herramientas más adecuadas para completar una tarea en particular, si bien al coste de que necesitas encontrar esos componentes por tí mismo.</p>
+
+<p>Django es "dogmático pero no del todo" y por tanto entrega "lo mejor de ambos mundos". Proporciona un conjunto de componentes para gestionar la mayoría de las tareas de desarrollo web y una (o dos) maneras preferidas de usarlos. Sin embargo, la arquitectura desacoplada de Django implica que puedes normalmente escoger y seleccionar de entre numerosas opciones diferentes o añadir soporte para otras completamente nuevas, si lo deseas.</p>
+
+<h2 id="¿Qué_pinta_tiene_el_código_de_Django">¿Qué pinta tiene el código de Django?</h2>
+
+<p>En un sitio web tradicional basado en datos, una aplicación web espera peticiones HTTP del explorador web (o de otro cliente). Cuando se recibe una petición la aplicación elabora lo que se necesita basándose en la URL y posiblemente en la información incluida en los datos <code>POST</code> o <code>GET</code>. Dependiendo de qué se necesita quizás pueda entonces leer o escribir información desde una base de datos o realizar otras tareas requeridas para satisfacer la petición. La aplicación devolverá a continuación una respuesta al explorador web, con frecuencia creando dinámicamente una página HTML para que el explorador la presente insertando los datos recuperados en marcadores de posición dentro de una plantilla HTML.</p>
+
+<p>Las aplicaciones web de Django normalmente agrupan el código que gestiona cada uno de estos pasos en ficheros separados:</p>
+
+<p><img alt="" src="https://mdn.mozillademos.org/files/13931/basic-django.png" style="border-style: solid; border-width: 1px; display: block; margin: 0px auto;"></p>
+
+<ul>
+ <li><strong>URLs: </strong>Aunque es posible procesar peticiones de cada URL individual vía una función individual, es mucho más sostenible escribir una función de visualización separada para cada recurso. Se usa un mapeador URL para redirigir las peticiones HTTP a la vista apropiada basándose en la URL de la petición. El mapeador URL se usa para redirigir las peticiones HTTP a la vista apropiada basándose en la URL de la petición. El mapeador URL puede también emparejar patrones de cadenas o dígitos específicos que aparecen en una URL y los pasan a la función de visualización como datos.</li>
+ <li><strong>Vista (View):</strong> Una vista es una función de gestión de peticiones que recibe peticiones HTTP y devuelve respuestas HTTP. Las vistas acceden a los datos que necesitan para satisfacer las peticiones por medio de <em>modelos</em>, y delegan el formateo de la respuesta a las <em>plantillas</em> (<em>"templates"</em>).</li>
+ <li><strong>Modelos (Models):</strong> Los Modelos son objetos de Python que definen la estructura de los datos de una aplicación y proporcionan mecanismos para gestionar (añadir, modificar y borrar) y consultar registros en la base de datos.</li>
+ <li><strong>Plantillas (Templates):</strong> una plantilla (template) es un fichero de texto que define la estructura o diagrama de otro fichero (tal como una página HTML), con marcadores de posición que se utilizan para representar el contenido real. Una <em>vista</em> puede crear dinámicamente una página usando una plantilla, rellenandola con datos de un <em>modelo</em>. Una plantilla se puede usar para definir la estructura de cualquier tipo de fichero; ¡no tiene porqué ser HTML!</li>
+</ul>
+
+<div class="note">
+<p><strong>Nota</strong>: Django se refiere a este tipo de organización como arquitectura Modelo Vista Plantilla "Model View Template (MVT)". Tiene muchas similaridades con la arquitectura más familiar <a href="/en-US/docs/Web/Apps/Fundamentals/Modern_web_app_architecture/MVC_architecture">Model View Controller</a>. </p>
+</div>
+
+<ul>
+</ul>
+
+<p>Las secciones de más abajo te darán una idea de la pinta que tienen estas partes principales de una aplicación Django (entraremos en más detalles más adelante en el curso, una vez que hallamos configurado un entorno de desarrollo).</p>
+
+<h3 id="Enviar_la_petición_a_la_vista_correcta_urls.py">Enviar la petición a la vista correcta (urls.py)</h3>
+
+<p>Un mapeador URL está normalmente almacenado en un fichero llamado <strong>urls.py</strong>. En el ejemplo más abajo el mapeador (<code>urlpatterns</code>) define una lista de mapeos entre <em>patrones</em> URL específicos y sus correspondientes funciones de visualización. Si se recibe una Petición HTTP que tiene una URL que empareja un patrón específico (ej. <code>r'^$'</code>, más abajo) se realizará una llamada y se pasará la petición a la función de visualización asociada (ej.  <code>views.index</code>).</p>
+
+<pre>urlpatterns = [
+ <strong>url(r'^$', views.index),</strong>
+ url(r'^([0-9]+)/$', views.best),
+]
+</pre>
+
+<div class="note">
+<p><strong>Nota</strong>: Un poco de Python:</p>
+
+<ul>
+ <li>El objeto <code>urlpatterns</code> es una lista de funciones <code>url()</code>. En Python, las listas se definen usando using corchetes. Los elementos se separan con comas y pueden tener una <a href="https://docs.python.org/2/faq/design.html#why-does-python-allow-commas-at-the-end-of-lists-and-tuples">coma colgante opcional</a>. Por ejemplo: <code>[item1, item2, item3,]</code>.</li>
+ <li>La extraña sintaxis de los patrones se conoce como <em>"expresión regular"</em>. ¡Hablaremos sobre ellas en un artículo posterior!.</li>
+ <li>El segundo argumento de <code>url()</code> es otra función a la que se llamará cuando se encuentre un patrón que coincida. La notación <code>views.index</code> indica que la función se llama <code>index()</code> y se puede encontrar en un módulo llamado <code>views</code> (es decir, dentro del fichero llamado <code>views.py</code>).</li>
+</ul>
+</div>
+
+<h3 id="Manejar_la_petición_views.py">Manejar la petición (views.py)</h3>
+
+<p>Las vistas son el corazón de la aplicación web, recibiendo peticiones HTTP de los clientes web y devolviendo respuestas HTTP. Entre éstas, organizan los otros recursos del framework para acceder a las bases de datos, consolidar plantillas, etc.</p>
+
+<p>El ejemplo más abajo muestra una mínima función de visualización <code>index()</code>, que podría ser llamada por nuestro mapeador de URL de la sección anterior. Al igual que todas las funciones de visualización, recibe un objeto <code>HttpRequest</code> como parámetro (<code>request</code>) y devuelve un objeto <code>HttpResponse</code>. En este caso no hacemos nada con la petición y nuestra respuesta simplemente devuelve una cadena insertada de forma fija en el código. Te mostraremos una petición que hace algo más interesante en la siguiente sección.</p>
+
+<pre class="brush: python">## fichero: views.py (funciones de visualizacion de Django)
+from django.http import HttpResponse
+
+def index(request):
+ # Obtener un HttpRequest - el parametro peticion
+ # Realizar operaciones usando la infomracion de la peticion.
+  # Devolver una HttpResponse
+ return HttpResponse('!Hola desde Django!')
+</pre>
+
+<div class="note">
+<p><strong>Nota</strong>: Un poco de Python:</p>
+
+<ul>
+ <li><a href="https://docs.python.org/3/tutorial/modules.html">Módulos Python</a> son "bibliotecas" de funciones, almacenadas en ficheros separados, que podríamos querer usar en nuestro código. Aquí importamos sólo el objeto <code>HttpResponse</code> desde el módulo  <code>django.http</code> de manera que podamos usarlo en nuestra vista: <code>from django.http import HttpResponse</code>. Hay también otras formas de importar algunos o todos los objetos de un módulo.</li>
+ <li>Las funciones se declaran usando la plabra clave <code>def</code> tal como se muestra arriba, con parámetros con nombre listados entre paréntesis después del nombre de la función; la línea entera termina con dos puntos. Fíjate como las líneas siguientes están todas ellas <strong>indentadas</strong>. La indentación es importante, ya que especifica que las líneas de código están dentro de ese bloque en particular (la indentación obligatoria es una característica clave de Python, y es una razón por la que el código de Python es tan fácil de leer.</li>
+</ul>
+</div>
+
+<ul>
+</ul>
+
+<p>Las vistas se almacenan normalmente en un fichero llamado <strong>views.py</strong>.</p>
+
+<h3 id="Definir_modelos_de_datos_models.py">Definir modelos de datos (models.py)</h3>
+
+<p>Las aplicaciones web Django manejan y consultan datos a través de objetos Python referidos como modelos. Los modelos definen la estructura de los datos almacenados, incluyendo los <em>tipos</em> de campos y posiblemente también su tamaño máximo, los valores por defecto, la lista de selección de opciones, texto de ayuda para documentación, etiquetas de texto para formularios, etc. La definición del modelo es independiente de la base de datos subyacente — puedes elegir una entre varias como parte de la configuración de tu proyecto. Una vez que has seleccionado qué base de datos quieres usar, no necesitas en absoluto comunicarte con ella directamente — sólo hay que escribir la estructura de la base y otro código y Django se encarga por tí de todo el trabajo sucio de comunicarse con la base de datos.</p>
+
+<p>El fragmento de código de más abajo muestra un modelo de Django muy simple para un objeto <code>Team</code>. La clase <code>Team</code> deriva de la clase de django <code>models.Model</code>. Define el nombre de un equipo y su nivel como campos de tipo carácter y especifica un número máximo de caracteres que pueden ser almacenados en cada registro. El <code>team_level</code> puede ser un valor de entre varios, de manera que lo definimos como un campo de opciones y proporcionamos un mapeo entre opciones para mostrar y datos para almacenar, junto con un valor por defecto.</p>
+
+<pre class="brush: python"># filename: models.py
+
+from django.db import models
+
+class Team(models.Model):
+  team_name = models.CharField(max_length=40)
+
+    TEAM_LEVELS = (
+        ('U09', 'Under 09s'),
+        ('U10', 'Under 10s'),
+        ('U11', 'Under 11s'),
+  ... #list other team levels
+    )
+    team_level = models.CharField(max_length=3,choices=TEAM_LEVELS,default='U11')
+</pre>
+
+<div class="note">
+<p><strong>Nota</strong>: Un poco de Python:</p>
+
+<ul>
+ <li>Python soporta "programación orientada a objetos", un estilo de programación donde organizamos nuestro código en objetos, que incluyen datos relacionados y funciones para operar con los datos. Los objetos también pueden heredarse/extenderse/derivarse de otros objetos, permitiendo que se comparta un comportamiento común entre objetos relacionados. En Python usamos la palabra clave <code>class</code> para definir el "prototipo" de un objeto. Podemos crear múltiples <em>instancias</em> específicas de ese tipo de objeto basado en el modelo especificado en la clase.<br>
+ Así por ejemplo, aquí tenemos una clase <code>Team</code>, que deriva de la clase <code>Model</code>. Esto significa que es un modelo y que contendrá los métodos de un modelo, pero también podemos darle características especializadas propias. En nuestro modelo definimos los campos de nuestra base que necesitaremos para almacenar nuestros datos, dándoles nombres específicos. Django usa estas definiciones, incluídos los nombres de los campos para crear la base subyacente.</li>
+</ul>
+</div>
+
+<h3 id="Consultar_datos_views.py">Consultar datos (views.py)</h3>
+
+<p>El modelo de Django proporciona una API de consulta simple para buscar en la base de datos. Esta puede buscar concidencias contra varios campos al mismo tiempo usando diferentes criterios (ej. exacto, insensible a las mayúsculas, mayor que, etc.), y puede soportar sentencias complejas (por ejemplo, puedes especificar que se busque equipos U11 que tengan un nombre de equipo que empiece por "Fr" o termine con "al").</p>
+
+<p>El fragmento de código de más abajo muestra una función de visualización (manejador de recursos) para presentar en pantalla todos nuestros equipos U09. La línea en negrilla muestra como podemos usar la API de consulta del modelo para filtrar todos los registros donde el campo <code>team_level</code> tenga exactamente el texto 'U09' (fíjate como este criterio se pasa como argumento a la función<code> filter()</code> con el nombre del campo y tipo de coincidencia separados por un doble guion bajo: <strong>team_level__exact</strong>).</p>
+
+<pre class="brush: python">## filename: views.py
+
+from django.shortcuts import render
+from .models import Team
+
+def index(request):
+    <strong>list_teams = Team.objects.filter(team_level__exact="U09")</strong>
+    context = {'youngest_teams': list_teams}
+    return render(request, '/best/index.html', context)
+</pre>
+
+<dl>
+</dl>
+
+<p>Esta función utiliza la función <code>render()</code> para crear la <code>HttpResponse</code> que se envía de vuelta al explorador. Esta función es un <em>atajo</em>; crea un fichero HTML mediante la combinación de una plantilla HTML específica y algunos datos para insertar en ella (proporcionados en la variable "<code>context</code>"). En la siguiente sección mostramos como la plantilla tiene los datos insertados en ella para crear el HTML.</p>
+
+<h3 id="Renderización_de_los_datos_plantillas_HTML">Renderización de los datos (plantillas HTML)</h3>
+
+<p>Los sistemas de plantillas permiten especificar la estructura de un documento de salida usando marcadores de posición para los datos que serán rellenados cuando se genere la página. Las plantillas se usan con frecuencia para crear HTML, también pueden crear otros tipos de documentos. Django soporta de fábrica tanto su sistema de plantillas nativo como otra biblioteca Python popular llamada <strong>Jinja2</strong> (y se puede hacer que soporte otros sistemas si hace falta).</p>
+
+<p>El fragmento de código de más abajo muestra el aspecto que podría tener la plantilla HTML llamada por la función <code>render()</code> de la sección anterior. Esta plantilla ha sido escrita bajo la suposición de que cuando sea renderizada tendrá acceso a una variable tipo lista llamada <code>youngest_teams</code> (contenida en la variable <code>context</code> dentro de la función <code>render()</code> de más arriba). Dentro del esqueleto HTML tenemos una expresión que primero comprueba que existe la variable <code>youngest_teams</code>, y luego itera sobre ella en un bucle <code>for</code>. En cada iteración la plantilla presenta cada valor del campo <code>team_name</code> del equipo en un elemento {{htmlelement("li")}}.</p>
+
+<pre class="brush: python">## filename: best/templates/best/index.html
+
+&lt;!DOCTYPE html&gt;
+&lt;html lang="en"&gt;
+&lt;body&gt;
+
+ {% if youngest_teams %}
+    &lt;ul&gt;
+    {% for team in youngest_teams %}
+        &lt;li&gt;\{\{ team.team_name \}\}&lt;/li&gt;
+    {% endfor %}
+    &lt;/ul&gt;
+{% else %}
+    &lt;p&gt;No teams are available.&lt;/p&gt;
+{% endif %}
+
+&lt;/body&gt;
+&lt;/html&gt;</pre>
+
+<h2 id="¿Qué_más_puedes_hacer">¿Qué más puedes hacer?</h2>
+
+<p>Las secciones prededentes muestran las principales características que usarás en casi todas las aplicaciones web: mapeo de URLs, vistas, modelos y plantillas. Sólo unas pocas de las otras cosas que Django proporciona y que incluyen:</p>
+
+<ul>
+ <li><strong>Formularios</strong>: Los formularios HTML se usan para recolectar datos de los usuarios para su procesamiento en el servidor. Django simplifica la creación, validación y procesamiento de los formularios.</li>
+ <li><strong>Autenticación y permisos de los usuarios</strong>: Django incluye un sistema robusto de autenticación y permisos que ha sido construido con la seguridad en mente.</li>
+ <li><strong>Cacheo</strong>: La creación dinámica de contenido es mucho más intensiva computacionalmente (y lenta) que un servico de contenido estático. Django proporciona un cacheo flexible de forma que puedes almacenar todo o parte de una página renderizada para que no sea re-renderizada nada más que cuando sea necesario.</li>
+ <li><strong>Sitio de Administracion</strong>: el sitio de administración de Django está incluido por defecto cuando creas una app usando el esqueleto básico. Esto hace que sea trivialmente fácil proporcionar una página de adminsitración para que los administradores puedan crear, editar y visualizar cualquiera de los modelos de datos de su sitio.</li>
+ <li><strong>Serialización de datos</strong>: Django hace fácil el serializar y servir tus datos como XML o JSON. Esto puede ser útil cuando se está creando un servicio web (un sitio web que sólo sirve datos para ser consumidos por otras aplicaciones o sitios, y que no presenta en pantalla nada por sí mismo), o cuando se crea un sitio web en el que el código del lado cliente maneja toda la renderización de los datos.</li>
+</ul>
+
+<h2 id="Sumario">Sumario</h2>
+
+<p>¡Felicidades, has completado el primer paso en tu viaje por Django! Deberías ahora ser consciente de los principales beneficios de Django, algo de su historia y a groso modo la pinta que tienen cada una de las partes principales de una de sus apps. Deberías también haber aprendido unas pocas cosas acerca del lenguaje de programación Python, incluyendo la sintaxis para las listas, funciones y clases.</p>
+
+<p>Has visto ya algo de código real de Django más arriba, pero a diferencia del código de lado cliente, necesitas configurar un entorno de desarrollo para hacerlo funcionar. Ese será nuestro siguiente paso.</p>
+
+<div>{{NextMenu("Learn/Server-side/Django/development_environment", "Learn/Server-side/Django")}}
+<h2 id="En_este_módulo">En este módulo</h2>
+
+<ul>
+ <li><a href="https://developer.mozilla.org/es/docs/Learn/Server-side/Django/Introducci%C3%B3n">Introducción a Django</a></li>
+ <li><a href="https://developer.mozilla.org/es/docs/Learn/Server-side/Django/development_environment">Puesta en marcha de un entorno de desarrollo de Django</a></li>
+ <li><a href="https://developer.mozilla.org/es/docs/Learn/Server-side/Django/Tutorial_local_library_website">Tutorial de Django: Sitio web "Biblioteca Local"</a></li>
+ <li><a href="https://developer.mozilla.org/es/docs/Learn/Server-side/Django/skeleton_website">Tutorial de Django Parte 2: Creación del esqueleto de un sitio web</a></li>
+ <li><a href="https://developer.mozilla.org/es/docs/Learn/Server-side/Django/Models">Tutorial de Django Parte 3: Uso de modelos</a></li>
+ <li><a href="https://developer.mozilla.org/es/docs/Learn/Server-side/Django/Admin_site">Tutorial de Django Parte 4: Sitio de Administración de Django</a></li>
+ <li><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></li>
+ <li><a href="https://developer.mozilla.org/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="https://developer.mozilla.org/es/docs/Learn/Server-side/Django/Sessions">Tutorial de Django Parte 7: Framework de sesiones</a></li>
+ <li><a href="https://developer.mozilla.org/es/docs/Learn/Server-side/Django/Authentication">Tutorial de Django Parte 8: Autenticación y permisos de Usuario</a></li>
+ <li><a href="https://developer.mozilla.org/es/docs/Learn/Server-side/Django/Forms">Tutorial de Django Parte 9: Trabajo con formularios</a></li>
+ <li><a href="https://developer.mozilla.org/es/docs/Learn/Server-side/Django/Testing">Tutorial de Django Parte 10: Probando una aplicación web Django</a></li>
+ <li><a href="https://developer.mozilla.org/es/docs/Learn/Server-side/Django/Deployment">Tutorial de Django Parte 11: Desplegando Django a producción</a></li>
+ <li><a href="https://developer.mozilla.org/es/docs/Learn/Server-side/Django/web_application_security">Seguridad de las aplicaciones web Django</a></li>
+ <li><a href="/es/docs/Learn/Server-side/Django/django_assessment_blog">DIY Django mini blog</a></li>
+</ul>
+</div>
diff --git a/files/es/learn/server-side/django/models/index.html b/files/es/learn/server-side/django/models/index.html
new file mode 100644
index 0000000000..95b6c670ea
--- /dev/null
+++ b/files/es/learn/server-side/django/models/index.html
@@ -0,0 +1,490 @@
+---
+title: 'Tutorial Django Parte 3: Uso de modelos'
+slug: Learn/Server-side/Django/Models
+tags:
+ - Aprender
+ - Artículo
+ - Datos
+ - Modelo
+ - Principiante
+ - Tutorial
+ - django
+ - lado-servidor
+translation_of: Learn/Server-side/Django/Models
+---
+<div>{{LearnSidebar}}</div>
+
+<div>{{PreviousMenuNext("Learn/Server-side/Django/skeleton_website", "Learn/Server-side/Django/Admin_site", "Learn/Server-side/Django")}}</div>
+
+<p class="summary">Este artículo muestra cómo definir modelos para el sitio web de la <a href="https://developer.mozilla.org/es/docs/Learn/Server-side/Django/Tutorial_local_library_website">BibliotecaLocal</a>. En él se explica lo que es un modelo, cómo se declara, y cuáles son algunos de los principales tipos de campos de un modelo. También veremos, brevemente, cuáles son algunas de las maneras en que puede accederse a los datos del modelo.</p>
+
+<table class="learn-box standard-table">
+ <tbody>
+ <tr>
+ <th scope="row">Pre-requisitos:</th>
+ <td><a href="https://developer.mozilla.org/es/docs/Learn/Server-side/Django/skeleton_website">Tutorial Django Parte 2: Creación del esqueleto del sitio web</a>.</td>
+ </tr>
+ <tr>
+ <th scope="row">Objetivo:</th>
+ <td>
+ <p>Ser capaz de diseñar y crear tus propios modelos, eligiendo de forma apropiada los campos.</p>
+ </td>
+ </tr>
+ </tbody>
+</table>
+
+<h2 id="Visión_general">Visión general</h2>
+
+<p>Las aplicaciones web de Django acceden y administran los datos a través de objetos de Python a los que se hace referencia como modelos. Los modelos definen la<em> estructura</em> de los datos almacenados, incluidos los <em>tipos</em> de campo y los atributos de cada campo, como su tamaño máximo, valores predeterminados, lista de selección de opciones, texto de ayuda para la documentación, texto de etiqueta para formularios, etc. La definición del modelo es independiente de la base de datos subyacente. puede elegir una de entre varias como parte de la configuración de su proyecto. Una vez que haya elegido la base de datos que desea usar, no necesita hablar directamente con ella. Simplemente escriba la estructura de su modelo y algo de código, y Django se encargará de todo el trabajo sucio, al comunicarse con la base de datos por usted.</p>
+
+<p>Este tutorial muestra cómo definir y acceder a los modelos para el ejemplo del <a href="https://developer.mozilla.org/es/docs/Learn/Server-side/Django/Tutorial_local_library_website">sitio web LocalLibrary</a>.</p>
+
+<h2 id="Diseñando_los_modelos_de_LocalLibrary">Diseñando los modelos de LocalLibrary</h2>
+
+<p>Antes de dar el salto y comenzar a codificar los modelos, vale la pena tomarse unos minutos para pensar qué datos necesitamos almacenar y cuáles serán las relaciones entre los diferentes objetos.</p>
+
+<p>Sabemos que tenemos que almacenar información sobre libros (título, resumen, autor, idioma escrito, categoría, ISBN) y que podríamos tener varias copias disponibles (con id único global, estado de disponibilidad, etc.). Es posible que necesitemos almacenar más información sobre el autor que solo su nombre, y puede haber varios autores con el mismo nombre o nombres similares. Queremos poder ordenar la información según el título del libro, el autor, el idioma escrito y la categoría.</p>
+
+<p>Al diseñar sus modelos, tiene sentido tener modelos separados para cada "objeto" (grupo de información relacionada). En este caso, los objetos obvios son libros, instancias de libros y autores.</p>
+
+<p>También es posible que desee utilizar modelos para representar las opciones de la lista de selección (por ejemplo, como una lista desplegable de opciones), en lugar de codificar las opciones en el sitio web en sí; esto se recomienda cuando no se conocen de antemano todas las opciones posibles o éstas están sujetas a cambios. Los candidatos obvios para las modelos, en este caso, incluyen el género del libro (por ejemplo, ciencia ficción, poesía francesa, etc.) y el idioma (inglés, francés, japonés).</p>
+
+<p>Una vez que hayamos decidido cuáles serán nuestros modelos y sus campos, debemos pensar en la relación que existe entre ellos. Django le permite definir relaciones de uno a uno (<span style='font-family: "Courier New";'>OneToOneField</span>), de uno a muchos (<span style='font-family: "Courier New";'>ForeignKey</span>) y de muchos a muchos (<span style='font-family: "Courier New";'>ManyToManyField</span>).</p>
+
+<p>Con esto en mente, el diagrama de asociación UML a continuación muestra los modelos que definiremos en este caso (como recuadros). Como se mencionó anteriormente, hemos creado modelos para el libro (los detalles genéricos del libro), instancia del libro (estado de copias físicas específicas del libro disponible en el sistema) y autor. También hemos decidido tener un modelo para el género, para que los valores se puedan crear/seleccionar a través de la interfaz admin. Hemos decidido no tener un modelo para el <span style='font-family: "Courier New";'>BookInstance:status</span>, en su lugar, hemos especificado directamente, en el código, los valores (<span style='font-family: "Courier New";'>LOAN_STATUS</span>) porque no esperamos que cambien. Dentro de cada uno de los cuadros, puede ver el nombre del modelo, los nombres y tipos de campo, y también los métodos y sus tipos de devolución.</p>
+
+<p>El diagrama también muestra las relaciones entre los modelos, incluida su <em>cardinalidad</em>. La cardinalidad expresa la cantidad de instancias (máximo y mínimo) de cada modelo que pueden estar presentes en la relación. Por ejemplo, la línea de conexión entre los cuadros muestra que <em>Book</em> y <em>Genre</em> están relacionados. Los números cercanos al modelo <em>Book</em> muestran que un libro debe tener uno o más <em>Genres</em> (tantos como desee), mientras que los números al otro lado de la línea al lado de <em>Genre</em> muestran que puede tener cero o más libros asociados.</p>
+
+<p><img alt="LocalLibrary Model UML" src="https://mdn.mozillademos.org/files/15646/local_library_model_uml.png" style="height: 660px; width: 977px;"></p>
+
+<div class="note">
+<p><strong>Nota</strong>: La siguiente sección proporciona un manual básico que explica cómo se definen y utilizan los modelos. Mientras lo lees, considera cómo construiremos cada uno de los modelos en el diagrama de arriba.</p>
+</div>
+
+<h2 id="Cartilla_del_Modelo">Cartilla del Modelo</h2>
+
+<p>Esta sección provee una vista resumida de cómo se define un modelo y algunos de los campos más importantes y argumentos de campo. </p>
+
+<h3 id="Definición_de_modelo">Definición de modelo</h3>
+
+<p>Los modelos están definidos, normalmente, en el archivo <strong>models.py</strong> de la aplicación. Son implementados como subclases de <code>django.db.models.Model</code>, y pueden incluir campos, métodos y metadata. El fragmento de código más abajo muestra un modelo "típico", llamado <code>MyModelName</code>:</p>
+
+<pre class="notranslate">from django.db import models
+
+class MyModelName(models.Model):
+    """
+    Una clase típica definiendo un modelo, derivado desde la clase Model.
+    """
+
+  # Campos
+    my_field_name = models.CharField(max_length=20, help_text="Enter field documentation")
+  ...
+
+  # Metadata
+ class Meta:
+  ordering = ["-my_field_name"]
+
+  # Métodos
+    def get_absolute_url(self):
+         """
+         Devuelve la url para acceder a una instancia particular de MyModelName.
+         """
+         return reverse('model-detail-view', args=[str(self.id)])
+
+    def __str__(self):
+        """
+        Cadena para representar el objeto MyModelName (en el sitio de Admin, etc.)
+        """
+        return self.field_name</pre>
+
+<p>En las secciones de abajo exploraremos cada una de las características interiores de un modelo en detalle:</p>
+
+<h4 id="Campos">Campos</h4>
+
+<p>Un modelo puede tener un número arbitrario de campos, de cualquier tipo. Cada uno representa una columna de datos que queremos guardar en nuestras tablas de la base de datos. Cada registro de la base de datos (fila) consistirá en uno de cada posible valor del campo. Echemos un vistazo al ejemplo visto arriba:</p>
+
+<pre class="brush: js notranslate">my_field_name = models.CharField(max_length=20, help_text="Enter field documentation")</pre>
+
+<p>Nuestro ejemplo de arriba tiene un único campo llamado <code>my_field_name</code>, de tipo <code>models.CharField</code> — lo que significa que este campo contendrá una cadena de caracteres alfanuméricos. Los tipos de campo son asignados usando clases específicas, que determinan el tipo de registro que se usa para guardar el dato en la base, junto con un criterio de evaluación que se usará cuando se reciban los valores de un formulario HTML (es decir, qué constituye un valor válido). Los tipos de campo pueden también tomar argumentos que especifican además cómo se guarda o cómo se puede usar. En este caso le damos a nuestro campo dos argumentos:</p>
+
+<ul>
+ <li><code>max_length=20</code> — Establece que la longitud máxima del valor de este campo es 20 caracteres.</li>
+ <li><code>help_text="Enter field documentation"</code> — proporciona una etiqueta de texto para mostrar que ayuda a los usuarios a saber qué valor proporcionar cuando un usuario ha de introducirlo via un formulario HTML.</li>
+</ul>
+
+<p>El nombre del campo se usa para referirnos a él en consultas (<em>queries</em>) y plantillas (<em>templates</em>). Los campos también tienen una etiqueta, que puede ser especificada como argumento (<code>verbose_name</code>) o inferida automáticamente, a partir del nombre de variable que identifica al campo, capitalizando la primera letra y reemplazando los guiones bajos por espacios (por ejemplo <code>my_field_name</code> tendría la etiqueta por defecto de <em>My field name</em>). El orden en que los campos son declarados afectará su orden por defecto si un modelo es renderizado en un formulario (ej. en el sitio de Administración), aunque este comportamiento se puede anular.</p>
+
+<h5 id="Argumentos_comunes_de_los_campos">Argumentos comunes de los campos</h5>
+
+<p>Los siguientes argumentos son comunes a la mayoría de los tipos de campo y pueden usarse al declararlos:</p>
+
+<ul>
+ <li><a href="https://docs.djangoproject.com/en/1.10/ref/models/fields/#help-text">help_text</a>: Proporciona una etiqueta de texto para formularios HTML (ej. en el sitio de Administración), tal como se describe arriba.</li>
+ <li><a href="https://docs.djangoproject.com/en/1.10/ref/models/fields/#verbose-name">verbose_name</a>: Nombre de fácil lectura que se usa en etiquetas para el campo. Si no se especifica, Django inferirá el valor por defecto del verbose name a partir del nombre del campo.</li>
+ <li><a href="https://docs.djangoproject.com/en/1.10/ref/models/fields/#default">default</a>: Valor por defecto para el campo. Puede ser un valor o un <em>callable object</em> (objeto que puede ser llamado como una función), en cuyo caso el objeto será llamado cada vez que se cree un nuevo registro.</li>
+ <li><a href="https://docs.djangoproject.com/en/1.10/ref/models/fields/#null">null</a>: Si es <code>True</code>, Django guardará valores en blanco o vacíos como <code>NULL</code> en la base de datos para campos donde sea apropiado (un <code>CharField</code> guardará una cadena vacía en su lugar). Por defecto es <code>False</code>.</li>
+ <li><a href="https://docs.djangoproject.com/en/1.10/ref/models/fields/#blank">blank</a>: Si es <code>True</code>, se permite que el campo quede en blanco en tus formularios. El valor por defecto es <code>False</code>, lo que significa que la validación de formularios de Django te forzará a introducir un valor. Con frecuencia se usa con <code>null=True</code>, porque si vas a permitir valores en blanco, también querrás que la base de datos sea capaz de representarlos de forma apropiada.</li>
+ <li><a href="https://docs.djangoproject.com/en/1.10/ref/models/fields/#choices">choices</a>: Un grupo de valores de selección para este campo. Si se proporciona, el widget correspondiente por defecto del formulario será una caja de selección con estos valores de selección en vez del campo de texto estándar.</li>
+ <li><a href="https://docs.djangoproject.com/en/1.10/ref/models/fields/#primary-key">primary_key</a>: Si es <code>True</code>, establece el campo actual como clave primaria para el modelo (Una clave primaria es una columna especial de la base de datos, diseñada para identificar de forma única todos los diferentes registros de una tabla). Si no se especifica ningún campo como clave primaria, Django añadirá automáticamente un campo para este propósito.</li>
+</ul>
+
+<p>Hay muchas otras opciones — puedes ver la <a href="https://docs.djangoproject.com/en/1.10/ref/models/fields/#field-options">lista completa de opciones aquí</a>.</p>
+
+<h5 id="Tipos_comunes_de_campos">Tipos comunes de campos</h5>
+
+<p>La lista siguiente describe algunos de los tipos de campo más comunmente usados. </p>
+
+<ul>
+ <li><a href="https://docs.djangoproject.com/en/1.10/ref/models/fields/#django.db.models.CharField">CharField</a> se usa para definir cadenas de longitud corta a media. Debes especificar la <code>max_length</code> (longitud máxima) de los datos que se guardarán.</li>
+ <li><a href="https://docs.djangoproject.com/en/1.10/ref/models/fields/#django.db.models.TextField">TextField</a> se usa para cadenas de longitud grande o arbitraria. Puedes especificar una <code>max_length</code> para el campo, pero sólo se usa cuando el campo se muestra en formularios (no se fuerza al nivel de la base de datos).</li>
+ <li><a href="https://docs.djangoproject.com/en/1.10/ref/models/fields/#django.db.models.IntegerField" title="django.db.models.IntegerField">IntegerField</a> es un campo para almacenar valores de números enteros y para validar los valores introducidos como enteros en los formularios.</li>
+ <li><a href="https://docs.djangoproject.com/en/1.10/ref/models/fields/#datefield">DateField</a> y <a href="https://docs.djangoproject.com/en/1.10/ref/models/fields/#datetimefield">DateTimeField</a> se usan para guardar/representar fechas e información fecha/hora (como en los objetos Python <code>datetime.date</code>  y <code>datetime.datetime</code>, respectivamente). Estos campos pueden adicionalmente declarar los parámetros (mutuamente excluyentes) <code>auto_now=True</code> (para establecer el campo a la fecha actual cada vez que se guarda el modelo), <code>auto_now_add</code> (para establecer sólo la fecha cuando se crea el modelo por primera vez), y <code>default</code> (para establecer una fecha por defecto que puede ser sobreescrita por el usuario).</li>
+ <li><a href="https://docs.djangoproject.com/en/1.10/ref/models/fields/#emailfield">EmailField</a> se usa para validar direcciones de correo electrónico.</li>
+ <li><a href="https://docs.djangoproject.com/en/1.10/ref/models/fields/#filefield">FileField</a> e <a href="https://docs.djangoproject.com/en/1.10/ref/models/fields/#imagefield">ImageField</a> se usan para subir ficheros e imágenes respectivamente (el <code>ImageField</code> añade simplemente una validación adicional de que el fichero subido es una imagen). Éstos tienen parámetros para definir cómo y donde se guardan los ficheros subidos.</li>
+ <li><a href="https://docs.djangoproject.com/en/1.10/ref/models/fields/#autofield">AutoField</a> es un tipo especial de <code>IntegerField</code> que se incrementa automáticamente. Cuando no especificas una clave primaria para tu modelo, se añade -automáticamente- una de éste tipo.</li>
+ <li><a href="https://docs.djangoproject.com/en/1.10/ref/models/fields/#foreignkey">ForeignKey</a> se usa para especificar una relación uno a muchos con otro modelo de la base de datos (ej. un coche tiene un fabricante, pero un fabricante puede hacer muchos coches). El lado "uno" de la relación es el modelo que contiene la clave.</li>
+ <li><a href="https://docs.djangoproject.com/en/1.10/ref/models/fields/#manytomanyfield">ManyToManyField</a> se usa para especificar una relación muchos a muchos (ej. un libro puede tener varios géneros, y cada género puede contener varios libros). En nuestra aplicación de la biblioteca usaremos ésta de forma muy similar a <code>ForeignKeys</code>, pero pueden usarse de formas más complicadas para describir las relaciones entre grupos. Éstas tienen el parámetro <code>on_delete</code> para definir que ocurre cuando un registro asociado se borra (ej. un valor de <code>models.SET_NULL</code> establecería simplemente el valor a <code>NULL</code>).</li>
+</ul>
+
+<p>Hay muchos otros tipos de campos, incluyendo campos para diferentes tipos de números (enteros grandes, enteros pequeños, en coma flotante), boleanos, URLs, slugs, identificadores únicos, y otra información relacionada con el tiempo (duración, hora, etc..). Puedes ver la <a href="https://docs.djangoproject.com/en/1.10/ref/models/fields/#field-types">lista completa aquí</a>.</p>
+
+<h4 id="Metadatos">Metadatos</h4>
+
+<p>Puedes declarar metadatos a nivel de modelo para tu Modelo declarando <code>class Meta</code>, tal como se muestra.</p>
+
+<pre class="brush: python notranslate">class Meta:
+ ordering = ["-my_field_name"]
+  ...</pre>
+
+<p>Una de las características más útiles de estos metadatos es controlar el <em>orden por defecto</em> de los registros que se devuelven cuando se consulta el tipo de modelo. Se hace especificando el orden de comprobación en una lista de nombres de campo en el atributo <code>ordering</code>, como se muestra arriba. La ordenación dependerá del tipo de campo (los campos de caracteres de ordenan alfabéticamente, mientras que los campos de fechas están clasificados por orden cronológico). Como se muestra arriba, se puede invertir el orden de clasificación añadiendo el símbolo (-) como prefijo del nombre del campo.</p>
+
+<p>Así como ejemplo, si elegimos clasificar los libros de esta forma por defecto:</p>
+
+<pre class="brush: python notranslate">ordering = ["title", "-pubdate"]</pre>
+
+<p>los libros serán clasificados alfabéticamente por título, de la A al a Z, y luego por fecha de publicación dentro de cada título, desde el más reciente al más antiguo.</p>
+
+<p>Otro atributo común es <code>verbose_name</code>, un nombre descriptivo para la clase en forma singular y plural:</p>
+
+<pre class="brush: python notranslate">verbose_name = "BetterName"</pre>
+
+<p>Otros atributos útiles te permiten crear y aplicar nuevos "permisos de acceso" para el modelo (los permisos por defecto se aplican automáticamente), te permiten la ordenación basado en otro campo, o declarar que la clase es "abstracta" (una clase base para la que no vas a crear registros, y que en cambio se derivará para crear otros modelos).</p>
+
+<p>Muchas de las otras opciones de metadatos controlan qué base datos debe usarse para el modelo y cómo se guardan los datos (éstas son realmente útiles si necesitas mapear un modelo a una base datos existente).</p>
+
+<p>La lista completa de opciones de metadatos está disponible aquí: <a href="https://docs.djangoproject.com/es/2.0/ref/models/options/">Opciones de metadatos de Modelos</a> (Django docs).</p>
+
+<h4 id="Metodos">Metodos</h4>
+
+<p>Un modelo puede tener también métodos</p>
+
+<p>Minimamente, en cada modelo deberías definir el método estándar de las clases de Python <code>__str__()</code> para devolver una cadena de texto legible por humanos para cada objeto. Esta cadena se usa para representar registros individuales en el sitio de administración (y en cualquier otro lugar donde necesites referirte a una instancia del modelo). Con frecuencia éste devolverá un título o nombre de campo del modelo.</p>
+
+<pre class="brush: python notranslate">def __str__(self):
+  return self.field_name</pre>
+
+<p>Otro método común a incluir en los modelos de Django es <code>get_absolute_url()</code>, que devuelve un URL para presentar registros individuales del modelo en el sitio web (si defines este método, Django añadirá automáticamente un botón "Ver en el sitio" en la ventana de edición del registro del modelo en el sitio de Administración). Un patrón típico para <code>get_absolute_url()</code> se muestra abajo.</p>
+
+<pre class="brush: python notranslate">def get_absolute_url(self):
+ """
+ Devuelve la url para acceder a una instancia particular del modelo.
+    """
+ return reverse('model-detail-view', args=[str(self.id)])
+</pre>
+
+<div class="note">
+<p><strong>Nota</strong>: Asumiendo que usarás URLs tipo <code>/myapplication/mymodelname/2</code> para presentar registros individuales para tu modelo (donde "2" es el <code>id</code> de un registro en particular), necesitarás crear un mapeador URL para pasar la respuesta e id a la "vista detallada del modelo (model detail view)" (que hará el trabajo requerido para presentar el registro). La función <code>reverse()</code> de arriba es capaz de "invertir" tu mapeador url (llamado <em>'model-detail-view' </em>en el caso de arriba) para crear una URL del formato correcto.</p>
+
+<p>Por supuesto para hacer este trabajo ¡tienes aún que escribir el mapeo URL, la vista y la plantilla!</p>
+</div>
+
+<p>Puedes también definir todos los métodos que te apetezca y llamarlos desde tu código o plantillas (siempre y cuando no reciban ningún parámetro).</p>
+
+<h3 id="Gestión_de_Modelos">Gestión de Modelos</h3>
+
+<p>Una vez que has definido tus clases de modelos puedes usarlas para crear, actualizar o borrar registros, y ejecutar consultas para obtener todos los registros o subconjuntos particulares de registros. Te mostraremos cómo hacer eso en el tutorial cuando definamos nuestras vistas, pero aquí va un breve resumen.</p>
+
+<h4 id="Creación_y_modificación_de_registros">Creación y modificación de registros</h4>
+
+<p>Para crear un registro puedes definir una instancia del modelo y llamar a <code>save()</code>.</p>
+
+<pre class="brush: python notranslate"># Creación de un nuevo registro usando el constructor del modelo.
+a_record = MyModelName(my_field_name="Instancia #1")
+
+# Guardar el objeto en la base de datos.
+a_record.save()
+</pre>
+
+<div class="note">
+<p><strong>Nota</strong>: Si no has declarado ningún campo como <code>primary_key</code>, al nuevo registro se le proporcionará una automáticamente, con el nombre de campo <code>id</code>. Puedes consultar este campo después de guardar el registro anterior y debería tener un valor de 1.</p>
+</div>
+
+<p>Puedes acceder a los campos de este nuevo registro usando la sintaxis de puntos y cambiar los valores. Tienes que llamar a <code>save()</code> para almacenar los valores modificados en la base de datos.</p>
+
+<pre class="brush: python notranslate"># Accesso a los valores de los campos del modelo usando atributos Python.
+print(a_record.id) # Debería devolver 1 para el primer registro.
+print(a_record.my_field_name) # Debería imprimir 'Instancia #1'
+
+# Cambio de un registro modificando los campos llamando a save() a continuación.
+a_record.my_field_name="Nuevo Nombre de Instancia"
+a_record.save()</pre>
+
+<h4 id="Búsqueda_de_registros">Búsqueda de registros</h4>
+
+<p>Puedes buscar registros que coincidan con un cierto criterio usando el atributo <code>objects</code> del modelo (proporcionado por la clase base).</p>
+
+<div class="note">
+<p><strong>Nota</strong>: Explicar cómo buscar registros usando un modelo y nombres de campo "abstractos" puede resultar un poco confuso. En la exposición de abajo nos referiremos a un modelo <code>Book</code> con campos <code>title</code> y <code>genre</code>, donde genre (género) es también un modelo con un solo campo <code>name</code>.</p>
+</div>
+
+<p>Podemos obtener todos los registros de un modelo como <code>QuerySet</code>, usando <code>objects.all()</code>. El <code>QuerySet</code> es un objeto iterable, significando que contiene un número de objetos por los que podemos iterar/hacer bucle.</p>
+
+<pre class="brush: python notranslate">all_books = Book.objects.all()
+</pre>
+
+<p>El método de Django <code>filter()</code> nos permite filtrar el <code>QuerySet</code> devuelto para que coincida un campo de <strong>texto</strong> o <strong>numérico</strong> con un criterio particular. Por ejemplo, para filtrar libros que contengan la palabra "wild" en el título y luego contarlos, podemos hacer lo siguiente:</p>
+
+<pre class="brush: python notranslate">wild_books = Book.objects.filter(title__contains='wild')
+number_wild_books = Book.objects.filter(title__contains='wild').count()
+</pre>
+
+<p>Los campos a buscar y el tipo de coincidencia son definidos en el nombre del parámetro de filtro, usando el formato: <code>field_name__match_type</code> (ten en cuenta el <em>doble guión bajo</em> entre <code>title</code> y <code>contains</code> anterior). En el ejemplo anterior estamos filtrando <code>title</code> por un valor sensible a mayúsculas y minúsculas. Puedes hacer otros muchos tipos de coincidencias: <code>icontains</code> (no sensible a mayúsculas ni minúsculas), <code>iexact</code> (coincidencia exacta no sensible a mayúsculas ni minúsculas), <code>exact</code> (coincidencia exacta sensible a mayúsculas y minúsculas) e <code>in</code>, <code>gt</code> (mayor que), <code>startswith</code>, etc. Puede ver la <a href="https://docs.djangoproject.com/en/1.10/ref/models/querysets/#field-lookups">lista completa aquí</a>.</p>
+
+<p>En algunos casos, necesitarás filtrar por un campo que define una relación uno-a-muchos con otro modelo (por ejemplo, una <code>ForeignKey</code>). En estos casos puedes "referenciar" a campos dentro del modelo relacionado con un doble guión bajo adicional. Así, por ejemplo, para filtrar los libros de un género específico tienes que referenciar el <code>name</code> a través del campo <code>genre</code> como se muestra más abajo:</p>
+
+<pre class="brush: python notranslate">books_containing_genre = Book.objects.filter(genre<strong>__</strong>name<strong>__</strong>icontains='fiction') # Will match on: Fiction, Science fiction, non-fiction etc.
+</pre>
+
+<div class="note">
+<p><strong>Nota</strong>: Puedes usar guiones bajos (__)  para navegar por tantos niveles de relaciones (<code>ForeignKey</code>/<code>ManyToManyField</code>) como quieras. Por ejemplo, un Book que tuviera diferentes "types", definidos usando una relación adicional "cover", podría tener un nombre de parámetro: <code>type__cover__name__exact='hard'.</code></p>
+</div>
+
+<p>Puedes hacer muchas más cosas con las consultas, incluyendo búsquedas hacia atrás de modelos relacionados, filtros encadenados, devolver un conjunto de valores más pequeño, etc. Para más información, puedes consultar <a href="https://docs.djangoproject.com/en/1.10/topics/db/queries/">Elaborar consultas</a> (Django Docs).</p>
+
+<h2 id="Definiendo_los_Modelos_de_LocalLybrary">Definiendo los Modelos de LocalLybrary</h2>
+
+<p>En esta sección comenzaremos a definir los modelos para nuestra biblioteca. Abre <em>models.py (en /locallibrary/catalog/)</em>. El código de más arriba importa el módulo <code>models</code> que contiene la clase <code>models.Model</code>, que servirá de base para nuestros modelos:</p>
+
+<pre class="brush: python notranslate">from django.db import models
+
+# Create your models here.</pre>
+
+<h3 id="Modelo_Genre">Modelo 'Genre'</h3>
+
+<p>Copia el código del modelo <code>Genre</code> que se muestra abajo y pégalo al final de tu archivo <code>models.py</code>. Este modelo nos servirá para almacenar información relativa a la categoría del libro (por ejemplo, si es ficción o no, si es un romancero o es un libro de historia, etc.) Como se dijo más arriba, preferimos modelar el género (Genre) como una entidad, en vez de utilizar un campo de texto o una lista de opciones, porque de esta manera es posible manejar los valores a través de nuestra base de datos, en vez de fijarlo en el código (<em>hard-coding</em>)</p>
+
+<pre class="brush: python notranslate">class Genre(models.Model):
+    """
+    Modelo que representa un género literario (p. ej. ciencia ficción, poesía, etc.).
+    """
+    name = models.CharField(max_length=200, help_text="Ingrese el nombre del género (p. ej. Ciencia Ficción, Poesía Francesa etc.)")
+
+    def __str__(self):
+        """
+        Cadena que representa a la instancia particular del modelo (p. ej. en el sitio de Administración)
+        """
+        return self.name</pre>
+
+<p>El modelo tiene un único campo (<code>name</code>), de tipo <code>CharField</code>, que usaremos para describir el género literario. Este campo tiene un tamaño máximo (<code>max_length</code>) de 200 caracteres y, además, posee un <code>help_text</code>. Al final de la clase, hemos declarado el método <code>__str__()</code>, que simplemente devuelve el nombre de un género definido por un registro en particular. Como no hemos definido un nombre explicativo (<code>verbose_name</code>) para nuestro campo, éste se establecerá en <code>Name</code> y se mostrará de esa manera en los formularios.</p>
+
+<h3 id="Modelo_Book">Modelo 'Book'</h3>
+
+<p>Copia el código del modelo <code>Book</code> que aparece más abajo y pégalo al final de tu archivo. El modelo Libro representa la información que se tiene sobre un libro, en sentido general, pero no sobre un libro particular que se encuentre disponible en la biblioteca. Este modelo utiliza campos de tipo <code>CharField</code> para representar el título (<code>title)</code> y el <code>isbn</code> del libro (nota que el campo <code>isbn</code> especifica su etiqueta como "ISBN" utilizando el primer parámetro posicional, ya que la etiqueta por defecto hubiera sido "Isbn"). Además tenemos un campo para la sinopsis (<code>summary</code>), de tipo <code>TextField</code>, ya que este texto podría ser bastante largo.</p>
+
+<pre class="brush: python notranslate">from django.urls import reverse #Used to generate URLs by reversing the URL patterns
+
+class Book(models.Model):
+    """
+    Modelo que representa un libro (pero no un Ejemplar específico).
+    """
+
+    title = models.CharField(max_length=200)
+
+    author = models.ForeignKey('Author', on_delete=models.SET_NULL, null=True)
+    # ForeignKey, ya que un libro tiene un solo autor, pero el mismo autor puede haber escrito muchos libros.
+    # 'Author' es un string, en vez de un objeto, porque la clase Author aún no ha sido declarada.
+
+    summary = models.TextField(max_length=1000, help_text="Ingrese una breve descripción del libro")
+
+ isbn = models.CharField('ISBN',max_length=13, help_text='13 Caracteres &lt;a href="https://www.isbn-international.org/content/what-isbn"&gt;ISBN number&lt;/a&gt;')
+
+    genre = models.ManyToManyField(Genre, help_text="Seleccione un genero para este libro")
+    # ManyToManyField, porque un género puede contener muchos libros y un libro puede cubrir varios géneros.
+    # La clase Genre ya ha sido definida, entonces podemos especificar el objeto arriba.
+
+ def __str__(self):
+ """
+        String que representa al objeto Book
+        """
+        return self.title
+
+
+    def get_absolute_url(self):
+        """
+        Devuelve el URL a una instancia particular de Book
+        """
+        return reverse('book-detail', args=[str(self.id)])
+</pre>
+
+<p>El género es un campo de tipo <code>ManyToManyField</code>, de manera tal que un mismo libro podrá abarcar varios géneros y un mismo género podrá abarcar muchos libros. El autor es declarado como <code>ForeignKey</code>, de modo que cada libro podrá tener un sólo autor, pero un autor podrá tener muchos libros (en la vida real, un mismo libro puede tener varios autores, pero en nuestra implementación no).</p>
+
+<p>En la declaración de ambos campos, el modelo relacionado se ingresa como primer parámetro posicional, usando el nombre la clase que lo implementa o, bien, el nombre del modelo como string, si éste no ha sido implementado en el archivo, antes de la declaración del campo. Otros parámetros interesantes que podemos observar, en el campo <code>author</code>, son <code>null=True</code>, que permite a la base de datos almacenar <code>null</code> si el autor no ha sido seleccionado, y <code>on_delete=models.SET_NULL</code>, que pondrá en <code>null</code> el campo si el registro del autor relacionado es eliminado de la base de datos.</p>
+
+<p>El modelo también define <code>__str__()</code>, usando el campo <code>title</code> para representar un registro de la clase <code>Book</code>. El último método, <code>get_absoulte_url()</code> devuelve un URL que puede ser usado para acceder al detalle de un registro particular (para que esto funcione, debemos definir un mapeo de URL que tenga el nombre <code>book-detail</code> y una vista y una plantilla asociadas a él)</p>
+
+<h3 id="Modelo_BookInstance">Modelo 'BookInstance'</h3>
+
+<p>A continuación, copie el model <code>BookInstance</code>  (mostrado a continuación) debajo de los otros modelos. <code>BookInstance</code> representa una copia específica de un libro que alguien pueda pedir prestado, en incluye información sobre si la copia esta disponible o sobre cual es la fecha que se espera sea devuelto, "imprenta" o detalles de versión, y un id único para el libro en la biblioteca.</p>
+
+<p>Algunos de los campos y métodos ahora serán familiares. El modelo usa</p>
+
+<ul>
+ <li><code>ForeignKey</code>  para identificar el Libro asociado (cada libro puede tener muchas copias, pero una copia solo puede tener un  <code>Book</code>).</li>
+ <li><code>CharField</code> para representar la imprenta (publicación específica) del libro.</li>
+</ul>
+
+<pre class="brush: python notranslate">import uuid # Requerida para las instancias de libros únicos
+
+class BookInstance(models.Model):
+ """
+ Modelo que representa una copia específica de un libro (i.e. que puede ser prestado por la biblioteca).
+ """
+ id = models.UUIDField(primary_key=True, default=uuid.uuid4, help_text="ID único para este libro particular en toda la biblioteca")
+ book = models.ForeignKey('Book', on_delete=models.SET_NULL, null=True)
+ imprint = models.CharField(max_length=200)
+ due_back = models.DateField(null=True, blank=True)
+
+ LOAN_STATUS = (
+ ('m', 'Maintenance'),
+ ('o', 'On loan'),
+ ('a', 'Available'),
+ ('r', 'Reserved'),
+ )
+
+ status = models.CharField(max_length=1, choices=LOAN_STATUS, blank=True, default='m', help_text='Disponibilidad del libro')
+
+ class Meta:
+ ordering = ["due_back"]
+
+
+ def __str__(self):
+ """
+ String para representar el Objeto del Modelo
+ """
+ return '%s (%s)' % (self.id,self.book.title)</pre>
+
+<p>Adicionalmente hemos declarado algunos tipos nuevos de campos:</p>
+
+<ul>
+ <li><code>UUIDField</code> es usado para establecer el campo <code>id</code> como una <code>primary_key</code> para este modelo. Este tipo de campo asigna un único valor global para cada instancia ( uno para cada libro que puedes encontrar en la biblioteca).</li>
+ <li><code>DateField</code> es usado para la fecha <code>due_back</code>  (en la que se espera que el libro este diponible despues de ser prestado o estar en mantenimiento). Este valor puede ser  <code>blank</code> o <code>null</code> (necesario cuando el libro esta disponible). El patrón metadata (<code>Class Meta</code>) usa este campo para ordenar registros cuando se retornan en una consulta.</li>
+ <li><code>status</code> es un <code>CharField</code>  que define una lista de elección/selección. Como puedes ver,  hemos definido una tupla que contiene tuplas de pares clave-valor y los pasamos a los argumentos de choice. El valor en un par clave/valor es  un valor desplegable que el usuario puede seleccionar, mientras las claves son valores que en realidad son guardados en la opción seleccionada. Tambien establecemos un valor por defecto de 'm' (maintenance) ya que los libros inicialmente se crearán como no disponibles antes de que esten almacenados en los estantes.</li>
+</ul>
+
+<p>El patrón <code>__str__()</code> representa el objeto <code>BookInstance</code> usando una combinación de  su id único y el título del  <code>Book</code> asociado.</p>
+
+<div class="note">
+<p><strong>Note</strong>: Un poco de Python:</p>
+
+<ul>
+ <li>El valor retornado por <code>__str__()</code> es una <em>cadena formateada</em>. Dentro de la cadena usamos <code>%s</code> para declarar "marcadores de posición". Después de la cadena ponemos <code>%</code>  y después una tupla que contiene los valores que serán puestos en los marcadores de posición.  Si solo tienes un marcador de posición entonces puedes omitir la tupla — e.j. <code>'Mi valor: %s' % variable.</code><br>
+ <br>
+ Note que aunque este enfoque es perfectamente válido,  sea conciente que ya no es preferido. Desde  Python 3 debes usar en su lugar el método <strong>format</strong>, ej. <code>'{0} ({1})'.format(self.id,self.book.title)</code>. Puedes leer más sobre esto <a href="https://www.python.org/dev/peps/pep-3101/">aquí</a>. A partir de  Python 3.6 también puedes usar la sintaxis de interpolación de cadena, e.j. <code>f'{self.id} ({self.book.title})'</code>.</li>
+</ul>
+</div>
+
+<h3 id="Modelo_Author">Modelo 'Author'</h3>
+
+<p>Copia el modelo <code>Author</code>  (mostrado abajo) bajo el código existente en <strong>models.py</strong>.</p>
+
+<p>Todos los campos/métodos ahora deben ser familiares. El modelo define a un autor que tiene un primer nombre, apellido, fecha de nacimiento, y (opcional) fecha de fallecimiento. Especifica que de forma predeterminada el  <code>__str__()</code> retorna el nombre   en  el <em>orden apellido</em>, <em>primer nombre</em>. El método <code>get_absolute_url()</code> invierte el mapeo URL <code>author-detail</code> para obtener el URL para mostrar un autor individual.</p>
+
+<pre class="brush: python notranslate">class Author(models.Model):
+    """
+    Modelo que representa un autor
+    """
+    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):
+        """
+        Retorna la url para acceder a una instancia particular de un autor.
+        """
+        return reverse('author-detail', args=[str(self.id)])
+
+
+    def __str__(self):
+        """
+        String para representar el Objeto Modelo
+        """
+        return '%s, %s' % (self.last_name, self.first_name)
+</pre>
+
+<h2 id="Reiniciar_las_migraciones_a_la_base_de_datos">Reiniciar las migraciones a la base de datos</h2>
+
+<p>Todos tus modelos han sido creados. Para añadirlos a tu base de datos, vuelve a ejecutar las migraciones de tu base de datos.</p>
+
+<pre class="notranslate"><code>python3 manage.py makemigrations
+python3 manage.py migrate</code></pre>
+
+<h2 id="Modelo_Language_-_desafío">Modelo 'Language' - desafío</h2>
+
+<p>Imagina que un benefactor local dona un número de libros nuevos escritos en otro lenguaje (digamos, Farsi). El desafío es averiguar como estos pueden ser bien representados en tu sitio Web, y luego agregarlos a los modelos.</p>
+
+<p>Algunas cosas a considerar:</p>
+
+<ul>
+ <li>¿Debe asociarse un "lenguaje" a un <code>Book</code>, <code>BookInstance</code>, o algún otro objeto?</li>
+ <li>¿Deberian representarse  los diferentes idiomas usando un modelo, un campo de texto libre o una lista de seleccion codificada?</li>
+</ul>
+
+<p>Después que hayas decidido, agrega el campo. Puedes ver que decidimos nostros en Github <a href="https://github.com/mdn/django-locallibrary-tutorial/blob/master/catalog/models.py">aquí</a>.</p>
+
+<p>No olvides que después de un cambio en tu modelo, debes volver a hacer las migraciones para que se apliquen los cambios en tu base de datos.</p>
+
+<pre class="brush: bash notranslate"><code>python3 manage.py makemigrations</code><code>
+python3 manage.py migrate</code></pre>
+
+<ul>
+</ul>
+
+<ul>
+</ul>
+
+<h2 id="Resumen">Resumen</h2>
+
+<p>En este artículo hemos aprendido como son definidos los modelos, y luego usar esta información para diseñar e implementar modelos apropiados para el sitio Web  <em>LocalLibrary</em>.</p>
+
+<p>En este punto nos desviaremos brevemente de la creación del sitio, y miraremos el <em>sitio de Administración de </em><em>Django</em>. Este sitio nos permitirá añadir algunos datos a la biblioteca, los cuales podemos mostrar usando nuestras (aún por crear) vistas y plantillas.</p>
+
+<h2 id="Vea_también">Vea también</h2>
+
+<ul>
+ <li><a href="https://docs.djangoproject.com/en/1.10/intro/tutorial02/">Escribiendo tu primera aplicación Django, parte 2</a> (Django docs)</li>
+ <li><a href="https://docs.djangoproject.com/en/1.10/topics/db/queries/">Realizando consultas</a> (Django Docs)</li>
+ <li><a href="https://docs.djangoproject.com/en/1.10/ref/models/querysets/">Referencia del API QuerySet</a> (Django Docs)</li>
+</ul>
+
+<p>{{PreviousMenuNext("Learn/Server-side/Django/skeleton_website", "Learn/Server-side/Django/Admin_site", "Learn/Server-side/Django")}}</p>
+
+<h2 id="En_este_modulo">En este modulo</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>
diff --git a/files/es/learn/server-side/django/sessions/index.html b/files/es/learn/server-side/django/sessions/index.html
new file mode 100644
index 0000000000..f5b751c115
--- /dev/null
+++ b/files/es/learn/server-side/django/sessions/index.html
@@ -0,0 +1,200 @@
+---
+title: 'Tutorial de Django Parte 7: Framework de sesiones'
+slug: Learn/Server-side/Django/Sessions
+tags:
+ - Artículo
+ - Principiante
+ - Python
+ - Servidor
+ - Sesion
+ - Sesiones
+ - Tutorial
+ - aprende
+ - django
+ - lado-servidor
+translation_of: Learn/Server-side/Django/Sessions
+---
+<div>{{LearnSidebar}}</div>
+
+<div>{{PreviousMenuNext("Learn/Server-side/Django/Generic_views", "Learn/Server-side/Django/authentication", "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">BibliotecaLocal</a>, añadiendo un contador de visitas a tu página de inicio basado en sesiones. Es un ejemplo relativamente simple, pero muestra cómo puedes usar el framework de sesión para proporcionar persistencia a usuarios anónimos en tus propios sitios.</p>
+
+<table class="learn-box standard-table">
+ <tbody>
+ <tr>
+ <th scope="row">Prerequisitos:</th>
+ <td>Completar todos los tópicos anteriores del tutorial, incluyendo <a href="/en-US/docs/Learn/Server-side/Django/Generic_views">Django Tutorial Part 6: Generic list and detail views</a></td>
+ </tr>
+ <tr>
+ <th scope="row">Objetivo:</th>
+ <td>Entender como emplear las sesiones.</td>
+ </tr>
+ </tbody>
+</table>
+
+<h2 id="Resumen">Resumen</h2>
+
+<p>El sitio web de la <a href="https://developer.mozilla.org/es/docs/Learn/Server-side/Django/Tutorial_local_library_website">BibliotecaLocal</a> que creamos en los tutoriales previos permite a los usuarios explorar los libros y autores en el catálogo. Mientras que el contenido se genera dinámicamente desde la base de datos, todos los usuarios básicamente tendrán acceso a las mismas páginas y tipos de información cuando usan el sitio.</p>
+
+<p>En una biblioteca "real" podrías querer dar a cada usuario individual una experiencia personalizada, basada en su uso previo del sitio, preferencias, etc. Por ejemplo, podrías ocultar los mensajes de advertencia que el usuario ya ha aceptado previamente la próxima vez que visite el sitio, o guardar y respetar sus preferencias (ej. el número de resultados de búsqueda que quiere desplegar en cada página).</p>
+
+<p>El framework de sesiones te permite implementar este tipo de comportamiento, pudiendo guardar y recuperar información arbitraria en base a cada visitante particular del sitio.</p>
+
+<h2 id="¿Qué_son_las_sesiones">¿Qué son las sesiones?</h2>
+
+<p>Toda comunicación entre los navegadores web y los servidores se da a través del protocolo HTTP, que es <em>sin estado</em>. El hecho de que el protocolo sea sin estado significa que los mensajes entre el cliente y el servidor son completamente independientes entre sí -- no existe una noción de "secuencia" o comportamiento basado en mensajes previos. Como resultado, si quieres tener un sitio que guarde registro de la relación que tiene lugar con un cliente, necesitas implementarlo tú mismo.</p>
+
+<p>Las sesiones son el mecanismo que usa Django (y la mayor parte de Internet) para guardar registro del "estado" entre el sitio y un navegador en particular. Las sesiones te permiten almacenar información arbitraria por navegador, y tener esta información disponible para el sitio cuando el navegador se conecta. Cada pieza individual de información asociada con una sesión se conoce como "clave", que se usa tanto para guardar como para recuperar la información.</p>
+
+<p>Django usa una cookie que contiene un <em>id de sesión</em> específica para identificar cada navegador y su sesión asociada con el sitio. La información real de la sesión se guarda por defecto en la base de datos del sitio (esto es más seguro que guardar la información en una cookie, donde es más vulnerable para los usuarios maliciosos). Puedes configurar Django para guardar la información de sesión en otros lugares (caché, archivos, cookies "seguras"), pero la opción por defecto es una buena opción y relativamente segura.</p>
+
+<h2 id="Habilitando_las_sesiones">Habilitando las sesiones</h2>
+
+<p>Las sesiones fueron automáticamente habilitadas cuando <a href="/es/docs/Learn/Server-side/Django/skeleton_website">creamos el sitio web esqueleto</a> (en el tutorial 2).</p>
+
+<p>La configuración está establecida en las secciones <code>INSTALLED_APPS</code> y <code>MIDDLEWARE</code> del archivo del proyecto (<strong>locallibrary/locallibrary/settings.py</strong>), como se muestra abajo:</p>
+
+<pre class="brush: python notranslate">INSTALLED_APPS = [
+ ...
+<strong> 'django.contrib.sessions',</strong>
+ ....
+
+MIDDLEWARE = [
+ ...
+<strong> 'django.contrib.sessions.middleware.SessionMiddleware',</strong>
+ ....</pre>
+
+<h2 id="Usando_las_sesiones">Usando las sesiones</h2>
+
+<p>Puedes usar el atributo <code>session</code> en la vista desde el parámetro <code>request</code> (una <code>HttpRequest</code> que se envía como el primer argumento a la vista). Este atributo de sesión representa la conección específica con el usuario actual (o para ser más preciso, la conección con el <em>navegador</em> actual, como se identifica mediante la id de sesión en la cookie del navegador para este sitio).</p>
+
+<p>El atributo <code>session</code> es un objeto tipo diccionario que puedes leer y escribir tantas veces como quieras en tu vista, modificándolo como desees. Puedes realizar todas las operaciones normales de diccionario, incluyendo eliminar toda la información, probar si una clave está presente, iterar a través de la información, etc. Sin embargo, la mayor parte del tiempo solo usarás la API estándar de "diccionario" para recuperar y establecer valores.</p>
+
+<p>Los fragmentos de código de abajo te muestran cómo puedes recuperar, establecer o eliminar información con la clave "<code>my_car</code>", asociada con la sesión actual (del navegador).</p>
+
+<div class="note">
+<p><strong>Nota</strong>: Una de las cosas grandiosas de Django es que no necesitas pensar sobre los mecanismos que relacionan la sesión con tu solicitud actual en tu vista. Si fuéramos a usar los fragmentos de abajo en nuestra vista, sabríamos que la información sobre <code>my_car</code> está asociada solo con el navegador que envió la solicitud.</p>
+</div>
+
+<pre class="brush: python notranslate"># Obtener un dato de la sesión por su clave (ej. 'my_car'), generando un KeyError si la clave no existe
+my_car = request.session['my_car']
+
+# Obtener un dato de la sesión, estableciendo un valor por defecto ('mini') si el dato requerido no existe
+my_car = request.session.get('my_car', 'mini')
+
+# Asignar un dato a la sesión
+request.session['my_car'] = 'mini'
+
+# Eliminar un dato de la sesión
+del request.session['my_car']
+</pre>
+
+<p>La API ofrece también una cantidad de métodos adicionales que se usan mayoritariamente para administrar la cookie de sesión asociada. Por ejemplo, hay métodos para probar si el navegador cliente soporta cookies, establecer y revisar las fechas de expiración de las cookies, y para eliminar sesiones expiradas del almacén de datos. Puedes encontrar información sobre la API completa en <a href="https://docs.djangoproject.com/en/1.10/topics/http/sessions/">Cómo usar sesiones</a> (Django docs).</p>
+
+<h2 id="Guardando_la_información_de_la_sesión">Guardando la información de la sesión</h2>
+
+<p>Por defecto, Django solo guarda información en la base de datos de sesión y envía la cookie de sesión al cliente cuando la sesión ha sido <em>modificada</em> (asignada) o <em>eliminada</em>. Si estás actualizando algún dato usando su clave de sesión como se mostró en la sección previa, ¡no necesitas preocuparte por esto! Por ejemplo:</p>
+
+<pre class="brush: python notranslate"># Esto es detectado como un cambio en la sesión, así que la información de la sesión es guardada.
+request.session['my_car'] = 'mini'</pre>
+
+<p>Si estás actualizando algún dato <em>dentro</em> de la información de sesión, Django no reconocerá que has hecho un cambio en la sesión y guardado la información (por ejemplo, si fueras a cambiar el dato "<code>wheels</code>" dentro de tu dato "<code>my_car</code>", como se muestra abajo). En este caso, necesitarás marcar explícitamente la sesión como que ha sido modificada.</p>
+
+<pre class="brush: python notranslate"># Objeto de sesión no directamente modificada, solo información dentro de la sesión.
+# ¡Cambios no guardados!
+request.session['my_car']['wheels'] = 'alloy'
+
+# Establecer la sesión como modificada para forzar a que se guarden los cambios.
+<code>request.session.modified = True</code>
+</pre>
+
+<div class="note">
+<p><strong>Nota</strong>: Puedes cambiar el comportamiento para que el sitio actualice la base de datos y envíe la cookie en cada solicitud añádiendo <code>SESSION_SAVE_EVERY_REQUEST = True</code> a la configuración de tu proyecto (<strong>locallibrary/locallibrary/settings.py</strong>).</p>
+</div>
+
+<h2 id="Ejemplo_simple_--_obteniendo_conteos_de_visitas">Ejemplo simple -- obteniendo conteos de visitas</h2>
+
+<p>Como un ejemplo simple del mundo real, actualizaremos nuestra biblioteca para decirle al usuario actual cuántas veces ha visitado la página principal de <em>BibliotecaLocal</em>.</p>
+
+<p>Abre <strong>/locallibrary/catalog/views.py</strong>, y aplica los cambios que se muestran con negrita abajo.</p>
+
+<pre class="brush: python notranslate">def index(request):
+ ...
+
+ num_authors=Author.objects.count() # El 'all()' se obvia en este caso.
+
+<strong> # Numero de visitas a esta view, como está contado en la variable de sesión.
+ num_visits = request.session.get('num_visits', 0)
+ request.session['num_visits'] = num_visits + 1</strong>
+
+ <strong>context = {
+ 'num_books':num_books,
+ 'num_instances':num_instances,
+ 'num_instances_available':num_instances_available,
+ 'num_authors':num_authors,
+ 'num_visits':num_visits,
+ } </strong>
+
+ # Carga la plantilla index.html con la información adicional en la variable context.
+ return render(request, 'index.html', context=context)</pre>
+
+<p>Aquí primero obtenemos el valor de la clave de sesión <code>'num_visits'</code>, estableciendo el valor a 0 si no había sido establecido previamente. Cada vez que se recibe la solicitud, incrementamos el valor y lo guardamos de vuelta en la sesión (para la siguiente vez que el usuario visita la página). La variable <code>num_visits</code> se envía entonces a la plantilla en nuestra variable de contexto.</p>
+
+<div class="note">
+<p><strong>Nota</strong>: Aquí podríamos incluso revisar si el navegador soporta cookies (mira <a href="/es/docs/https://docs.djangoproject.com/en/1.10/topics/http/sessions/">Cómo usar sesiones</a> para ejemplos) o diseñar nuestra UI de modo que no importe si el navegador soporta cookies o no.</p>
+</div>
+
+<p>Añade la línea que se ve al final del siguiente bloque a tu plantilla HTML principal (<strong>/locallibrary/catalog/templates/index.html</strong>) al final de la sección "Dynamic content" para desplegar la variable de contexto:</p>
+
+<pre class="brush: html notranslate">&lt;h2&gt;Dynamic content&lt;/h2&gt;
+
+&lt;p&gt;The library has the following record counts:&lt;/p&gt;
+&lt;ul&gt;
+ &lt;li&gt;&lt;strong&gt;Books:&lt;/strong&gt; \{{ num_books }}&lt;/li&gt;
+ &lt;li&gt;&lt;strong&gt;Copies:&lt;/strong&gt; \{{ num_instances }}&lt;/li&gt;
+ &lt;li&gt;&lt;strong&gt;Copies available:&lt;/strong&gt; \{{ num_instances_available }}&lt;/li&gt;
+ &lt;li&gt;&lt;strong&gt;Authors:&lt;/strong&gt; \{{ num_authors }}&lt;/li&gt;
+&lt;/ul&gt;
+
+<strong>&lt;p&gt;You have visited this page \{{ num_visits }}{% if num_visits == 1 %} time{% else %} times{% endif %}.&lt;/p&gt;</strong>
+</pre>
+
+<p>Guarda tus cambios y reinicia el servidor de pruebas. Cada vez que refresques la página, el número se debería actualizar.</p>
+
+<ul>
+</ul>
+
+<h2 id="Resumen_2">Resumen</h2>
+
+<p>Ahora sabes lo fácil que es usar sesiones para mejorar tu interacción con usuarios <em>anónimos</em>.</p>
+
+<p>En nuestros siguientes artículos explicaremos el framework de autenticación y autorización (permisos), y te mostraremos cómo soportar cuentas de usuario.</p>
+
+<h2 id="Mira_también">Mira también</h2>
+
+<ul>
+ <li><a href="https://docs.djangoproject.com/en/1.10/topics/http/sessions/">Cómo usar sesiones</a> (Django docs)</li>
+</ul>
+
+<p>{{PreviousMenuNext("Learn/Server-side/Django/Generic_views", "Learn/Server-side/Django/Authentication", "Learn/Server-side/Django")}}</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>
diff --git a/files/es/learn/server-side/django/skeleton_website/index.html b/files/es/learn/server-side/django/skeleton_website/index.html
new file mode 100644
index 0000000000..96c8b0d3b0
--- /dev/null
+++ b/files/es/learn/server-side/django/skeleton_website/index.html
@@ -0,0 +1,397 @@
+---
+title: 'Tutorial Django Parte 2: Creación del esqueleto del sitio web'
+slug: Learn/Server-side/Django/skeleton_website
+tags:
+ - Aprendizaje
+ - Artículo
+ - Codificación de scripts
+ - Guía
+ - Principiante
+ - Tutorial
+ - django
+ - introducción
+ - lado servidor
+translation_of: Learn/Server-side/Django/skeleton_website
+---
+<div>{{LearnSidebar}}</div>
+
+<div>{{PreviousMenuNext("Learn/Server-side/Django/Tutorial_local_library_website", "Learn/Server-side/Django/Models", "Learn/Server-side/Django")}}</div>
+
+<p class="summary">Este segundo artículo de nuestro <a href="/en-US/docs/Learn/Server-side/Django/Tutorial_local_library_website">Tutorial Django</a> muestra cómo puedes crear un proyecto de "esqueleto" de sitio web como base, que puedes continuar luego llenado de configuraciones específicas del sitio, urls, modelos, vistas y plantillas.</p>
+
+<table class="learn-box standard-table">
+ <tbody>
+ <tr>
+ <th scope="row">Pre-requisitos:</th>
+ <td><a href="https://developer.mozilla.org/es/docs/Learn/Server-side/Django/development_environment">Poner en marcha un entorno de desarrollo Django</a>. Repasar el <a href="https://developer.mozilla.org/en-US/docs/Learn/Server-side/Django/Tutorial_local_library_website">Tutorial Django</a>.</td>
+ </tr>
+ <tr>
+ <th scope="row">Objetivo:</th>
+ <td>
+ <p>Ser capaz de usar las herramientas de Django para empezar tus propios proyectos de sitios web nuevos.</p>
+ </td>
+ </tr>
+ </tbody>
+</table>
+
+<h2 id="Visión_general">Visión general</h2>
+
+<p>Este artículo muestra como puedes crear un sitio web "esqueleto", que puedes luego llenar con configuraciones específicas del sitio, urls, modelos, vistas y plantillas (trataremos ésto en artículos posteriores).</p>
+
+<p>El proceso es sencillo:</p>
+
+<ol>
+ <li><span style="line-height: 1.5;">Usar la herramienta </span><code style="font-style: normal; font-weight: normal; line-height: 1.5;">django-admin</code><span style="line-height: 1.5;"> para crear la carpeta del proyecto, los ficheros de plantillas básicos y el script de gestión del proyecto </span><span style="line-height: 1.5;"> (</span><strong style="line-height: 1.5;">manage.py</strong><span style="line-height: 1.5;">).</span></li>
+ <li><span style="line-height: 1.5;">Usar </span><strong style="line-height: 1.5;">manage.py</strong><span style="line-height: 1.5;"> para crear una o más <em>aplicaciones</em></span><span style="line-height: 1.5;">.</span>
+ <div class="note">
+ <p><strong>Nota</strong>: Un sitio web puede consistir de una o más secciones, ej. sitio principal, blog, wiki, area de descargas, etc. Django te recomienda encarecidamente que desarrolles estos componentes como <em>aplicaciones</em> separadas que podrían ser reutilizadas, si se desea, en otros proyectos. </p>
+ </div>
+ </li>
+ <li><span style="line-height: 1.5;">Registrar las nuevas aplicaciones para incluirlas en el proyecto. </span></li>
+ <li><span style="line-height: 1.5;">Conectar el mapeador url de cada aplicación</span><span style="line-height: 1.5;">.</span></li>
+</ol>
+
+<p>Para el <a href="https://developer.mozilla.org/es/docs/Learn/Server-side/Django/Tutorial_local_library_website">sitio web de la BibliotecaLocal</a> la carpeta del sitio y la carpeta de su proyecto se llamarán <em>locallibrary</em>, y tendremos sólo una aplicación llamada <em>catalog</em>. El nivel más alto de la estructura de carpetas quedará por tanto como sigue:</p>
+
+<pre class="brush: bash"><em>locallibrary/ # Carpeta del sitio web</em>
+  <strong>manage.py </strong># Script para ejecutar las herramientas de Django para este proyecto (creadas usando django-admin)
+  <em>locallibrary/ # Carpeta del Sitio web/Proyecto </em>(creada usando django-admin)
+ <em>catalog/ # Carpeta de la Aplicación </em>(creada usando manage.py)
+</pre>
+
+<p><span style="line-height: 1.5;">Las siguientes secciones abordan los pasos del proceso en detalle, y muestran cómo puedes comprobar los cambios. Al final de cada artículo trataremos alguna de los otros ajustes aplicables al sitio entero que deberías también efectuar en esta etapa</span><span style="line-height: 1.5;">.</span></p>
+
+<h2 id="Creación_del_proyecto">Creación del proyecto</h2>
+
+<p>En primer lugar abre una ventana de comandos/terminal, navega hasta donde quieres almacenar tus apps Django (hazlo en algún lugar que sea fácil de encontrar, como dentro de la carpeta de tus <span style="line-height: 1.5;"><em>documentos</em>), y crea una carpeta para tu nuevo sitio web (en este caso </span><span style="line-height: 1.5;">: </span><em>locallibrary</em>). Entra en el directorio a continuación usando el comando cd:</p>
+
+<pre class="brush: bash">mkdir locallibrary
+cd locallibrary</pre>
+
+<p>Crear el nuevo proyecto usando el comando <code>django-admin startproject</code> como se muestra, y navega luego dentro de la carpeta.</p>
+
+<pre class="brush: bash">django-admin startproject locallibrary .
+cd locallibrary</pre>
+
+<p>La herramienta <code>django-admin</code> crea una estructura de carpetas/ficheros como se muestra abajo:</p>
+
+<pre class="brush: bash"><em>locallibrary/</em>
+  <strong>manage.py</strong>
+  <em>locallibrary/</em>
+    settings.py
+    urls.py
+    wsgi.py</pre>
+
+<p>La subcarpeta del proyecto <em>locallibrary</em> es el punto de entrada al sitio web: </p>
+
+<ul>
+ <li><strong>settings.py</strong> contiene todos los ajustes del sitio. Es donde registramos todas las aplicaciones que creamos, la localización de nuestros ficheros estáticos, los detalles de configuración de la base de datos, etc.</li>
+ <li><strong>urls.py</strong> define los mapeos url-vistas. A pesar de que éste podría contener <span style="line-height: 1.5;"><em>todo</em> el código del mapeo url, es más común delegar algo del mapeo a las propias aplicaciones, como verás más tarde</span><span style="line-height: 1.5;">. </span></li>
+ <li><strong style="line-height: 1.5;">wsgi.py</strong><span style="line-height: 1.5;"> se usa para ayudar a la aplicación Django a comunicarse con el servidor web. Puedes tratarlo como código base que puedes utilizar de plantilla</span><span style="line-height: 1.5;">.</span></li>
+</ul>
+
+<p>El script <strong>manage.py</strong> se usa para crear aplicaciones, trabajar con bases de datos y empezar el desarrollo del servidor web.</p>
+
+<h2 id="Creación_de_la_aplicación_catalog">Creación de la aplicación catalog</h2>
+
+<p>A continuación, ejecuta el siguiente comando para crear la aplicación <em>catalog</em> que vivirá dentro de nuestro proyecto locallibrary (éste debe ejecutarse en la misma carpeta que el <strong>manage.py</strong> de tu proyecto):</p>
+
+<pre class="brush: bash">python3 manage.py startapp catalog</pre>
+
+<div class="note">
+<p><strong>Nota</strong>: el comando de arriba es para Linux/Mac OS X. En Windows el comando debería ser: <code>py -3 manage.py startapp catalog</code></p>
+
+<p>Si estás trabajando en Windows, reemplaza <code>python3</code> por <code>py -3</code> a lo largo de este módulo o simplemente python: <code>python manage.py startapp catalog</code>.</p>
+</div>
+
+<p>La herramienta crea una nueva carpeta y la rellena con ficheros para las diferentes partes de la aplicación (mostradas en negrilla abajo). La mayoría de los ficheros se nombran de acuerdo a su propósito, para que sea má útil (ej. las vistas se deberán guardar en <strong>views.py</strong>, los Modelos en <strong>models.py</strong>, las pruebas en <strong>tests.py</strong>, la configuración del sitio de administración en <strong>admin.py</strong>, el registro de aplicaciones en <strong>apps.py</strong>) y contienen algo de código base mínimo para trabajar con los objetos asociados.</p>
+
+<p>El directorio actualizado del proyecto debería tener ahora el aspecto siguiente:</p>
+
+<pre class="brush: bash"><em>locallibrary/</em>
+  manage.py
+  <em>locallibrary/
+</em><strong>  <em>catalog/</em>
+      admin.py
+      apps.py
+      models.py
+      tests.py
+      views.py
+      __init__.py
+  <em>migrations/</em></strong>
+</pre>
+
+<p>Además ahora tenemos:</p>
+
+<ul>
+ <li>Una carpeta <em>migrations</em> que se utiliza para guardar las "migraciones"— ficheros que te permiten actualizar tus bases de datos a medida que modificas tus modelos. </li>
+ <li><strong>__init__.py</strong> — Un fichero vacío creado aquí para que Django/Python reconozca la carpeta como un <a href="https://docs.python.org/3/tutorial/modules.html#packages">Paquete Python</a> y te permita usar sus objetos dentro de otras partes del proyecto.</li>
+</ul>
+
+<div class="note">
+<p><strong>Nota</strong>: ¿Te has dado cuenta qué es lo que falta en la lista de ficheros de arriba? Si bien hay un lugar para que coloques tus vistas y modelos, no hay nada para que pongas los mapeos url, las plantillas  y los ficheros estáticos. Te mostraremos cómo crearlos más adelante (éstos no se necesitan en todos los sitios web pero se necesitan en este ejemplo).</p>
+</div>
+
+<h2 id="Registro_de_la_aplicación_catalog">Registro de la aplicación catalog</h2>
+
+<p>Ahora que se ha creado la aplicación tenemos que registrarla en el proyecto de manera que sea incluida cuando cualquiera de las herramientas se ejecute (por ejemplo, para añadir modelos a la base de datos). Las aplicaciones se registran añadiéndolas a la lista de <code>INSTALLED_APPS</code> en los ajustes del proyecto.</p>
+
+<p>Abre el fichero de ajustes del proyecto <strong>locallibrary/locallibrary/settings.py</strong> y encuentra la definición de la lista <code>INSTALLED_APPS</code>. Añade a continuación una nueva linea al final de la lista, como se muestra en negrilla abajo.</p>
+
+<pre class="brush: bash">INSTALLED_APPS = [
+ 'django.contrib.admin',
+ 'django.contrib.auth',
+ 'django.contrib.contenttypes',
+ 'django.contrib.sessions',
+ 'django.contrib.messages',
+ 'django.contrib.staticfiles',
+<strong> 'catalog.apps.CatalogConfig', </strong>
+]</pre>
+
+<p>La nueva linea especifica el objeto de configuración de la aplicación (<code>CatalogConfig</code>) que se generó para tí en <strong>/locallibrary/catalog/apps.py</strong> cuando creaste la aplicación.</p>
+
+<div class="note">
+<p><strong>Nota</strong>: Te habrás fijado que hay ya un montón de otras <code>INSTALLED_APPS</code> (y <code>MIDDLEWARE</code>, más abajo en el fichero de ajustes). Éstas habilitan en soporte para el <a href="/es/docs/Learn/Server-side/Django/Admin_site">Sitio de admistración Django</a> y como consecuencia el motón de la funcionalidad que usa (incluyendo sesiones, autenticación, etc).</p>
+</div>
+
+<h2 id="Especificación_de_la_base_de_datos">Especificación de la base de datos</h2>
+
+<p>Éste es también el punto donde normalmente especificarías la base de datos a utilizar en el proyecto — tiene sentido usar la misma base datos para desarrollo y producción donde sea posible, para evitar diferencias menores en el comportamiento. Puedes encontrar información sobre las diferentes opciones de bases de datos en <a href="https://docs.djangoproject.com/en/2.0/ref/settings/#databases">Databases</a> (Django docs). </p>
+
+<p>Nosotros usaremos la base de datos SQLite para este ejemplo, porque no esperamos que se requiera un montón de accesos concurrentes en una base de datos de demostración, y también ¡porque no requiere trabajo adicional para ponerla en marcha! Puedes ver cómo está configurada en <strong>settings.py</strong> (más información también se incluye abajo):</p>
+
+<pre class="brush: python">DATABASES = {
+ 'default': {
+ 'ENGINE': 'django.db.backends.sqlite3',
+ 'NAME': os.path.join(BASE_DIR, 'db.sqlite3'),
+ }
+}
+</pre>
+
+<p>Debido a que usamos SQLite, no necesitamos hacer ningún ajuste adicional aquí. ¡Sigamos!</p>
+
+<h2 id="Otros_ajustes_del_proyecto">Otros ajustes del proyecto</h2>
+
+<p>El fichero <strong>settings.py</strong> se usa para configurar muchos otros ajustes, pero en este punto probablemente sólo querrás cambiar la <a href="https://docs.djangoproject.com/en/2.0/ref/settings/#std:setting-TIME_ZONE">TIME_ZONE</a> — ésta debería ser igual a una cadena de la <a href="https://en.wikipedia.org/wiki/List_of_tz_database_time_zones">Lista de base de datos tz de time zones</a> (la columna TZ column de la tabla contiene los valores que quieres). Cambia tu <code>TIME_ZONE</code> al valor de entre estas cadenas que sea apropiado para tu zona de tiempo, por ejemplo:</p>
+
+<pre class="brush: python">TIME_ZONE = 'Europe/Madrid'</pre>
+
+<p>Hay otros dos otros ajustes que no cambiarás ahora, pero de los que deberías ser consciente:</p>
+
+<ul>
+ <li><code>SECRET_KEY</code>. Ésta es una clave secreta que se usa como parte de la estrategia de seguridad del sitio web de Django. Si no vas a proteger este código durante el desarrollo, necesitarás usar un código diferente (quizás leyendo de una variable de entorno o un fichero) cuando lo pongas en producción. </li>
+ <li><code>DEBUG</code>. Ésto habilita que los registros (logs) de depuración se muestren en caso de error, en vez de las respuestas con los códigos de estado HTTP. Éste debería ajustarse a <code>false</code> en producción, ya que la información de depuración es útil a los atacantes. </li>
+</ul>
+
+<h2 id="Conectar_el_mapeador_URL">Conectar el mapeador URL</h2>
+
+<p>El sitio web se crea con un fichero mapeador de URLs (<strong>urls.py</strong>) en la carpeta del proyecto. Aunque puedes usar este fichero para gestionar todos tus mapeos URL, es más usual deferir los mapeos a su aplicación asociada.</p>
+
+<p>Abre <strong>locallibrary/locallibrary/urls.py</strong> y fíjate en el texto educativo que explica algunas formas de usar el mapeador URL. </p>
+
+<pre class="brush: python">"""locallibrary URL Configuration
+
+The `urlpatterns` list routes URLs to views. For more information please see:
+    https://docs.djangoproject.com/en/2.0/topics/http/urls/
+Examples:
+Function views
+    1. Add an import:  from my_app import views
+    2. Add a URL to urlpatterns:  path('', views.home, name='home')
+Class-based views
+    1. Add an import:  from other_app.views import Home
+    2. Add a URL to urlpatterns:  path('', Home.as_view(), name='home')
+Including another URLconf
+    1. Import the include() function: from django.urls import include, path
+    2. Add a URL to urlpatterns:  path('blog/', include('blog.urls'))
+"""
+from django.contrib import admin
+from django.urls import path
+
+urlpatterns = [
+    path('admin/', admin.site.urls),
+]
+</pre>
+
+<p>Los mapeos URL se gestionan a través de la variable <code>urlpatterns</code>, que es una <em>lista</em> Python de funciones <code>path()</code>. Cada función <code>path()</code> o asocia un patrón URL a una <em>vista específica</em>, que se presentará cuando el patrón se empareja o con otra lista de código de comprobación de patrones URL (en este segundo caso, los patrones se convierten en la "URL base" de patrones definidos en el módulo destino). La lista <code>urlpatterns</code> define inicialmente una función que mapea todos los URLs con el patrón <em>admin/</em> al módulo <code>admin.site.urls</code> , que contiene las definiciones de mapeos URL propios de la aplicación de Administración.</p>
+
+<div class="note">
+<p><strong>Nota</strong>: La ruta en <code>path()</code> es una cadena que define un patrón URL para emparejar. Esta cadena podría incluir una variable nombrada (entre paréntesis angulares), ej. <code>'catalog/&lt;id&gt;/'</code>. Este patrón emparejará con una URL como <strong>/catalog/</strong><em>any_chars</em><strong>/</strong> y pasará a la vista <em>any_chars</em> como cadena asociada al parámetro de nombre <code>id</code>). Trataremos de los métodos de caminos y rutas de patrones más adelante en los últimos temas.</p>
+</div>
+
+<p>Añade las lineas de abajo al final del fichero para añadir un nuevo elemento a la lista <code>urlpatterns</code>. Este nuevo elemento incluye un <code>path()</code> que redirige las peticiones con el patrón <code>catalog/</code> al módulo<code>catalog.urls</code> (el fichero con el URL relativo <strong>/catalog/urls.py</strong>).</p>
+
+<pre class="brush: python"># Use include() to add paths from the catalog application
+from django.urls import include
+
+urlpatterns += [
+    path('catalog/', include('catalog.urls')),
+]
+</pre>
+
+<p>Ahora redirijamos la URL raíz de nuestro sitio (ej. <code>127.0.0.1:8000</code>) al URL <code>127.0.0.1:8000/catalog/</code>; esta es la única app que usaremos en este proyecto, así que es lo que deberíamos hacer. Para hacer esto, usaremos una función especial (<code>RedirectView</code>), que toma como su primer argumento la nueva URL relativa a redirigir a (<code>/catalog/</code>) cuando el patrón URL especificado en la función <code>path()</code> se empareja (la URL raíz, en este caso).</p>
+
+<p>Añade las siguientes líneas, otra vez al final del fichero:</p>
+
+<pre class="brush: python">#Add URL maps to redirect the base URL to our application
+from django.views.generic import RedirectView
+urlpatterns += [
+ path('', RedirectView.as_view(url='/catalog/', permanent=True)),
+]</pre>
+
+
+
+<p>Deja el primer parámetro de la función path vacío, para implicar '/'. Si escribes el primer parámetro como '/' Django te dará la siguiente advertencia cuando arranque el servidor de desarrollo:</p>
+
+<p><em>La comprobación del sistema encontró algunos problemas:</em></p>
+
+<pre><em>WARNINGS:
+?: (urls.W002) Tu patrón URL '/' tiene una ruta que empieza con una '/'.
+Quita esta barra invertida ya que es inncesaria.
+Si este patrón figura como destino en un include(), asegúrate que el patrón include() tiene un '/' final.</em></pre>
+
+
+
+<p>Django no sirve ficheros estáticos como CSS, JavaScript e imágenes por defecto, pero puede ser útil para el servidor web de desarrollo hacerlo así mientras creas tu sitio. Como adición final a este mapeador URL, puedes habilitar el servicio de ficheros estáticos durante el desarrollo añadiendo las líneas siguientes.</p>
+
+<p>Añade ahora el siguiente bloque final al final del fichero:</p>
+
+<pre><code># Use static() to add url mapping to serve static files during development (only)
+from django.conf import settings
+from django.conf.urls.static import static
+
+urlpatterns += static(settings.STATIC_URL, document_root=settings.STATIC_ROOT)</code>
+</pre>
+
+<div class="note">
+<p><strong>Nota</strong>: Hay numerosas formas de extender la lista <code>urlpatterns</code> (arriba hemos añadido simplemente un nuevo elemento a la lista usando el operador<span style="font-size: 1.125rem;"> </span><code>+=</code><span style="font-size: 1.125rem;"> para separar claramente el código antiguo y el nuevo</span><span style="font-size: 1.125rem;">). En vez de ello podríamos haber simplemente incluido este nuevo patrón de mapeo en la definición de la lista original:</span></p>
+
+<pre class="brush: python">urlpatterns = [
+  path('admin/', admin.site.urls),
+  path('catalog/', include('catalog.urls')),
+ path('/', RedirectView.as_view(url='/catalog/', permanent=True)),
+] + <code>static(settings.STATIC_URL, document_root=settings.STATIC_ROOT)</code>
+</pre>
+
+<p>Además, hemos incluido la linea import (<code>from django.urls import include</code>) con el código que la usa (de manera que sea fácil ver qué hemos añadido), pero es común incluir todas tus líneas import al principio del fichero Python.</p>
+</div>
+
+<p>Como paso final, crea un fichero dentro de tu carpeta catalog llamado <strong>urls.py</strong>, y añade el siguiente texto para definir la <code>urlpatterns</code> importada (vacía). Éste es donde añadimos nuestros patrones a medida que construimos la aplicación. </p>
+
+<pre class="brush: python">from django.conf.urls import url
+
+from . import views
+
+
+urlpatterns = [
+
+]
+</pre>
+
+<h2 id="Prueba_del_framework_del_sitio_web">Prueba del framework del sitio web</h2>
+
+<p>En este punto tenemos un proyecto de esqueleto completo. El sitio web <em>no hace</em> realmente nada todavía, pero merece la pena ejecutarlo para estar seguros de que ninguno de nuestros cambios no han roto nada.</p>
+
+<p>Antes de hacerlo, deberíamos primero ejecutar una <em>migración de la base de datos</em>. Ésto actualiza nuestra base de datos para incluir todos los modelos de nuestras aplicaciones instaladas (y eliminar algunas advertencias de construcción).</p>
+
+<h3 id="Ejecución_de_migraciones_de_la_base_de_datos">Ejecución de migraciones de la base de datos</h3>
+
+<p>Django usa un Object-Relational-Mapper (ORM) para mapear las definiciones de Modelos en el código Django con la estructura de datos utilizada por la base de datos subyacente. A medida que cambiamos nuestras definiciones de modelos, Django sigue la pista a los cambios y puede crear scripts de migración de la base de datos (en <strong>/locallibrary/catalog/migrations/</strong>) para migrar automáticamente la estructura de datos subyacente en el base de datos para igualarse al modelo.</p>
+
+<p>Cuando creamos el sitio web de Django añadimos automáticamente unos cuantos modelos para que ser usados por la sección de administración del sitio (al que echaremos un vistazo más tarde). Ejecuta los siguientes comandos para definir tablas para esos modelos de la base (asegúrate que estás en el directorio que contiene <strong>manage.py</strong>):</p>
+
+<pre class="brush: bash">python3 manage.py makemigrations
+python3 manage.py migrate
+</pre>
+
+<div class="warning">
+<p><strong>Importante</strong>: Necesitarás ejecutar los comandos de arriba cada vez que cambien tus modelos de una manera que afecte a la estructura de datos y necesite ser guardada (incluyendo tanto la adicción como la eliminación de modelos enteros o campos individuales).</p>
+</div>
+
+<p>El comando <code>makemigrations</code> <em>crea</em> (pero no aplica) las migraciones para todas las aplicaciones instaladas en tu proyecto (también puedes especificar el nombre de una aplicación para ejecutar una migración para un sólo proyecto). Ésto te da la opoortunidad de comprobar el código para estas migraciones antes de que se apliquen — cuando seas un experto en Django ¡podrás elegir modificarlos ligeramente!</p>
+
+<p>El comando <code>migrate</code> aplica realmente las migraciones a tu base de datos (Django lleva la cuenta de cuáles han sido añadidas a la base de datos actual).</p>
+
+<div class="note">
+<p><strong>Nota</strong>: Mira en <a href="https://docs.djangoproject.com/en/2.0/topics/migrations/">Migrations</a> (Django docs) para obtener información adicional sobre los comandos de migración menos usados.</p>
+</div>
+
+<h3 id="Arrancando_el_sitio_web">Arrancando el sitio web</h3>
+
+<p>Durante el desarrollo puedes probar el sitio web usando para servirlo el <em>servidor de desarrollo web</em>, y visualizádolo en tu explorador web local. </p>
+
+<div class="note">
+<p><strong>Nota</strong>: el servidor web de desarrollo no es robusto y sin suficientes prestaciones para su uso en producción, pero es una manera muy fácil de tener levantado y funcionando tu sitio web Django durante el desarrollo para hacerle una prueba rápida y conveniente. Por defecto servirá el sitio a tu computadora local <span style="font-size: 1.125rem;">(</span><code>http://127.0.0.1:8000/)</code><span style="font-size: 1.125rem;">, pero puedes también especificar que se sirva a otras computdoras en tu red.</span><span style="font-size: 1.125rem;"> Para más información ver </span><a href="https://docs.djangoproject.com/en/2.0/ref/django-admin/#runserver" style="font-size: 1.125rem;">django-admin y manage.py: runserver</a><span style="font-size: 1.125rem;"> (Django docs).</span></p>
+</div>
+
+<p>Ejecuta el <em>servidor web de desarrollo</em> llamando al comando <code>runserver</code> (en el mismo directorio donde está <strong>manage.py</strong>):</p>
+
+<pre class="brush: bash">python3 manage.py runserver
+
+ Performing system checks...
+
+ System check identified no issues (0 silenced).
+ September 22, 2016 - 16:11:26
+ Django version 1.10, using settings 'locallibrary.settings'
+ Starting development server at http://127.0.0.1:8000/
+ Quit the server with CTRL-BREAK.
+</pre>
+
+<p>Una vez que el servidor está funcionando puedes ver el sitio navegando a <code>http://127.0.0.1:8000/</code> en tu explorador local. Deberías ver una página de error del sitio que tiene el siguiente aspecto:</p>
+
+<p><img alt="Django Debug page for Django 2.0" src="https://mdn.mozillademos.org/files/15729/django_404_debug_page.png"></p>
+
+<p>¡No te preocupes! Esta página de error es lo esperado porque no tenemos ninguna página/url definidas en el módulo <code>catalogs.urls</code> (que es al que nos redirigimos cuando obtenemos la URL a la raíz del sitio). </p>
+
+<div class="note">
+<p><strong>Nota</strong>: La página superior demuestra una gran característica de Django— El registro de depuración automático. Cada vez que una página no pueda ser encontrada, o el código provoque un error cualquiera, se mostrará una pantalla de error con información útil. En este caso vemos que la URL que hemos suministrado no empareja con ninguno de nuestros patrones de URL (como los listados). El resgistro de depuración puede desconectarse durante la producción (cuando colocamos el sitio en vivo en la Web), en cuyo caso se servirá una página menos informativa pero más amigable.</p>
+</div>
+
+<p>¡En este punto ya sabemos que Django está funcionando!</p>
+
+<div class="note">
+<p><strong>Nota</strong>: Deberías volver a ejecutar las migraciones y volver a probar el sitio cada vez que hagas cambios significativos. ¡No lleva tanto tiempo!</p>
+</div>
+
+<h2 id="Desafíate_a_tí_mismo">Desafíate a tí mismo</h2>
+
+<p>El directorio <strong>catalog/</strong> contiene ficheros para las vistas, modelos y otras partes de la aplicación. Abre estos ficheros e inspecciona el código base. </p>
+
+<p>Como has visto arriba, se ha añadido ya un mapeo de URLs para el sitio de administración en el fichero del proyecto <strong>urls.py</strong>. Navega al área de adminsitración en tu explorador y mira qué sucede (puedes inferir el URL correcto de los mapeos de arriba).</p>
+
+<ul>
+</ul>
+
+<h2 id="Sumario">Sumario</h2>
+
+<p>ahora ya has creado un proyecto de esqueleto completo de sitio web, con el que puedes continuar rellenando con urls, modelos, vistas y plantillas.</p>
+
+<p>Ahora que el esqueleto del <a href="/es/docs/Learn/Server-side/Django/Tutorial_local_library_website">sitio web de la BibliotecaLocal</a> está completo y funcionando, es hora de empezar a escribir el código que hace que este sitio haga lo que se supone que debe hacer. </p>
+
+<h2 id="Ver_también">Ver también</h2>
+
+<ul>
+ <li><a href="https://docs.djangoproject.com/en/2.0/intro/tutorial01/">Escribiendo tu primera aplicación Django - parte 1</a> (Django docs)</li>
+ <li><a href="https://docs.djangoproject.com/en/2.0/ref/applications/#configuring-applications">Aplicaciones</a> (Django Docs). Contiene información sobre cómo configurar aplicaciones.</li>
+</ul>
+
+<p>{{PreviousMenuNext("Learn/Server-side/Django/Tutorial_local_library_website", "Learn/Server-side/Django/Models", "Learn/Server-side/Django")}}</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>
diff --git a/files/es/learn/server-side/django/testing/index.html b/files/es/learn/server-side/django/testing/index.html
new file mode 100644
index 0000000000..54055b2780
--- /dev/null
+++ b/files/es/learn/server-side/django/testing/index.html
@@ -0,0 +1,906 @@
+---
+title: 'Tutorial de Django Parte 10: Probando una aplicación web Django'
+slug: Learn/Server-side/Django/Testing
+translation_of: Learn/Server-side/Django/Testing
+---
+<div>{{LearnSidebar}}</div>
+
+<div>{{PreviousMenuNext("Learn/Server-side/Django/Forms", "Learn/Server-side/Django/Deployment", "Learn/Server-side/Django")}}</div>
+
+<p class="summary">A medida que crecen los sitios web se vuelven más difíciles de probar a mano — no sólo hay más para probar, sino que además, a medida que las interacciones entre los componentes se vuelven más complejas, un pequeño cambio en un área puede suponer muchas pruebas adicionales para verificar su impacto en otras áreas. Una forma de mitigar estos problemas es escribir tests automatizados, que pueden ser ejecutados de manera fácil y fiable cada vez que hagas un cambio. Este tutorial muestra cómo automatizar la unidad de pruebas de tu sitio web usando el framework de pruebas de Django.</p>
+
+<table class="learn-box standard-table">
+ <tbody>
+ <tr>
+ <th scope="row">Prerequisites:</th>
+ <td>Completa todos los tópicos anteriores, incluyendo <a href="/en-US/docs/Learn/Server-side/Django/Forms">Tutorial Django Parte 9: Trabajando con formularios</a>.</td>
+ </tr>
+ <tr>
+ <th scope="row">Objective:</th>
+ <td>Entender como escribir pruebas unidatarias para django basado en Páginas web.</td>
+ </tr>
+ </tbody>
+</table>
+
+<h2 id="Vista_previa">Vista previa</h2>
+
+<p>El <a href="https://developer.mozilla.org/en-US/docs/Learn/Server-side/Django/Tutorial_local_library_website">Local Library</a> actualmente tiene páginas para mostrar las listas con todos los libros y autores, vistas detalladas para los items de <code>Book</code> y <code>Author,</code><span class="tlid-translation translation" lang="es"><span title="">una página para renovar BookInstances y páginas para crear, actualizar y eliminar elementos de autor (y también registros de libros, si  usted completó el desafío en el tutorial de formularios).</span> <span title="">Incluso con este sitio relativamente pequeño, navegar manualmente a cada página y verificar superficialmente que todo funcione como se espera, puede llevar varios minutos.</span> <span title="">A medida que hagamos cambios y el sitio vaya creciendo, el tiempo requerido para verificar manualmente que todo funcione "correctamente", aumentará de forma muy perniciosa.</span> <span title="">Si continuamos como estamos, pasaríamos la mayor parte de nuestro tiempo probando, y muy poco tiempo mejorando nuestro código.</span></span></p>
+
+<p>¡Las pruebas automatizadas realmente pueden ayudar con este problema! Los beneficios obvios son que pueden  ejecutarse mucho más rápido que las pruebas manuales, pueden probar con un nivel de detalle mucho más bajo y probar exactamente la misma funcionalidad cada vez (¡los testers humanos no son tan confiables!) Porque son pruebas rápidas y automatizadas se puede ejecutar más regularmente, y si falla una prueba,<br>
+ señalan exactamente dónde el código no está funcionando como se esperaba.</p>
+
+<p><span class="tlid-translation translation" lang="es"><span title="">Además, las pruebas automatizadas pueden actuar como el primer "usuario" del mundo real de su código, lo que le obliga a ser riguroso a la hora de definir y documentar bien, cómo debe comportarse su sitio web.</span> <span title="">A menudo son la base de sus ejemplos de código y documentación.</span> <span title="">Por estas razones, algunos procesos de desarrollo de software comienzan con la definición e implementación de la prueba, después de lo cual el código se escribe para que coincida con el comportamiento requerido (por ejemplo, desarrollo basado en pruebas y en comportamiento).</span><br>
+ <br>
+ <span title="">Este tutorial muestra cómo escribir pruebas automatizadas para Django, agregando una serie de pruebas al sitio web LocalLibrary.</span></span></p>
+
+<h3 id="Tipos_de_pruebas">Tipos de pruebas</h3>
+
+<p>Hay numeroso tipos, niveles y clasificaciones de pruebas y enfoques de pruebas. Las pruebas automáticas más importantes son:</p>
+
+<dl>
+ <dt>Pruebas unitarias</dt>
+ <dd>Verifica el comportamiento funcional de un componente individual, a menudo de una clase y su nivel de funcional.</dd>
+ <dt>Pruebas de regresión</dt>
+ <dd>Pruebas que reproducen errores históricos. Cada prueba es inicialmente ejecutada para verificar que el error ha sido corregido, y estos son ejecutados de nuevo para asegurarnos que los errores no fueron reintroducidos con los futuros cambios en el código.</dd>
+ <dt>Pruebas de integración</dt>
+ <dd>Verifica cómo funcionan los grupos de componentes cuando se usan juntos. Las pruebas de integración son conscientes de las interacciones requeridas entre componentes, pero no necesariamente de las operaciones internas de cada componente. Pueden cubrir agrupaciones simples de componentes hasta todo el sitio web.</dd>
+</dl>
+
+<div class="note">
+<p><strong>Nota: </strong>Otros tipos comunes de pruebas incluyen pruebas de caja negra, caja blanca, manuales, automatizadas, canarias, de humo, de conformidad, de aceptación, funcionales, de rendimiento, de carga y de esfuerzo. Búscalos para más información.</p>
+</div>
+
+<h3 id="Que_provee_Django_para_pruebas">Que provee Django para pruebas?</h3>
+
+<p>Probar un sitio web es una tarea compleja, porque está compuesto por varias capas de lógica, desde el manejo de solicitudes a nivel HTTP, modelos de consultas, hasta la validación y procesamiento de formularios y la representación de plantillas.</p>
+
+<p>Django proporciona un marco de prueba con una pequeña jerarquía de clases que se basan en la libreria <code><a href="https://docs.python.org/3/library/unittest.html#module-unittest" title="(in Python v3.5)">unittest</a></code> estándar Python. A pesar del nombre, este marco de prueba es adecuado tanto para pruebas unitarias como de integración. El marco de Django agrega métodos y herramientas API para ayudar a probar el comportamiento web y específico de Django. Estos le permiten simular solicitudes, insertar datos de prueba e inspeccionar la salida de su aplicación. Django también proporciona una API(<a href="https://docs.djangoproject.com/en/1.10/topics/testing/tools/#liveservertestcase">LiveServerTestCase</a>) y herramientas para  <a href="https://docs.djangoproject.com/en/1.10/topics/testing/advanced/#other-testing-frameworks">usar diferentes frameworks de pruebas</a> , por ejemplo, puede integrarse con el popular framework <a href="/en-US/docs/Learn/Tools_and_testing/Cross_browser_testing/Your_own_automation_environment">Selenium</a> para simular la interacción de un usuario con un navegador en vivo.</p>
+
+<p>Para escribir una prueba, se deriva de cualquiera de las clases base de prueba de Django (o unittest)(<a href="https://docs.djangoproject.com/en/1.10/topics/testing/tools/#simpletestcase">SimpleTestCase</a>, <a href="https://docs.djangoproject.com/en/1.10/topics/testing/tools/#transactiontestcase">TransactionTestCase</a>, <a href="https://docs.djangoproject.com/en/1.10/topics/testing/tools/#testcase">TestCase</a>, <a href="https://docs.djangoproject.com/en/1.10/topics/testing/tools/#liveservertestcase">LiveServerTestCase</a>) y luego escribir métodos separados para verificar que la funcionalidad específica funcione como se esperaba (las pruebas usan métodos "assert"  para probar que las expresiones dan valores <code>True</code> o <code>False</code>, o que dos valores son iguales, etc.) Cuando inicia una ejecución de prueba, el marco ejecuta los métodos de prueba elegidos en sus clases derivadas. Los métodos de prueba se ejecutan de forma independiente, con un comportamiento común de configuración y / o desmontaje definido en la clase, como se muestra a continuación.</p>
+
+<pre class="brush: python notranslate">class YourTestClass(TestCase):
+
+    def setUp(self):
+  #Setup run before every test method.
+        pass
+
+    def tearDown(self):
+  #Clean up run after every test method.
+        pass
+
+    def test_something_that_will_pass(self):
+        self.assertFalse(False)
+
+    def test_something_that_will_fail(self):
+        self.assertTrue(False)
+</pre>
+
+<p>La mejor clase base para la mayoría de las pruebas es <a href="https://docs.djangoproject.com/en/1.10/topics/testing/tools/#testcase">django.test.TestCase</a>.  Esta clase de prueba crea una base de datos limpia antes de que se ejecuten sus pruebas y ejecuta cada función de prueba en su propia transacción. La clase también posee una prueba <a href="https://docs.djangoproject.com/en/1.10/topics/testing/tools/#django.test.Client" title="django.test.Client">Client</a> que puede utilizar para simular la interacción de un usuario con el código en el nivel de vista. En las siguientes secciones, nos concentraremos en las pruebas unitarias, creadas con esta clase <a href="https://docs.djangoproject.com/en/1.10/topics/testing/tools/#testcase">TestCase</a></p>
+
+<div class="note">
+<p><strong>Nota:</strong> La clase <a href="https://docs.djangoproject.com/en/1.10/topics/testing/tools/#testcase">django.test.TestCase</a> es muy conveniente, pero puede resultar en que algunas pruebas sean más lentas de lo necesario (no todas las pruebas necesitarán configurar su propia base de datos o simular la interacción de la vista). Una vez que esté familiarizado con lo que puede hacer con esta clase, es posible que desee reemplazar algunas de sus pruebas con las clases de prueba más simples disponibles.</p>
+</div>
+
+<h3 id="Que_deberias_probar">Que deberias probar?</h3>
+
+<p>Debe probar todos los aspectos de su propio código, pero no ninguna biblioteca o funcionalidad proporcionada como parte de Python o Django.</p>
+
+<p>Por ejemplo, considere el modelo <code>Author</code> definido abajo. No es necesario probarlo explícitamente <code>first_name</code> y <code>last_name</code> han sido almacenados correctamente como <code>CharField</code> en la base de datos porque eso es algo definido por Django (aunque, por supuesto, en la práctica, inevitablemente probará esta funcionalidad durante el desarrollo). Tampoco es necesario probar que el <code>date_of_birth</code> ha sido validado para ser un campo de fecha, porque nuevamente es algo implementado en Django.</p>
+
+<p>Sin embargo, debe verificar el texto utilizado para las etiquetas (nombre, apellido, fecha de nacimiento, fallecimiento) y el tamaño del campo asignado para el texto (100 caracteres), porque estos son parte de su diseño y algo que podría ser roto / cambiado en el futuro.</p>
+
+<pre class="brush: python notranslate">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)</pre>
+
+<p>Del mismo modo, debe verificar que los métodos personalizados <code style="font-style: normal; font-weight: normal;">get_absolute_url()</code> y  <code style="font-style: normal; font-weight: normal;">__str__()</code>comportarse como sea necesario porque son su código / lógica empresarial. En el caso de <code style="font-style: normal; font-weight: normal;">get_absolute_url()</code> puedes confiar en que el metodo de Django <code>reverse()</code> se ha implementado correctamente, por lo que lo que está probando es que la vista asociada se haya definido realmente.</p>
+
+<div class="note">
+<p><strong>Nota:</strong> Los lectores astutos pueden notar que también querríamos restringir la fecha de nacimiento y muerte a valores sensibles, y comprobar que la muerte viene después del nacimiento. En Django, esta restricción se agregaría a sus clases de formulario (aunque puede definir validadores para los campos, estos parecen usarse solo en el nivel del formulario, no en el nivel del modelo).</p>
+</div>
+
+<p>Con eso en mente, comencemos a ver cómo definir y ejecutar pruebas.</p>
+
+<h2 id="Descripción_general_de_la_estructura_de_prueba">Descripción general de la estructura de prueba</h2>
+
+<p>Antes de entrar en los detalles de "qué probar", primero veamos brevemente dónde y cómo se definen las pruebas..</p>
+
+<p>Django utiliza el descubrimiento de pruebas integrado del módulo unittest (<a href="https://docs.python.org/3/library/unittest.html#unittest-test-discovery" title="(in Python v3.5)">built-in test discovery)</a>, que descubrirá pruebas en el directorio de trabajo actual en cualquier archivo nombrado con el patrón <strong>test*.py</strong>. Siempre que asigne un nombre a los archivos de forma adecuada, puede utilizar la estructura que desee. Le recomendamos que cree un módulo para su código de prueba y que tenga archivos separados para modelos, vistas, formularios y cualquier otro tipo de código que necesite probar. Por ejemplo:</p>
+
+<pre class="notranslate">catalog/
+  /tests/
+  __init__.py
+  test_models.py
+  test_forms.py
+  test_views.py
+</pre>
+
+<p>Cree una estructura de archivo como se muestra arriba en su proyecto  <em>LocalLibrary</em>. El  <strong>__init__.py</strong> debe ser un archivo vacío (esto le dice a Python que el directorio es un paquete). Puede crear los tres archivos de prueba copiando y cambiando el nombre del archivo de prueba de esqueleto <strong>/catalog/tests.py</strong>.</p>
+
+<div class="note">
+<p><strong>Note:</strong>El archivo de prueba <strong>/catalog/tests.py</strong>se creó automáticamente cuando creamos el sitio web esqueleto de Django ( <a href="/en-US/docs/Learn/Server-side/Django/skeleton_website">built the Django skeleton website)</a>.</p>
+
+<p>Es perfectamente "legal" poner todas sus pruebas dentro de él, pero si prueba correctamente, rápidamente terminará con un archivo de prueba muy grande e inmanejable.</p>
+
+<p>Elimina el archivo esqueleto ya que no lo necesitaremos.</p>
+</div>
+
+<p>Abre el archivo <strong>/catalog/tests/test_models.py</strong>. El archivo debe importar <code>django.test.TestCase</code>, como se muestra:</p>
+
+<pre class="brush: python notranslate">from django.test import TestCase
+
+# Create your tests here.
+</pre>
+
+<p>A menudo, agregará una clase de prueba para cada modelo / vista / formulario que desee probar, con métodos individuales para probar una funcionalidad específica. En otros casos, es posible que desee tener una clase separada para probar un caso de uso específico, con funciones de prueba individuales que prueben aspectos de ese caso de uso (por ejemplo, una clase para probar que un campo de modelo está validado correctamente, con funciones para probar cada uno de los posibles casos de falla). Una vez más, la estructura depende en gran medida de usted, pero es mejor si es coherente.</p>
+
+<p>Agregue la clase de prueba a continuación al final del archivo. La clase demuestra cómo construir una clase de caso de prueba derivando de <code>TestCase</code>.</p>
+
+<pre class="brush: python notranslate">class YourTestClass(TestCase):
+
+    @classmethod
+    def setUpTestData(cls):
+        print("setUpTestData: Run once to set up non-modified data for all class methods.")
+        pass
+
+    def setUp(self):
+        print("setUp: Run once for every test method to setup clean data.")
+        pass
+
+    def test_false_is_false(self):
+        print("Method: test_false_is_false.")
+        self.assertFalse(False)
+
+    def test_false_is_true(self):
+        print("Method: test_false_is_true.")
+        self.assertTrue(False)
+
+    def test_one_plus_one_equals_two(self):
+        print("Method: test_one_plus_one_equals_two.")
+        self.assertEqual(1 + 1, 2)</pre>
+
+<p>La nueva clase define dos métodos que puede utilizar para la configuración previa a la prueba (por ejemplo, para crear modelos u otros objetos que necesitará para la prueba):</p>
+
+<ul>
+ <li><code>setUpTestData()</code> se llama una vez al comienzo de la ejecución de prueba para la configuración a nivel de clase. Usaría esto para crear objetos que no se modificarán ni cambiarán en ninguno de los métodos de prueba.</li>
+ <li><code>setUp()</code> se llama antes de cada función de prueba para configurar cualquier objeto que pueda ser modificado por la prueba (cada función de prueba obtendrá una versión "nueva" de estos objetos).</li>
+</ul>
+
+<div class="note">
+<p>Las clases de prueba también tienen un metodo <code>tearDown()</code> que no hemos utilizado. Este método no es particularmente útil para las pruebas de bases de datos, ya que <code>TestCase</code> la clase base se encarga del desmontaje de la base de datos por usted.</p>
+</div>
+
+<p>Debajo de ellos tenemos una serie de métodos de prueba, que utilizamos funciones <code>Assert</code> toprobar si las condiciones son verdaderas, falsas o iguales (<code>AssertTrue</code>, <code>AssertFalse</code>, <code>AssertEqual</code>). Si la condición no se evalúa como se esperaba, la prueba fallará y reportará el error a su consola.</p>
+
+<p>Los <code>AssertTrue</code>, <code>AssertFalse</code>, <code>AssertEqual</code> son afirmaciones estándar proporcionadas por <strong>unittest</strong>.  Hay otras aserciones estándar en el marco y también aserciones específicas de Django (<a href="https://docs.djangoproject.com/en/1.10/topics/testing/tools/#assertions">Django-specific assertions</a>) para probar si una vista redirecciona (<code>assertRedirects</code>),para probar si se ha utilizado una plantilla en particular (<code>assertTemplateUsed</code>), etc.</p>
+
+<div class="note">
+<p>Normalmente no debería incluir funciones print () en sus pruebas como se muestra arriba. Lo hacemos aquí solo para que pueda ver el orden en que se llaman las funciones de configuración en la consola (en la siguiente sección).</p>
+</div>
+
+<h2 id="Como_correr_las_pruebas">Como correr las pruebas</h2>
+
+<p>La forma más sencilla de ejecutar todas las pruebas es utilizar el comando:</p>
+
+<pre class="brush: bash notranslate">python3 manage.py test</pre>
+
+<p>Esto descubrirá todos los archivos nombrados con el patrón <strong>test*.py</strong> bajo el directorio actual y ejecute todas las pruebas definidas usando las clases base apropiadas (aquí tenemos una serie de archivos de prueba, pero solo <strong>/catalog/tests/test_models.py</strong> contiene actualmente cualquier prueba). De forma predeterminada, las pruebas informarán individualmente solo sobre las fallas de las pruebas, seguidas de un resumen de la prueba.</p>
+
+<div class="note">
+<p>Si recibe errores similares a: <code>ValueError: Missing staticfiles manifest entry ...</code> esto puede deberse a que las pruebas no ejecutan collectstatic de forma predeterminada y su aplicación usa una clase de almacenamiento que lo requiere (consulte manifest_strict para obtener más información). Hay varias formas de superar este problema; la más fácil es simplemente ejecutar collectstatic antes de ejecutar las pruebas:</p>
+
+<pre class="brush: bash notranslate">python3 manage.py collectstatic
+</pre>
+</div>
+
+<p>Ejecute las pruebas en el directorio raíz de LocalLibrary. Debería ver un resultado como el siguiente.</p>
+
+<pre class="brush: bash notranslate">&gt;python manage.py test
+
+Creating test database for alias 'default'...
+<strong>setUpTestData: Run once to set up non-modified data for all class methods.
+setUp: Run once for every test method to setup clean data.
+Method: test_false_is_false.
+.setUp: Run once for every test method to setup clean data.
+Method: test_false_is_true.
+FsetUp: Run once for every test method to setup clean data.
+Method: test_one_plus_one_equals_two.</strong>
+.
+======================================================================
+FAIL: test_false_is_true (catalog.tests.tests_models.YourTestClass)
+----------------------------------------------------------------------
+Traceback (most recent call last):
+  File "D:\Github\django_tmp\library_w_t_2\locallibrary\catalog\tests\tests_models.py", line 22, in test_false_is_true
+    self.assertTrue(False)
+AssertionError: False is not true
+
+----------------------------------------------------------------------
+Ran 3 tests in 0.075s
+
+FAILED (failures=1)
+Destroying test database for alias 'default'...</pre>
+
+<p>Aquí vemos que tuvimos una falla de prueba, y podemos ver exactamente qué función falló y por qué (se espera esta falla, porque <code>False</code> no es <code>True</code>!).</p>
+
+<div class="note">
+<p>Sugerencia: Lo más importante que debe aprender del resultado de la prueba anterior es que es mucho más valioso si usa nombres descriptivos / informativos para sus objetos y métodos.</p>
+</div>
+
+<p>El texto que se muestra en <strong>negritas</strong> anterior normalmente no aparecería en la salida de prueba (esto es generado por la funcion <code>print()</code> en nuestra prueba). Esto muestra el metodo <code>setUpTestData()</code> es llamado una vez para la clase y <code>setUp()</code>se llama antes de cada método.</p>
+
+<p>Las siguientes secciones muestran cómo puede ejecutar pruebas específicas y cómo controlar cuánta información muestran las pruebas.</p>
+
+<h3 id="Mostrando_más_información_de_las_pruebas">Mostrando más información de las pruebas</h3>
+
+<p>Si desea obtener más información sobre la ejecución de prueba, puede cambiar el nivel de detalle. Por ejemplo, para enumerar los éxitos y fallas de la prueba (y una gran cantidad de información sobre cómo está configurada la base de datos de prueba), puede establecer la verbosidad en "2" como se muestra:</p>
+
+<pre class="brush: bash notranslate">python3 manage.py test --verbosity 2</pre>
+
+<p>The allowed verbosity levels are 0, 1, 2, and 3, with the default being "1".</p>
+
+<h3 id="Ejecutando_pruebas_especificas">Ejecutando pruebas especificas</h3>
+
+<p>Si desea ejecutar un subconjunto de sus pruebas, puede hacerlo especificando la ruta de puntos completa al paquete (s), módulo, <code>TestCase</code> subclase o metodo:</p>
+
+<pre class="brush: bash notranslate">python3 manage.py test catalog.tests # Run the specified module
+python3 manage.py test catalog.tests.test_models # Run the specified module
+python3 manage.py test catalog.tests.test_models.YourTestClass # Run the specified class
+python3 manage.py test catalog.tests.test_models.YourTestClass.test_one_plus_one_equals_two # Run the specified method
+</pre>
+
+<h2 id="Pruebas_en_el_proyecto_LocalLibrary">Pruebas en el proyecto LocalLibrary</h2>
+
+<p>Ahora que sabemos cómo ejecutar nuestras pruebas y qué tipo de cosas necesitamos probar, veamos algunos ejemplos prácticos.</p>
+
+<div class="note">
+<p>Nota: No escribiremos todas las pruebas posibles, pero esto debería darle una idea de cómo funcionan las pruebas y qué más puede hacer.</p>
+</div>
+
+<h3 id="Modelos">Modelos</h3>
+
+<p>Como se discutió anteriormente, debemos probar todo lo que sea parte de nuestro diseño o que esté definido por el código que hayamos escrito, pero no las bibliotecas / código que ya haya probado Django o el equipo de desarrollo de Python.</p>
+
+<p>Por ejemplo, considere el modelo de <code>Author</code> a continuación. Aquí deberíamos probar las etiquetas para todos los campos, porque aunque no hemos especificado explícitamente la mayoría de ellos, tenemos un diseño que dice cuáles deberían ser estos valores. Si no probamos los valores, entonces no sabemos que las etiquetas de los campos tienen sus valores deseados. De manera similar, aunque confiamos en que Django creará un campo de la longitud especificada, vale la pena especificar una prueba para esta longitud para asegurarse de que se implementó según lo planeado.</p>
+
+<pre class="brush: python notranslate">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)</pre>
+
+<p>Abra su <strong>/catalog/tests/test_models.py</strong>, y reemplace cualquier código existente con el siguiente código de prueba para el modelo de <code>Author</code>.</p>
+
+<p>Aquí usted verá que primero importamos <code>TestCase</code> y derivamos nuestras clases de prueba (<code>AuthorModelTest</code>) de ello, usando un nombre descriptive para que así podamos fácilmente cualquier pruebas fallidas en el output de la prueba. Luego llamamos a <code>setUpTestData()</code> para crear un objeto de autor que usaremos pero no modificaremos en ninguna de las pruebas.</p>
+
+<pre class="brush: python notranslate">from django.test import TestCase
+
+# Create your tests here.
+
+from catalog.models import Author
+
+class AuthorModelTest(TestCase):
+
+ @classmethod
+ def setUpTestData(cls):
+ #Set up non-modified objects used by all test methods
+ Author.objects.create(first_name='Big', last_name='Bob')
+
+ def test_first_name_label(self):
+ author=Author.objects.get(id=1)
+ field_label = author._meta.get_field('first_name').verbose_name
+ self.assertEquals(field_label,'first name')
+
+ def test_date_of_death_label(self):
+ author=Author.objects.get(id=1)
+ field_label = author._meta.get_field('date_of_death').verbose_name
+ self.assertEquals(field_label,'died')
+
+ def test_first_name_max_length(self):
+ author=Author.objects.get(id=1)
+ max_length = author._meta.get_field('first_name').max_length
+ self.assertEquals(max_length,100)
+
+ def test_object_name_is_last_name_comma_first_name(self):
+ author=Author.objects.get(id=1)
+ expected_object_name = '%s, %s' % (author.last_name, author.first_name)
+ self.assertEquals(expected_object_name,str(author))
+
+ def test_get_absolute_url(self):
+ author=Author.objects.get(id=1)
+ #This will also fail if the urlconf is not defined.
+ self.assertEquals(author.get_absolute_url(),'/catalog/author/1')</pre>
+
+<p>The field tests check that the values of the field labels (<code>verbose_name</code>) and that the size of the character fields are as expected. These methods all have descriptive names, and follow the same pattern:</p>
+
+<pre class="brush: python notranslate">author=Author.objects.get(id=1) # Get an author object to test
+field_label = author._meta.get_field('first_name').verbose_name # Get the metadata for the required field and use it to query the required field data
+self.assertEquals(field_label,'first name') # Compare the value to the expected result</pre>
+
+<p>The interesting things to note are:</p>
+
+<ul>
+ <li>We can't get the <code>verbose_name</code> directly using <code>author.first_name.verbose_name</code>, because <code>author.first_name</code> is a <em>string</em> (not a handle to the <code>first_name</code> object that we can use to access its properties). Instead we need to use the author's <code>_meta</code> attribute to get an instance of the field and use that to query for the additional information.</li>
+ <li>We chose to use <code>assertEquals(field_label,'first name')</code> rather than <code>assertTrue(field_label == 'first name')</code>. The reason for this is that if the test fails the output for the former tells you what the label actually was, which makes debugging the problem just a little easier.</li>
+</ul>
+
+<div class="note">
+<p><strong>Note:</strong> Tests for the <code>last_name</code> and <code>date_of_birth</code> labels, and also the test for the length of the <code>last_name</code> field have been omitted. Add your own versions now, following the naming conventions and approaches shown above.</p>
+</div>
+
+<p>We also need to test our custom methods. These essentially just check that the object name was constructed as we expected using "Last Name", "First Name" format, and that the URL we get for an <code>Author</code> item is as we would expect.</p>
+
+<pre class="brush: python notranslate">def test_object_name_is_last_name_comma_first_name(self):
+    author=Author.objects.get(id=1)
+    expected_object_name = '%s, %s' % (author.last_name, author.first_name)
+    self.assertEquals(expected_object_name,str(author))
+
+def test_get_absolute_url(self):
+    author=Author.objects.get(id=1)
+    #This will also fail if the urlconf is not defined.
+    self.assertEquals(author.get_absolute_url(),'/catalog/author/1')</pre>
+
+<p>Run the tests now. If you created the Author model as we described in the models tutorial it is quite likely that you will get an error for the <code>date_of_death</code> label as shown below. The test is failing because it was written expecting the label definition to follow Django's convention of not capitalising the first letter of the label (Django does this for you).</p>
+
+<pre class="brush: bash notranslate">======================================================================
+FAIL: test_date_of_death_label (catalog.tests.test_models.AuthorModelTest)
+----------------------------------------------------------------------
+Traceback (most recent call last):
+ File "D:\...\locallibrary\catalog\tests\test_models.py", line 32, in test_date_of_death_label
+ self.assertEquals(field_label,'died')
+AssertionError: 'Died' != 'died'
+- Died
+? ^
++ died
+? ^</pre>
+
+<p>This is a very minor bug, but it does highlight how writing tests can more thoroughly check any assumptions you may have made.</p>
+
+<div class="note">
+<p><strong>Note: </strong>Change the label for the date_of_death field (/catalog/models.py) to "died" and re-run the tests.</p>
+</div>
+
+<p>The patterns for testing the other models are similar so we won't continue to discuss these further. Feel free to create your own tests for the our other models.</p>
+
+<h3 id="Formularios">Formularios</h3>
+
+<p>The philosophy for testing your forms is the same as for testing your models; you need to test anything that you've coded or your design specifies, but not the behaviour of the underlying framework and other third party libraries.</p>
+
+<p>Generally this means that you should test that the forms have the fields that you want, and that these are displayed with appropriate labels and help text. You don't need to verify that Django validates the field type correctly (unless you created your own custom field and validation) — i.e. you don't need to test that an email field only accepts emails. However you would need to test any additional validation that you expect to be performed on the fields and any messages that your code will generate for errors.</p>
+
+<p>Consider our form for renewing books. This has just one field for the renewal date, which will have a label and help text that we will need to verify.</p>
+
+<pre class="brush: python notranslate">class RenewBookForm(forms.Form):
+ """
+ Form for a librarian to renew books.
+ """
+ renewal_date = forms.DateField(help_text="Enter a date between now and 4 weeks (default 3).")
+
+ def clean_renewal_date(self):
+ data = self.cleaned_data['renewal_date']
+
+ #Check date is not in past.
+ if data &lt; datetime.date.today():
+ raise ValidationError(_('Invalid date - renewal in past'))
+ #Check date is in range librarian allowed to change (+4 weeks)
+ if data &gt; datetime.date.today() + datetime.timedelta(weeks=4):
+ raise ValidationError(_('Invalid date - renewal more than 4 weeks ahead'))
+
+ # Remember to always return the cleaned data.
+ return data</pre>
+
+<p>Open our <strong>/catalog/tests/test_forms.py</strong> file and replace any existing code with the following test code for the <code>RenewBookForm</code> form. We start by importing our form and some Python and Django libraries to help test test time-related functionality. We then declare our form test class in the same way as we did for models, using a descriptive name for our <code>TestCase</code>-derived test class.</p>
+
+<pre class="brush: python notranslate">from django.test import TestCase
+
+# Create your tests here.
+
+import datetime
+from django.utils import timezone
+from catalog.forms import RenewBookForm
+
+class RenewBookFormTest(TestCase):
+
+    def test_renew_form_date_field_label(self):
+        form = RenewBookForm()
+        self.assertTrue(form.fields['renewal_date'].label == None or form.fields['renewal_date'].label == 'renewal date')
+
+    def test_renew_form_date_field_help_text(self):
+        form = RenewBookForm()
+        self.assertEqual(form.fields['renewal_date'].help_text,'Enter a date between now and 4 weeks (default 3).')
+
+    def test_renew_form_date_in_past(self):
+        date = datetime.date.today() - datetime.timedelta(days=1)
+        form_data = {'renewal_date': date}
+        form = RenewBookForm(data=form_data)
+        self.assertFalse(form.is_valid())
+
+    def test_renew_form_date_too_far_in_future(self):
+        date = datetime.date.today() + datetime.timedelta(weeks=4) + datetime.timedelta(days=1)
+        form_data = {'renewal_date': date}
+        form = RenewBookForm(data=form_data)
+        self.assertFalse(form.is_valid())
+
+    def test_renew_form_date_today(self):
+        date = datetime.date.today()
+        form_data = {'renewal_date': date}
+        form = RenewBookForm(data=form_data)
+        self.assertTrue(form.is_valid())
+
+    def test_renew_form_date_max(self):
+        date = timezone.now() + datetime.timedelta(weeks=4)
+        form_data = {'renewal_date': date}
+        form = RenewBookForm(data=form_data)
+        self.assertTrue(form.is_valid())
+</pre>
+
+<p>The first two functions test that the field's <code>label</code> and <code>help_text</code> are as expected. We have to access the field using the fields dictionary (e.g. <code>form.fields['renewal_date']</code>). Note here that we also have to test whether the label value is <code>None</code>, because even though Django will render the correct label it returns <code>None</code> if the value is not <em>explicitly</em> set.</p>
+
+<p>The rest of the functions test that the form is valid for renewal dates just inside the acceptable range and invalid for values outside the range. Note how we construct test date values around our current date (<code>datetime.date.today()</code>) using <code>datetime.timedelta()</code> (in this case specifying a number of days or weeks). We then just create the form, passing in our data, and test if it is valid.</p>
+
+<div class="note">
+<p><strong>Note:</strong> Here we don't actually use the database or test client. Consider modifying these tests to use <a href="https://docs.djangoproject.com/en/1.10/topics/testing/tools/#django.test.SimpleTestCase">SimpleTestCase</a>.</p>
+
+<p>We also need to validate that the correct errors are raised if the form is invalid, however this is usually done as part of view processing, so we'll take care of that in the next section.</p>
+</div>
+
+<p>That's all for forms; we do have some others, but they are automatically created by our generic class-based editing views, and should be tested there! Run the tests and confirm that our code still passes!</p>
+
+<h3 id="Vistas">Vistas</h3>
+
+<p>To validate our view behaviour we use the Django test <a href="https://docs.djangoproject.com/en/1.10/topics/testing/tools/#django.test.Client">Client</a>. This class acts like a dummy web browser that we can use to simulate <code>GET</code> and <code>POST</code> requests on a URL and observe the response. We can see almost everything about the response, from low-level HTTP (result headers and status codes) through to the template we're using to render the HTML and the context data we're passing to it. We can also see the chain of redirects (if any) and check the URL and status code at each step. This allows us to verify that each view is doing what is expected.</p>
+
+<p>Let's start with one of our simplest views, which provides a list of all Authors. This is displayed at URL <strong>/catalog/authors/</strong> (an URL named 'authors' in the URL configuration).</p>
+
+<pre class="brush: python notranslate">class AuthorListView(generic.ListView):
+ model = Author
+ paginate_by = 10
+</pre>
+
+<p>As this is a generic list view almost everything is done for us by Django. Arguably if you trust Django then the only thing you need to test is that the view is accessible at the correct URL and can be accessed using its name. However if you're using a test-driven development process you'll start by writing tests that confirm that the view displays all Authors, paginating them in lots of 10.</p>
+
+<p>Open the <strong>/catalog/tests/test_views.py</strong> file and replace any existing text with the following test code for <code>AuthorListView</code>. As before we import our model and some useful classes. In the <code>setUpTestData()</code> method we set up a number of <code>Author</code> objects so that we can test our pagination.</p>
+
+<pre class="brush: python notranslate">from django.test import TestCase
+
+# Create your tests here.
+
+from catalog.models import Author
+from django.urls import reverse
+
+class AuthorListViewTest(TestCase):
+
+    @classmethod
+    def setUpTestData(cls):
+        #Create 13 authors for pagination tests
+        number_of_authors = 13
+        for author_num in range(number_of_authors):
+            Author.objects.create(first_name='Christian %s' % author_num, last_name = 'Surname %s' % author_num,)
+
+    def test_view_url_exists_at_desired_location(self):
+        resp = self.client.get('/catalog/authors/')
+        self.assertEqual(resp.status_code, 200)
+
+    def test_view_url_accessible_by_name(self):
+        resp = self.client.get(reverse('authors'))
+        self.assertEqual(resp.status_code, 200)
+
+    def test_view_uses_correct_template(self):
+        resp = self.client.get(reverse('authors'))
+        self.assertEqual(resp.status_code, 200)
+
+        self.assertTemplateUsed(resp, 'catalog/author_list.html')
+
+    def test_pagination_is_ten(self):
+        resp = self.client.get(reverse('authors'))
+        self.assertEqual(resp.status_code, 200)
+        self.assertTrue('is_paginated' in resp.context)
+        self.assertTrue(resp.context['is_paginated'] == True)
+        self.assertTrue( len(resp.context['author_list']) == 10)
+
+    def test_lists_all_authors(self):
+        #Get second page and confirm it has (exactly) remaining 3 items
+        resp = self.client.get(reverse('authors')+'?page=2')
+        self.assertEqual(resp.status_code, 200)
+        self.assertTrue('is_paginated' in resp.context)
+        self.assertTrue(resp.context['is_paginated'] == True)
+        self.assertTrue( len(resp.context['author_list']) == 3)</pre>
+
+<p>All the tests use the client (belonging to our <code>TestCase</code>'s derived class) to simulate a <code>GET</code> request and get a response (<code>resp</code>). The first version checks a specific URL (note, just the specific path without the domain) while the second generates the URL from its name in the URL configuration.</p>
+
+<pre class="brush: python notranslate">resp = self.client.get('/catalog/authors/')
+resp = self.client.get(reverse('authors'))
+</pre>
+
+<p>Once we have the response we query it for its status code, the template used, whether or not the response is paginated, the number of items returned, and the total number of items.</p>
+
+<p>The most interesting variable we demonstrate above is <code>resp.context</code>, which is the context variable passed to the template by the view. This is incredibly useful for testing, because it allows us to confirm that our template is getting all the data it needs. In other words we can check that we're using the intended template and what data the template is getting, which goes a long way to verifying that any rendering issues are solely due to template.</p>
+
+<h4 id="Views_that_are_restricted_to_logged_in_users">Views that are restricted to logged in users</h4>
+
+<p>In some cases you'll want to test a view that is restricted to just logged in users. For example our <code>LoanedBooksByUserListView</code> is very similar to our previous view but is only available to logged in users, and only displays <code>BookInstance</code> records that are borrowed by the current user, have the 'on loan' status, and are ordered "oldest first".</p>
+
+<pre class="brush: python notranslate">from django.contrib.auth.mixins import LoginRequiredMixin
+
+class LoanedBooksByUserListView(LoginRequiredMixin,generic.ListView):
+ """
+ Generic class-based view listing books on loan to current user.
+ """
+ model = BookInstance
+ template_name ='catalog/bookinstance_list_borrowed_user.html'
+ paginate_by = 10
+
+ def get_queryset(self):
+ return BookInstance.objects.filter(borrower=self.request.user).filter(status__exact='o').order_by('due_back')</pre>
+
+<p>Add the following test code to <strong>/catalog/tests/test_views.py</strong>. Here we first use <code>SetUp()</code> to create some user login accounts and <code>BookInstance</code> objects (along with their associated books and other records) that we'll use later in the tests. Half of the books are borrowed by each test user, but we've initially set the status of all books to "maintenance". We've used <code>SetUp()</code> rather than <code>setUpTestData()</code> because we'll be modifying some of these objects later.</p>
+
+<div class="note">
+<p><strong>Note:</strong> The <code>setUp()</code> code below creates a book with a specified <code>Language</code>, but <em>your</em> code may not include the <code>Language</code> model as this was created as a <em>challenge</em>. If this is the case, simply  comment out the parts of the code that create or import Language objects. You should also do this in the <code>RenewBookInstancesViewTest</code> section that follows.</p>
+</div>
+
+<pre class="brush: python notranslate">import datetime
+from django.utils import timezone
+
+from catalog.models import BookInstance, Book, Genre, Language
+from django.contrib.auth.models import User #Required to assign User as a borrower
+
+class LoanedBookInstancesByUserListViewTest(TestCase):
+
+    def setUp(self):
+        #Create two users
+        test_user1 = User.objects.create_user(username='testuser1', password='12345')
+        test_user1.save()
+        test_user2 = User.objects.create_user(username='testuser2', password='12345')
+        test_user2.save()
+
+        #Create a book
+        test_author = Author.objects.create(first_name='John', last_name='Smith')
+        test_genre = Genre.objects.create(name='Fantasy')
+        test_language = Language.objects.create(name='English')
+        test_book = Book.objects.create(title='Book Title', summary = 'My book summary', isbn='ABCDEFG', author=test_author, language=test_language)
+        # Create genre as a post-step
+        genre_objects_for_book = Genre.objects.all()
+  test_book.genre.set(genre_objects_for_book) #Direct assignment of many-to-many types not allowed.
+        test_book.save()
+
+        #Create 30 BookInstance objects
+        number_of_book_copies = 30
+        for book_copy in range(number_of_book_copies):
+            return_date= timezone.now() + datetime.timedelta(days=book_copy%5)
+            if book_copy % 2:
+                the_borrower=test_user1
+            else:
+                the_borrower=test_user2
+            status='m'
+            BookInstance.objects.create(book=test_book,imprint='Unlikely Imprint, 2016', due_back=return_date, borrower=the_borrower, status=status)
+
+    def test_redirect_if_not_logged_in(self):
+        resp = self.client.get(reverse('my-borrowed'))
+        self.assertRedirects(resp, '/accounts/login/?next=/catalog/mybooks/')
+
+    def test_logged_in_uses_correct_template(self):
+        login = self.client.login(username='testuser1', password='12345')
+        resp = self.client.get(reverse('my-borrowed'))
+
+        #Check our user is logged in
+        self.assertEqual(str(resp.context['user']), 'testuser1')
+        #Check that we got a response "success"
+        self.assertEqual(resp.status_code, 200)
+
+        #Check we used correct template
+        self.assertTemplateUsed(resp, 'catalog/bookinstance_list_borrowed_user.html')
+</pre>
+
+<p>To verify that the view will redirect to a login page if the user is not logged in we use <code>assertRedirects</code>, as demonstrated in <code>test_redirect_if_not_logged_in()</code>. To verify that the page is displayed for a logged in user we first log in our test user, and then access the page again and check that we get a <code>status_code</code> of 200 (success). </p>
+
+<p>The rest of the test verify that our view only returns books that are on loan to our current borrower. Copy the (self-explanatory) code at the end of the test class above.</p>
+
+<pre class="brush: python notranslate">    def test_only_borrowed_books_in_list(self):
+        login = self.client.login(username='testuser1', password='12345')
+        resp = self.client.get(reverse('my-borrowed'))
+
+        #Check our user is logged in
+        self.assertEqual(str(resp.context['user']), 'testuser1')
+        #Check that we got a response "success"
+        self.assertEqual(resp.status_code, 200)
+
+        #Check that initially we don't have any books in list (none on loan)
+        self.assertTrue('bookinstance_list' in resp.context)
+        self.assertEqual( len(resp.context['bookinstance_list']),0)
+
+        #Now change all books to be on loan
+        get_ten_books = BookInstance.objects.all()[:10]
+
+        for copy in get_ten_books:
+            copy.status='o'
+            copy.save()
+
+        #Check that now we have borrowed books in the list
+        resp = self.client.get(reverse('my-borrowed'))
+        #Check our user is logged in
+        self.assertEqual(str(resp.context['user']), 'testuser1')
+        #Check that we got a response "success"
+        self.assertEqual(resp.status_code, 200)
+
+        self.assertTrue('bookinstance_list' in resp.context)
+
+        #Confirm all books belong to testuser1 and are on loan
+        for bookitem in resp.context['bookinstance_list']:
+            self.assertEqual(resp.context['user'], bookitem.borrower)
+            self.assertEqual('o', bookitem.status)
+
+    def test_pages_ordered_by_due_date(self):
+
+        #Change all books to be on loan
+        for copy in BookInstance.objects.all():
+            copy.status='o'
+            copy.save()
+
+        login = self.client.login(username='testuser1', password='12345')
+        resp = self.client.get(reverse('my-borrowed'))
+
+        #Check our user is logged in
+        self.assertEqual(str(resp.context['user']), 'testuser1')
+        #Check that we got a response "success"
+        self.assertEqual(resp.status_code, 200)
+
+        #Confirm that of the items, only 10 are displayed due to pagination.
+        self.assertEqual( len(resp.context['bookinstance_list']),10)
+
+        last_date=0
+        for copy in resp.context['bookinstance_list']:
+            if last_date==0:
+                last_date=copy.due_back
+            else:
+                self.assertTrue(last_date &lt;= copy.due_back)</pre>
+
+<p>You could also add pagination tests, should you so wish!</p>
+
+<h4 id="Testing_views_with_forms">Testing views with forms</h4>
+
+<p>Testing views with forms is a little more complicated than in the cases above, because you need to test more code paths: initial display, display after data validation has failed, and display after validation has succeeded. The good news is that we use the client for testing in almost exactly the same way as we did for display-only views.</p>
+
+<p>To demonstrate, let's write some tests for the view used to renew books (<code>renew_book_librarian()</code>):</p>
+
+<pre class="brush: python notranslate">from .forms import RenewBookForm
+
+@permission_required('catalog.can_mark_returned')
+def renew_book_librarian(request, pk):
+    """
+    View function for renewing a specific BookInstance by librarian
+    """
+    book_inst=get_object_or_404(BookInstance, pk = pk)
+
+    # If this is a POST request then process the Form data
+    if request.method == 'POST':
+
+        # Create a form instance and populate it with data from the request (binding):
+        form = RenewBookForm(request.POST)
+
+        # Check if the form is valid:
+        if form.is_valid():
+            # process the data in form.cleaned_data as required (here we just write it to the model due_back field)
+            book_inst.due_back = form.cleaned_data['renewal_date']
+            book_inst.save()
+
+            # redirect to a new URL:
+            return HttpResponseRedirect(reverse('all-borrowed') )
+
+    # If this is a GET (or any other method) create the default form
+    else:
+        proposed_renewal_date = datetime.date.today() + datetime.timedelta(weeks=3)
+        form = RenewBookForm(initial={'renewal_date': proposed_renewal_date,})
+
+    return render(request, 'catalog/book_renew_librarian.html', {'form': form, 'bookinst':book_inst})</pre>
+
+<p>We'll need to test that the view is only available to users who have the <code>can_mark_returned </code>permission, and that users are redirected to an HTTP 404 error page if they attempt to renew a <code>BookInstance</code> that does not exist. We should check that the initial value of the form is seeded with a date three weeks in the future, and that if validation succeeds we're redirected to the "all-borrowed books" view. As part checking the validation-fail tests we'll also check that our form is sending the appropriate error messages.</p>
+
+<p>Add the first part of the test class (shown below) to the bottom of <strong>/catalog/tests/test_views.py</strong>. This creates two users and two book instances, but only gives one user the permission required to access the view. The code to grant permissions during tests is shown in bold:</p>
+
+<pre class="brush: python notranslate">from django.contrib.auth.models import Permission # Required to grant the permission needed to set a book as returned.
+
+class RenewBookInstancesViewTest(TestCase):
+
+ def setUp(self):
+ #Create a user
+ test_user1 = User.objects.create_user(username='testuser1', password='12345')
+ test_user1.save()
+
+ test_user2 = User.objects.create_user(username='testuser2', password='12345')
+ test_user2.save()
+<strong> permission = Permission.objects.get(name='Set book as returned')
+ test_user2.user_permissions.add(permission)
+ test_user2.save()</strong>
+
+ #Create a book
+ test_author = Author.objects.create(first_name='John', last_name='Smith')
+ test_genre = Genre.objects.create(name='Fantasy')
+ test_language = Language.objects.create(name='English')
+ test_book = Book.objects.create(title='Book Title', summary = 'My book summary', isbn='ABCDEFG', author=test_author, language=test_language,)
+ # Create genre as a post-step
+ genre_objects_for_book = Genre.objects.all()
+ test_book.genre.set(genre_objects_for_book) # Direct assignment of many-to-many types not allowed.
+ test_book.save()
+
+ #Create a BookInstance object for test_user1
+ return_date= datetime.date.today() + datetime.timedelta(days=5)
+ self.test_bookinstance1=BookInstance.objects.create(book=test_book,imprint='Unlikely Imprint, 2016', due_back=return_date, borrower=test_user1, status='o')
+
+ #Create a BookInstance object for test_user2
+ return_date= datetime.date.today() + datetime.timedelta(days=5)
+ self.test_bookinstance2=BookInstance.objects.create(book=test_book,imprint='Unlikely Imprint, 2016', due_back=return_date, borrower=test_user2, status='o')</pre>
+
+<p>Add the following tests to the bottom of the test class. These check that only users with the correct permissions (<em>testuser2</em>) can access the view. We check all the cases: when the user is not logged in, when a user is logged in but does not have the correct permissions, when the user has permissions but is not the borrower (should succeed), and what happens when they try to access a <code>BookInstance</code> that doesn't exist. We also check that the correct template is used.</p>
+
+<pre class="brush: python notranslate">   def test_redirect_if_not_logged_in(self):
+        resp = self.client.get(reverse('renew-book-librarian', kwargs={'pk':self.test_bookinstance1.pk,}) )
+        #Manually check redirect (Can't use assertRedirect, because the redirect URL is unpredictable)
+        self.assertEqual( resp.status_code,302)
+        self.assertTrue( resp.url.startswith('/accounts/login/') )
+
+    def test_redirect_if_logged_in_but_not_correct_permission(self):
+        login = self.client.login(username='testuser1', password='12345')
+        resp = self.client.get(reverse('renew-book-librarian', kwargs={'pk':self.test_bookinstance1.pk,}) )
+
+        #Manually check redirect (Can't use assertRedirect, because the redirect URL is unpredictable)
+        self.assertEqual( resp.status_code,302)
+        self.assertTrue( resp.url.startswith('/accounts/login/') )
+
+    def test_logged_in_with_permission_borrowed_book(self):
+        login = self.client.login(username='testuser2', password='12345')
+        resp = self.client.get(reverse('renew-book-librarian', kwargs={'pk':self.test_bookinstance2.pk,}) )
+
+        #Check that it lets us login - this is our book and we have the right permissions.
+        self.assertEqual( resp.status_code,200)
+
+    def test_logged_in_with_permission_another_users_borrowed_book(self):
+        login = self.client.login(username='testuser2', password='12345')
+        resp = self.client.get(reverse('renew-book-librarian', kwargs={'pk':self.test_bookinstance1.pk,}) )
+
+        #Check that it lets us login. We're a librarian, so we can view any users book
+        self.assertEqual( resp.status_code,200)
+
+    def test_HTTP404_for_invalid_book_if_logged_in(self):
+        import uuid
+        test_uid = uuid.uuid4() #unlikely UID to match our bookinstance!
+        login = self.client.login(username='testuser2', password='12345')
+        resp = self.client.get(reverse('renew-book-librarian', kwargs={'pk':test_uid,}) )
+        self.assertEqual( resp.status_code,404)
+
+    def test_uses_correct_template(self):
+        login = self.client.login(username='testuser2', password='12345')
+        resp = self.client.get(reverse('renew-book-librarian', kwargs={'pk':self.test_bookinstance1.pk,}) )
+        self.assertEqual( resp.status_code,200)
+
+        #Check we used correct template
+        self.assertTemplateUsed(resp, 'catalog/book_renew_librarian.html')
+</pre>
+
+<p>Add the next test method, as shown below. This checks that the initial date for the form is three weeks in the future. Note how we are able to access the value of the initial value of the form field (shown in bold).</p>
+
+<pre class="brush: python notranslate">    def test_form_renewal_date_initially_has_date_three_weeks_in_future(self):
+        login = self.client.login(username='testuser2', password='12345')
+        resp = self.client.get(reverse('renew-book-librarian', kwargs={'pk':self.test_bookinstance1.pk,}) )
+        self.assertEqual( resp.status_code,200)
+
+        date_3_weeks_in_future = datetime.date.today() + datetime.timedelta(weeks=3)
+        self.assertEqual(<strong>resp.context['form'].initial['renewal_date']</strong>, date_3_weeks_in_future )
+</pre>
+
+<p>The next test (add this to the class too) checks that the view redirects to a list of all borrowed books if renewal succeeds. What differs here is that for the first time we show how you can <code>POST</code> data using the client. The post <em>data</em> is the second argument to the post function, and is specified as a dictionary of key/values.</p>
+
+<pre class="brush: python notranslate">    def test_redirects_to_all_borrowed_book_list_on_success(self):
+        login = self.client.login(username='testuser2', password='12345')
+        valid_date_in_future = datetime.date.today() + datetime.timedelta(weeks=2)
+        resp = <strong>self.client.<em>post</em>(reverse('renew-book-librarian', kwargs={'pk':self.test_bookinstance1.pk,}), {'renewal_date':valid_date_in_future} )</strong>
+        self.assertRedirects(resp, reverse('all-borrowed') )
+</pre>
+
+<div class="warning">
+<p>The <em>all-borrowed</em> view was added as a <em>challenge</em>, and your code may instead redirect to the home page '/'. If so, modify the last two lines of the test code to be like the code below. The <code>follow=True</code> in the request ensures that the request returns the final destination URL (hence checking <code>/catalog/</code> rather than <code>/</code>).</p>
+
+<pre class="brush: python notranslate"> resp = self.client.post(reverse('renew-book-librarian', kwargs={'pk':self.test_bookinstance1.pk,}), {'renewal_date':valid_date_in_future},<strong>follow=True</strong> )
+ <strong>self.assertRedirects(resp, '/catalog/')</strong></pre>
+</div>
+
+<p>Copy the last two functions into the class, as seen below. These again test <code>POST</code> requests, but in this case with invalid renewal dates. We use <code>assertFormError() </code>to verify that the error messages are as expected.</p>
+
+<pre class="brush: python notranslate">    def test_form_invalid_renewal_date_past(self):
+        login = self.client.login(username='testuser2', password='12345')
+        date_in_past = datetime.date.today() - datetime.timedelta(weeks=1)
+        resp = self.client.post(reverse('renew-book-librarian', kwargs={'pk':self.test_bookinstance1.pk,}), {'renewal_date':date_in_past} )
+        self.assertEqual( resp.status_code,200)
+        <strong>self.assertFormError(resp, 'form', 'renewal_date', 'Invalid date - renewal in past')</strong>
+
+    def test_form_invalid_renewal_date_future(self):
+        login = self.client.login(username='testuser2', password='12345')
+        invalid_date_in_future = datetime.date.today() + datetime.timedelta(weeks=5)
+        resp = self.client.post(reverse('renew-book-librarian', kwargs={'pk':self.test_bookinstance1.pk,}), {'renewal_date':invalid_date_in_future} )
+        self.assertEqual( resp.status_code,200)
+        <strong>self.assertFormError(resp, 'form', 'renewal_date', 'Invalid date - renewal more than 4 weeks ahead')</strong>
+</pre>
+
+<p>The same sorts of techniques can be used to test the other view.</p>
+
+<h3 id="Templates">Templates</h3>
+
+<p>Django provides test APIs to check that the correct template is being called by your views, and to allow you to verify that the correct information is being sent. There is however no specific API support for testing in Django that your HTML output is rendered as expected.</p>
+
+<h2 id="Other_recommended_test_tools">Other recommended test tools</h2>
+
+<p>Django's test framework can help you write effective unit and integration tests — we've only scratched the surface of what the underlying <strong>unittest</strong> framework can do, let alone Django's additions (for example, check out how you can use <a href="https://docs.python.org/3.5/library/unittest.mock-examples.html">unittest.mock</a> to patch third party libraries so you can more thoroughly test your own code).</p>
+
+<p>While there are numerous other test tools that you can use, we'll just highlight two:</p>
+
+<ul>
+ <li><a href="http://coverage.readthedocs.io/en/latest/">Coverage</a>: This Python tool reports on how much of your code is actually executed by your tests. It is particularly useful when you're getting started, and you are trying to work out exactly what you should test.</li>
+ <li><a href="/en-US/docs/Learn/Tools_and_testing/Cross_browser_testing/Your_own_automation_environment">Selenium</a> is a framework to automate testing in a real browser. It allows you to simulate a real user interacting with the site, and provides a great framework for system testing your site (the next step up from integration testing.</li>
+</ul>
+
+<h2 id="Reto_para_mi_mismo">Reto para mi mismo</h2>
+
+<p>There are a lot more models and views we can test. As a simple task, try to create a test case for the <code>AuthorCreate</code> view.</p>
+
+<pre class="brush: python notranslate">class AuthorCreate(PermissionRequiredMixin, CreateView):
+ model = Author
+ fields = '__all__'
+ initial={'date_of_death':'12/10/2016',}
+ permission_required = 'catalog.can_mark_returned'</pre>
+
+<p>Remember that you need to check anything that you specify or that is part of the design. This will include who has access, the initial date, the template used, and where the view redirects on success.</p>
+
+<h2 id="Resumen">Resumen</h2>
+
+<p>Writing test code is neither fun nor glamorous, and is consequently often left to last (or not at all) when creating a website. It is however an essential part of making sure that your code is safe to release after making changes, and cost-effective to maintain.</p>
+
+<p>In this tutorial we've shown you how to write and run tests for your models, forms, and views. Most importantly we've provided a brief summary of what you should test, which is often the hardest thing to work out when your getting started. There is a lot more to know, but even with what you've learned already you should be able to create effective unit tests for your websites.</p>
+
+<p>The next and final tutorial shows how you can deploy your wonderful (and fully tested!) Django website.</p>
+
+<h2 id="Mirar_tambien">Mirar tambien</h2>
+
+<ul>
+ <li><a href="https://docs.djangoproject.com/en/1.10/topics/testing/overview/">Writing and running tests</a> (Django docs)</li>
+ <li><a href="https://docs.djangoproject.com/en/1.10/intro/tutorial05/">Writing your first Django app, part 5 &gt; Introducing automated testing</a> (Django docs)</li>
+ <li><a href="https://docs.djangoproject.com/en/1.10/topics/testing/tools/">Testing tools reference</a> (Django docs)</li>
+ <li><a href="https://docs.djangoproject.com/en/1.10/topics/testing/advanced/">Advanced testing topics</a> (Django docs)</li>
+ <li><a href="http://toastdriven.com/blog/2011/apr/10/guide-to-testing-in-django/">A Guide to Testing in Django</a> (Toast Driven Blog, 2011)</li>
+ <li><a href="http://test-driven-django-development.readthedocs.io/en/latest/index.html">Workshop: Test-Driven Web Development with Django</a> (San Diego Python, 2014)</li>
+ <li><a href="https://realpython.com/blog/python/testing-in-django-part-1-best-practices-and-examples/">Testing in Django (Part 1) - Best Practices and Examples</a> (RealPython, 2013)</li>
+</ul>
+
+<p>{{PreviousMenuNext("Learn/Server-side/Django/Forms", "Learn/Server-side/Django/Deployment", "Learn/Server-side/Django")}}</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>
diff --git a/files/es/learn/server-side/django/tutorial_local_library_website/index.html b/files/es/learn/server-side/django/tutorial_local_library_website/index.html
new file mode 100644
index 0000000000..a7450fafd0
--- /dev/null
+++ b/files/es/learn/server-side/django/tutorial_local_library_website/index.html
@@ -0,0 +1,103 @@
+---
+title: 'Tutorial Django: El Sitio Web de La Biblioteca Local'
+slug: Learn/Server-side/Django/Tutorial_local_library_website
+tags:
+ - Aprendizaje
+ - Artículo
+ - Codificación de scripts
+ - Guía
+ - Principiante
+ - Tutorial
+ - django
+ - lado servidor
+translation_of: Learn/Server-side/Django/Tutorial_local_library_website
+---
+<div>{{LearnSidebar}}</div>
+
+<div>{{PreviousMenuNext("Learn/Server-side/Django/development_environment", "Learn/Server-side/Django/skeleton_website", "Learn/Server-side/Django")}}</div>
+
+<p class="summary">El primer artículo de nuestra serie de tutoriales prácticos explica qué puedes aprender, y proporciona una visión general del sitio web de ejemplo de "biblioteca local" en el que estaremos trabajando y evolucionando en artículos subsiguientes.</p>
+
+<table class="learn-box standard-table">
+ <tbody>
+ <tr>
+ <th scope="row">Pre-requisitos:</th>
+ <td>
+ <p>Leer la <a href="https://developer.mozilla.org/es/docs/Learn/Server-side/Django/Introducci%C3%B3n">Introducción a Django</a>. Durante los siguientes artículos necesitarás tener <a href="https://developer.mozilla.org/es/docs/Learn/Server-side/Django/development_environment">levantado un entorno de desarrollo Django</a>. </p>
+ </td>
+ </tr>
+ <tr>
+ <th scope="row">Objetivo:</th>
+ <td>
+ <p>Presentar el ejemplo de aplicación usado en este tutorial, y permitir que los lectores comprendan los temas que se tratarán aquí. </p>
+ </td>
+ </tr>
+ </tbody>
+</table>
+
+<h2 id="Visión_general">Visión general</h2>
+
+<p>Bienvenidos al tutorial de MDN sobre la "Biblioteca Local" Django en el que desarrollaremos un sitio web que podría usarse para gestionar el catálogo de una biblioteca local. </p>
+
+<p>En esta serie de tutoriales:</p>
+
+<ul>
+ <li>Usarás las herramientas de Django para crear el esqueleto de un sitio web y una aplicación.</li>
+ <li>Arrancarás y pararás el servidor de desarrollo.</li>
+ <li>Crearás modelos para representar los datos de tu aplicación.</li>
+ <li>Usarás el sitio de administración de Django para rellenar los datos de tu sitio.</li>
+ <li>Crearás views para recuperar datos específicos en respuesta a diferentes peticiones, y plantillas para renderizar los datos como HTML que serán presentados en pantalla por el explorador web.</li>
+ <li>Crearás mapeadores para asociar los diferentes patrones URL con views específicas.</li>
+ <li>Añadirás autorizaciones de usuario y sesiones para controlar el comportamiento del sitio y acceso al mismo.</li>
+ <li>Trabajarás con formularios.</li>
+ <li>Escribirás código para comprobar tu aplicación.</li>
+ <li>Usarás la seguridad de Django con eficacia.</li>
+ <li>Desplegarás tu aplicación a producción.</li>
+</ul>
+
+<p>Has aprendido algo de estos tópicos ya, y tocado otros brevemente. Al final de esta serie de tutoriales sabrás lo suficiente para desarrollar aplicaciones simples Django por tí mismo.</p>
+
+<h2 id="El_sitio_web_de_la_Biblioteca_Local">El sitio web de la Biblioteca Local</h2>
+
+<p><em>BibliotecaLocal</em> es el nombre del sitio web que crearemos para evolucionar a lo largo del curso de esta serie de tutoriales. Como habrás supuesto, el propósito de este sitio web es proporcionar un catálogo online para una pequeña biblioteca local, en la que los usuarios pueden explorar los libros disponibles y gestionar sus cuentas.</p>
+
+<p>Este ejemplo ha sido seleccionado cuidadosamente porque puede escalar para mostrar tanto mucho detalle como poco según necesitemos, y puede usarse para mostrar casi cualquier característica de Django. Más importante aún, nos permite proporcionar un camino guiado a través de las funcionalidades más importantes del framework web Django:</p>
+
+<ul>
+ <li>En los primeros artículos del tutorial definiremos una blibioteca simple para <em>explorar sólo</em> que podrán usar sus miembros para encontrar qué libros están disponibles. Esto nos permitirá explorar las operaciones que son comunes a casi todos los sitios web: lectura y presentación de los contenidos de una base de datos.</li>
+ <li>A medida que progresemos, el ejemplo de la biblioteca demostrará características más avanzadas de Django. Por ejemplo podremos extender la biblioteca para permitir a los usuarios reservar libros, y usar ésto para demostrar cómo se usan los formularios, y soportar la autenticación de usuario.</li>
+</ul>
+
+<p>Incluso aunque es un ejemplo muy extenso, se llama <em>Biblioteca<strong>Local</strong></em> por una razón — esperamos mostrar el mínimo de información que te ayude a ponerte en marcha con Django rápidamente. Como resultado almacenaremos información sobre libros, copias de libros, autores y otras información clave. Sin embargo, no almacenaremos información sobre otros elementos que una biblioteca podría almacenar, ni proporcionaremos la infraestructura necesaría para mantener multiples sitios de bibliotecas u otras características de una "gran biblioteca".</p>
+
+<h2 id="Estoy_atascado_¿Donde_puedo_encontrar_el_código_fuente">Estoy atascado, ¿Donde puedo encontrar el código fuente?</h2>
+
+<p>A medida que avances a través del tutorial te proporcionaremos los fragmentos de código apropiados para que copies y pegues en cada punto, y habrá otro código que esperamos que puedas entender tú mismo (con algo de ayuda).</p>
+
+<p>Si te quedas atascado, puedes encontrar la versión completamente desarrollada <a href="https://github.com/mdn/django-locallibrary-tutorial">en el sitio web de Github aquí</a>.</p>
+
+<h2 id="Resumen">Resumen</h2>
+
+<p>Ahora que sabes un poco más sobre el sitio de la <em>BibliotecaLocal</em> y lo que vas a aprender, es hora de empezar a crear el <a href="/es/docs/Learn/Server-side/Django/skeleton_website">esqueleto(estructura)</a> de nuestro proyecto.</p>
+
+<p>{{PreviousMenuNext("Learn/Server-side/Django/development_environment", "Learn/Server-side/Django/skeleton_website", "Learn/Server-side/Django")}}</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>
diff --git a/files/es/learn/server-side/django/web_application_security/index.html b/files/es/learn/server-side/django/web_application_security/index.html
new file mode 100644
index 0000000000..e00a1771bb
--- /dev/null
+++ b/files/es/learn/server-side/django/web_application_security/index.html
@@ -0,0 +1,176 @@
+---
+title: Seguridad de las aplicaciones web Django
+slug: Learn/Server-side/Django/web_application_security
+translation_of: Learn/Server-side/Django/web_application_security
+---
+<div>{{LearnSidebar}}</div>
+
+<div>{{PreviousMenuNext("Learn/Server-side/Django/Deployment", "Learn/Server-side/Django/django_assessment_blog", "Learn/Server-side/Django")}}</div>
+
+<p class="summary">Proteger los datos de los usuarios es una parte esencial de cualquier diseño de un sitio web. Previamente ya explicamos algunas de las amenazas de seguridad más comunes en el artículo <a href="https://developer.mozilla.org/en-US/docs/Web/Security">Seguridad Web</a> — este artículo proporciona una demostración práctica de cómo las protecciones integradas de Django gestionan estas amenazas.</p>
+
+<table class="learn-box standard-table">
+ <tbody>
+ <tr>
+ <th scope="row">Prerrequisitos:</th>
+ <td>Lee la documentación de la Programación del Lado del servidor "<a href="https://developer.mozilla.org/en-US/docs/Learn/Server-side/First_steps/Website_security">Seguridad de la aplicación web de Django</a>". Completa todos los temas del Tutorial de Django (incluyendo este) o por lo menos el <a href="/en-US/docs/Learn/Server-side/Django/Forms">Tutorial de Django Parte 9: Trabajar con formularios web</a>.</td>
+ </tr>
+ <tr>
+ <th scope="row">Objetivo:</th>
+ <td>Comprender las cosas principales que debes hacer (y las que no debes) para proteger su aplicación web de Django.</td>
+ </tr>
+ </tbody>
+</table>
+
+<h2 id="Overview">Overview</h2>
+
+<p>The <a href="https://developer.mozilla.org/en-US/docs/Web/Security">Website security</a> topic provides an overview of what website security means for server-side design, and some of the more common threats that you may need to protect against. One of the key messages in that article is that almost all attacks are successful when the web application trusts data from the browser.</p>
+
+<div class="warning">
+<p><strong>Important:</strong> The single most important lesson you can learn about website security is to <strong>never trust data from the browser</strong>. This includes <code>GET</code> request data in URL parameters, <code>POST</code> data, HTTP headers and cookies, user-uploaded files, etc. Always check and sanitize all incoming data. Always assume the worst.</p>
+</div>
+
+<p>The good news for Django users is that many of the more common threats are handled by the framework! The <a href="https://docs.djangoproject.com/en/1.10/topics/security/">Security in Django</a> (Django docs) article explains Django's security features and how to secure a Django-powered website.</p>
+
+<h2 id="Common_threatsprotections">Common threats/protections</h2>
+
+<p>Rather than duplicate the Django documentation here, in this article we'll demonstrate just a few of the security features in the context of our Django <a href="/en-US/docs/Learn/Server-side/Django/Tutorial_local_library_website">LocalLibrary</a> tutorial.</p>
+
+<h3 id="Cross_site_scripting_(XSS)">Cross site scripting (XSS)</h3>
+
+<p>XSS is a term used to describe a class of attacks that allow an attacker to inject client-side scripts <em>through</em> the website into the browsers of other users. This is usually achieved by storing malicious scripts in the database where they can be retrieved and displayed to other users, or by getting users to click a link that will cause the attacker’s JavaScript to be executed by the user’s browser.</p>
+
+<p>Django's template system protects you against the majority of XSS attacks by <a href="https://docs.djangoproject.com/en/1.10/ref/templates/language/#automatic-html-escaping">escaping specific characters</a> that are "dangerous" in HTML. We can demonstrate this by attempting to inject some JavaScript into our LocalLibrary website using the Create-author form we set up in <a href="/en-US/docs/Learn/Server-side/Django/Forms">Django Tutorial Part 9: Working with forms</a>.</p>
+
+<ol>
+ <li>Start the website using the development server (<code>python3 manage.py runserver</code>).</li>
+ <li>Open the site in your local browser and login to your superuser account.</li>
+ <li>Navigate to the author-creation page (which should be at URL: <code><a href="http://127.0.0.1:8000/catalog/author/create/">http://127.0.0.1:8000/catalog/author/create/</a></code>).</li>
+ <li>Enter names and date details for a new user, and then append the following text to the Last Name field:<br>
+ <code>&lt;script&gt;alert('Test alert');&lt;/script&gt;</code>.<br>
+ <img alt="Author Form XSS test" src="https://mdn.mozillademos.org/files/14305/author_create_form_alert_xss.png" style="border-style: solid; border-width: 1px; height: 245px; width: 594px;">
+ <div class="note">
+ <p><strong>Note:</strong> This is a harmless script that, if executed, will display an alert box in your browser. If the alert is displayed when you submit the record then the site is vulnerable to XSS threats.</p>
+ </div>
+ </li>
+ <li>Press <strong>Submit</strong> to save the record.</li>
+ <li>When you save the author it will be displayed as shown below. Because of the XSS protections the <code>alert()</code> should not be run. Instead the script is displayed as plain text.<img alt="Author detail view XSS test" src="https://mdn.mozillademos.org/files/14307/author_detail_alert_xss.png" style="border-style: solid; border-width: 1px; height: 248px; width: 986px;"></li>
+</ol>
+
+<p>If you view the page HTML source code, you can see that the dangerous characters for the script tags have been turned into their harmless escape code equivalents (e.g. <code>&gt;</code> is now <code>&amp;gt;</code>)</p>
+
+<pre class="brush: html">&lt;h1&gt;Author: Boon&amp;lt;script&amp;gt;alert(&amp;#39;Test alert&amp;#39;);&amp;lt;/script&amp;gt;, David (Boonie) &lt;/h1&gt;
+</pre>
+
+<p>Using Django templates protects you against the majority of XSS attacks. However it is possible to turn off this protection, and the protection isn't automatically applied to all tags that wouldn't normally be populated by user input (for example, the <code>help_text</code> in a form field is usually not user-supplied, so Django doesn't escape those values).</p>
+
+<p>It is also possible for XSS attacks to originate from other untrusted source of data, such as cookies, Web services or uploaded files (whenever the data is not sufficiently sanitized before including in a page). If you're displaying data from these sources, then you may need to add your own sanitisation code.</p>
+
+<h3 id="Cross_site_request_forgery_(CSRF)_protection">Cross site request forgery (CSRF) protection</h3>
+
+<p>CSRF attacks allow a malicious user to execute actions using the credentials of another user without that user’s knowledge or consent. For example consider the case where we have a hacker who wants to create additional authors for our LocalLibrary.</p>
+
+<div class="note">
+<p><strong>Note:</strong> Obviously our hacker isn't in this for the money! A more ambitious hacker could use the same approach on other sites to perform much more harmful tasks (e.g. transfer money to their own accounts, etc.)</p>
+</div>
+
+<p>In order to do this, they might create an HTML file like the one below, which contains an author-creation form (like the one we used in the previous section) that is submitted as soon as the file is loaded. They would then send the file to all the Librarians and suggest that they open the file (it contains some harmless information, honest!). If the file is opened by any logged in librarian, then the form would be submitted with their credentials and a new author would be created.</p>
+
+<pre class="brush: html">&lt;html&gt;
+&lt;body onload='document.EvilForm.submit()'&gt;
+
+&lt;form action="http://127.0.0.1:8000/catalog/author/create/" method="post" name='EvilForm'&gt;
+ &lt;table&gt;
+ &lt;tr&gt;&lt;th&gt;&lt;label for="id_first_name"&gt;First name:&lt;/label&gt;&lt;/th&gt;&lt;td&gt;&lt;input id="id_first_name" maxlength="100" name="first_name" type="text" value="Mad" required /&gt;&lt;/td&gt;&lt;/tr&gt;
+ &lt;tr&gt;&lt;th&gt;&lt;label for="id_last_name"&gt;Last name:&lt;/label&gt;&lt;/th&gt;&lt;td&gt;&lt;input id="id_last_name" maxlength="100" name="last_name" type="text" value="Man" required /&gt;&lt;/td&gt;&lt;/tr&gt;
+ &lt;tr&gt;&lt;th&gt;&lt;label for="id_date_of_birth"&gt;Date of birth:&lt;/label&gt;&lt;/th&gt;&lt;td&gt;&lt;input id="id_date_of_birth" name="date_of_birth" type="text" /&gt;&lt;/td&gt;&lt;/tr&gt;
+ &lt;tr&gt;&lt;th&gt;&lt;label for="id_date_of_death"&gt;Died:&lt;/label&gt;&lt;/th&gt;&lt;td&gt;&lt;input id="id_date_of_death" name="date_of_death" type="text" value="12/10/2016" /&gt;&lt;/td&gt;&lt;/tr&gt;
+ &lt;/table&gt;
+ &lt;input type="submit" value="Submit" /&gt;
+&lt;/form&gt;
+
+&lt;/body&gt;
+&lt;/html&gt;
+</pre>
+
+<p>Run the development web server, and log in with your superuser account. Copy the text above into a file and then open it in the browser. You should get a CSRF error, because Django has protection against this kind of thing!</p>
+
+<p>The way the protection is enabled is that you include the <code>{% csrf_token %}</code> template tag in your form definition. This token is then rendered in your HTML as shown below, with a value that is specific to the user on the current browser.</p>
+
+<pre class="brush: html">&lt;input type='hidden' name='csrfmiddlewaretoken' value='0QRWHnYVg776y2l66mcvZqp8alrv4lb8S8lZ4ZJUWGZFA5VHrVfL2mpH29YZ39PW' /&gt;
+</pre>
+
+<p>Django generates a user/browser specific key and will reject forms that do not contain the field, or that contain an incorrect field value for the user/browser.</p>
+
+<p>To use this type of attack the hacker now has to discover and include the CSRF key for the specific target user. They also can't use the "scattergun" approach of sending a malicious file to all librarians and hoping that one of them will open it, since the CSRF key is browser specific.</p>
+
+<p>Django's CSRF protection is turned on by default. You should always use the <code>{% csrf_token %}</code> template tag in your forms and use <code>POST</code> for requests that might change or add data to the database.</p>
+
+<h3 id="Other_protections">Other protections</h3>
+
+<p>Django also provides other forms of protection (most of which would be hard or not particularly useful to demonstrate):</p>
+
+<dl>
+ <dt>SQL injection protection</dt>
+ <dd>SQL injection vulnerabilities enable malicious users to execute arbitrary SQL code on a database, allowing data to be accessed, modified, or deleted irrespective of the user's permissions. In almost every case you'll be accessing the database using Django’s querysets/models, so the resulting SQL will be properly escaped by the underlying database driver. If you do need to write raw queries or custom SQL then you'll need to explicitly think about preventing SQL injection.</dd>
+ <dt>Clickjacking protection</dt>
+ <dd>In this attack a malicious user hijacks clicks meant for a visible top level site and routes them to a hidden page beneath. This technique might be used, for example, to display a legitimate bank site but capture the login credentials in an invisible <a href="https://developer.mozilla.org/en-US/docs/Web/HTML/Element/iframe" title="The HTML Inline Frame Element (&lt;iframe>) represents a nested browsing context, effectively embedding another HTML page into the current page. In HTML 4.01, a document may contain a head and a body or a head and a frameset, but not both a body and a frameset. However, an &lt;iframe> can be used within a normal document body. Each browsing context has its own session history and active document. The browsing context that contains the embedded content is called the parent browsing context. The top-level browsing context (which has no parent) is typically the browser window."><code>&lt;iframe&gt;</code></a> controlled by the attacker. Django contains <a href="https://docs.djangoproject.com/en/1.10/ref/clickjacking/#clickjacking-prevention">clickjacking protection</a> in the form of the <a href="https://docs.djangoproject.com/en/1.10/ref/middleware/#django.middleware.clickjacking.XFrameOptionsMiddleware" title="django.middleware.clickjacking.XFrameOptionsMiddleware"><code>X-Frame-Options middleware</code></a> which, in a supporting browser, can prevent a site from being rendered inside a frame.</dd>
+ <dt>Enforcing SSL/HTTPS</dt>
+ <dd>SSL/HTTPS can be enabled on the web server in order to encrypt all traffic between the site and browser, including authentication credentials that would otherwise be sent in plain text (enabling HTTPS is highly recommended). If HTTPS is enabled then Django provides a number of other protections you can use:</dd>
+</dl>
+
+<ul>
+ <li><a href="https://docs.djangoproject.com/en/1.10/ref/settings/#std:setting-SECURE_PROXY_SSL_HEADER"><code>SECURE_PROXY_SSL_HEADER</code></a> can be used to check whether content is secure, even if it is incoming from a non-HTTP proxy.</li>
+ <li><a href="https://docs.djangoproject.com/en/1.10/ref/settings/#std:setting-SECURE_SSL_REDIRECT"><code>SECURE_SSL_REDIRECT</code></a> is used to redirect all HTTP requests to HTTPS.</li>
+ <li>Use <a href="https://docs.djangoproject.com/en/1.10/ref/middleware/#http-strict-transport-security">HTTP Strict Transport Security</a> (HSTS). This is an HTTP header that informs a browser that all future connections to a particular site should always use HTTPS. Combined with redirecting HTTP requests to HTTPS, this setting ensures that HTTPS is always used after a successful connection has occurred. HSTS may either be configured with <a href="https://docs.djangoproject.com/en/1.10/ref/settings/#std:setting-SECURE_HSTS_SECONDS"><code>SECURE_HSTS_SECONDS</code></a> and <a href="https://docs.djangoproject.com/en/1.10/ref/settings/#std:setting-SECURE_HSTS_INCLUDE_SUBDOMAINS"><code>SECURE_HSTS_INCLUDE_SUBDOMAINS</code></a> or on the Web server.</li>
+ <li>Use ‘secure’ cookies by setting <a href="https://docs.djangoproject.com/en/1.10/ref/settings/#std:setting-SESSION_COOKIE_SECURE"><code>SESSION_COOKIE_SECURE</code></a> and <a href="https://docs.djangoproject.com/en/1.10/ref/settings/#std:setting-CSRF_COOKIE_SECURE"><code>CSRF_COOKIE_SECURE</code></a> to <code>True</code>. This will ensure that cookies are only ever sent over HTTPS.</li>
+</ul>
+
+<dl>
+ <dt>Host header validation</dt>
+ <dd>Use <a href="https://docs.djangoproject.com/en/1.10/ref/settings/#std:setting-ALLOWED_HOSTS"><code>ALLOWED_HOSTS</code></a> to only accept requests from trusted hosts.</dd>
+</dl>
+
+<p>There are many other protections, and caveats to the usage of the above mechanisms. While we hope that this has given you an overview of what Django offers, you should still read the Django security documentation.</p>
+
+<ul>
+</ul>
+
+<h2 id="Summary">Summary</h2>
+
+<p>Django has effective protections against a number of common threats, including XSS and CSRF attacks. In this article we've demonstrated how those particular threats are handled by Django in our <em>LocalLibrary</em> website. We've also provided a brief overview of some of the other protections.</p>
+
+<p>This has been a very brief foray into web security. We strongly recommend that you read <a href="https://docs.djangoproject.com/en/1.10/topics/security/">Security in Django</a> to gain a deeper understanding.</p>
+
+<p>The next and final step in this module about Django is to complete the <a href="/en-US/docs/Learn/Server-side/Django/django_assessment_blog">assessment task</a>.</p>
+
+<h2 id="See_also">See also</h2>
+
+<ul>
+ <li><a href="https://docs.djangoproject.com/en/1.10/topics/security/">Security in Django</a> (Django docs)</li>
+ <li><a href="https://developer.mozilla.org/en-US/docs/Web/Security">Server side website security</a> (MDN)</li>
+ <li><a href="https://developer.mozilla.org/en-US/docs/Web/Security">Web security</a> (MDN)</li>
+ <li><a href="/en-US/docs/Web/Security/Securing_your_site">Securing your site</a> (MDN)</li>
+</ul>
+
+<p>{{PreviousMenuNext("Learn/Server-side/Django/Deployment", "Learn/Server-side/Django/django_assessment_blog", "Learn/Server-side/Django")}}</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>