One place for hosting & domains

      aplicación

      Cómo crear una aplicación Web usando Flask en Python 3


      El autor seleccionó la Free and Open Source Fund para recibir una donación como parte del programa Write for DOnations.

      Introducción

      Flask es un marco web de Python pequeño y ligero que proporciona herramientas y funciones útiles que hacen que crear aplicaciones web en Python sea más fácil. Ofrece a los desarrolladores flexibilidad y un marco más accesible para los nuevos desarrolladores ya que puede crear una aplicación web rápidamente usando únicamente un archivo Python. Flask también es extensible y no fuerza una estructura de directorio concreta ni requiere código estándar complicado antes de iniciarse.

      Como parte de este tutorial, usará el kit de herramientas Bootstrap para dar estilo a su aplicación, para que sea más atractiva visualmente. Bootstrap le ayudará a incorporar páginas web reactivas en su aplicación web para que también funcione bien en navegadores móviles sin tener que escribir su propio código HTML, CSS y JavaScript para conseguir estos objetivos. El kit de herramientas le permitirá centrarse en aprender cómo funciona Flask.

      Flask utiliza el motor de plantillas Jinja para crear dinámicamente páginas HTML usando conceptos Python familiares como variables, bucles, listas, etcétera. Usará estas plantillas como parte de este proyecto.

      En este tutorial, creará un pequeño blog web usando Flask y SQLite en Python 3. Los usuarios de la aplicación pueden ver todas las entradas del blog en su base de datos y hacer clic sobre el título de una entrada para ver su contenido, con la capacidad de añadir una nueva publicación a la base de datos y editar o eliminar una entrada existente.

      Requisitos previos

      Antes de comenzar con esta guía, necesitará lo siguiente:

      Paso 1: Instalar Flask

      En este paso, activará su entorno Python e instalará Flask usando el instalador de paquetes pip.

      Si aún no ha activado su entorno de programación, asegúrese de que está en el directorio de su proyecto (flask_blog) y utilice el siguiente comando para activar el entorno:

      Una vez activado su entorno de programación, su línea de comandos ahora tendrá un prefijo env que puede tener el siguiente aspecto:

      Este prefijo es una indicación de que el entorno env está activo actualmente, y puede tener otro nombre dependiendo de cómo lo llamó durante la creación.

      Nota: Puede utilizar Git, un sistema de control de versiones, para administrar y realizar un seguimiento de forma efectiva del proceso de desarrollo de su proyecto. Para aprender cómo usar Git, es posible que desee consultar nuestro artículo Introducción a la instalación de Git, uso y ramificaciones.

      Si está usando Git, es una buena idea ignorar el recién creado directorio env en su archivo .gitignore para evitar realizar un seguimiento de los archivos no relacionados con el proyecto.

      Ahora, instalará los paquetes Python y aislará el código de su proyecto de la instalación del sistema principal de Python. Hará esto usando pip y python.

      Para instalar Flask, ejecute el siguiente comando:

      Una vez que se complete la instalación, ejecute el siguiente comando para confirmar la instalación:

      • python -c "import flask; print(flask.__version__)"

      Utiliza la interfaz de la línea de comandos python con la opción -c para ejecutar el código Python. A continuación, importa el paquete flask con import flask; luego imprime la versión de Flask, que encontrará a través de la variable flask._version_.

      El resultado será un número de versión similar al siguiente:

      Output

      1.1.2

      Ha creado la carpeta del proyecto, un entorno virtual, e instalado Flask. Ahora está listo para configurar su aplicación básica.

      Paso 2: Crear una aplicación básica

      Ahora que tiene su entorno de programación configurado, comenzará a usar Flask. En este paso, creará una pequeña aplicación web dentro de un archivo Python, y la ejecutará para iniciar el servidor, que mostrará cierta información en el navegador.

      En su directorio flask_blog, abra un archivo llamado hello.py para su edición; para ello utilice nano o su editor de texto favorito:

      Este archivo hello.py servirá como ejemplo mínimo de cómo gestionar solicitudes HTTP. Dentro, importará el objeto Flask, y creará una función que devuelva una respuesta HTTP. Escriba el siguiente código dentro de hello.py:

      flask_blog/hello.py

      from flask import Flask
      
      app = Flask(__name__)
      
      
      @app.route('/')
      def hello():
          return 'Hello, World!'
      

      En el bloque de código anterior, primero importa el objeto Flask desde el paquete flask. A continuación lo usará para crear su instancia de aplicación Flask con el nombre app. Pasa la variable especial __name__ que alberga el nombre del módulo Pyhthon actual. Se utiliza para indicar a la instancia dónde está ubicada. Necesitará hacerlo porque Flask configura algunas rutas en segundo plano.

      Una vez que cree la instancia app, la utiliza para gestionar las solicitudes web entrantes y enviar respuestas al usuario. @app.route es un decorador que convierte una función Python regular en una función vista de Flask, que convierte el valor de devolución de la función en una respuesta HTTP que se mostrará mediante un cliente HTTP, como un navegador web. Pasa el valor '/' a @app.route() para indicar que esta función responderá a las solicitudes web para la URL /, que es la URL principal.

      La función de vista hello() devuelve la cadena 'Hello, World!'​​ como respuesta.

      Guarde y cierre el archivo.

      Para ejecutar su aplicación web, primero indicará a Flask dónde encontrar la aplicación (el archivo hello.py en su caso) con la variable de entorno FLASK_APP:

      A continuación, ejecútela en modo de desarrollo con la variable de entorno FLASK_ENV:

      • export FLASK_ENV=development

      Por último, ejecute la aplicación usando el comando flask run:

      Una vez que la aplicación se esté ejecutando, el resultado será algo similar a esto:

      Output

      * Serving Flask app "hello" (lazy loading) * Environment: development * Debug mode: on * Running on http://127.0.0.1:5000/ (Press CTRL+C to quit) * Restarting with stat * Debugger is active! * Debugger PIN: 813-894-335

      El resultado anterior tiene varias informaciones, como:

      • El nombre de la aplicación que está ejecutando.
      • El entorno en el cual se ejecuta la aplicación.
      • Debug mode: on significa que el depurador de Flask se está ejecutando. Esto es útil durante el desarrollo porque nos proporciona mensajes de error detallados cuando algo va mal, lo que hace que sea más fácil resolver los problemas.
      • La aplicación se ejecuta localmente sobre la URL http://127.0.0.1:5000/, 127.0.0.1 es la IP que representa el localhost de su equipo y :5000 es el número de puerto.

      Abra un navegador y escriba la URL http://127.0.0.1:5000; recibirá la cadena Hello, World! como respuesta. Esto confirma que su aplicación se está ejecutando correctamente.

      Advertencia Flask utiliza un servidor web sencillo para presentar nuestra aplicación en un entorno de desarrollo, lo que también significa que el depurador de Flask se ejecuta para hacer que sea más fácil ver los errores. Este servidor de desarrollo no debería usarse en una implementación de producción. Consulte la página Opciones de implementación en la documentación de Flask para obtener más información; también puede consultar este tutorial de Implementación Flask.

      Ahora puede dejar el servidor de desarrollo en ejecución en el terminal y abrir otra ventana del terminal. Vaya a la carpeta del proyecto donde está hello.py, active el entorno virtual, establezca las variables del entorno FLASK_ENV y FLASK_APP y continúe a los siguientes pasos. (Estos comandos se han enumerado antes en este paso).

      Nota: Cuando abra un nuevo terminal, es importante recordar activar el entorno virtual y configurar las variables de entorno FLASK_ENV y FLASK_APP.

      Mientras un servidor de desarrollo de la aplicación de Flask esté ejecutándose, no es posible ejecutar otra aplicación Flask con el mismo comando flask run. Esto es porque flask run utiliza el número de puerto 5000 por defecto, y una vez utilizado, no está disponible para ejecutar otra aplicación, de forma que recibiría un error similar al siguiente:

      Output

      OSError: [Errno 98] Address already in use

      Para resolver este problema, detenga el servidor que se está ejecutando actualmente mediante CTRL+C, luego ejecute flask run de nuevo, o si desea que ambos se ejecuten a la vez, puede pasar un número de puerto diferente al argumento -p; por ejemplo, para ejecutar otra aplicación en el puerto 5001 utilice el siguiente comando:

      Ahora tiene una aplicación web Flask pequeña. Ha ejecutado su aplicación y mostrado información en el navegador web. A continuación, utilizará los archivos HTML en su aplicación.

      Paso 3: Usar plantillas HTML

      Actualmente, su aplicación solo muestra un mensaje sencillo sin HTML. Las aplicaciones web utilizan principalmente HTML para mostrar información al visitante, de forma que ahora trabajará para incorporar archivos HTML en su aplicación, que puede ser mostrada en el navegador web.

      Flask proporciona una función de ayuda render_template() que permite el uso del motor de plantillas Jinja. Esto hará que gestionar HTML sea mucho más fácil escribiendo su código HTML en archivos .html, además de utilizar la lógica en su código HTML. Usará estos archivos HTML, (plantillas), para crear todas las páginas de su aplicación, de igual forma que la página principal donde mostrará las entradas actuales del blog, la página de la entrada del blog, la página donde el usuario puede añadir una nueva entrada, etcétera.

      En este paso, creará su aplicación Flask principal en un nuevo archivo.

      Primero, en su directorio flask_blog, utilice nano o su editor de texto favorito para crear y editar su archivo app.py. Éste albergará todo el código que usará para crear la aplicación de blog:

      En este nuevo archivo, importará el objeto Flask para crear una instancia de aplicación Flask, como hizo antes. También importará la función de ayuda render_template() que le permite representar archivos de plantilla HTML que existen en la carpeta templates que está a punto de crear. El archivo tendrá una función de vista única que será responsable de gestionar las solicitudes a la ruta principal /. Añada el siguiente contenido:

      flask_blog/app.py

      from flask import Flask, render_template
      
      app = Flask(__name__)
      
      @app.route('/')
      def index():
          return render_template('index.html')
      

      La función de vista index() devuelve el resultado de invocar render_template() con index.html como argumento; esto indica a render_template() que busque un archivo llamado index.html en la carpeta templates. La carpeta y el archivo no existen aún, y recibirá un error si ejecutase la aplicación en este momento. De todas formas la va a ejecutar, para que se familiarice con esta excepción que se encuentra comúnmente. Luego lo resolverá creando la carpeta y archivo necesarios.

      Guarde el archivo y ciérrelo.

      Detenga el servidor de desarrollo en su otro terminal que ejecuta la aplicación hello con CTRL+C.

      Antes de ejecutar la aplicación, asegúrese de especificar correctamente el valor para la variable de entorno FLASK_APP, ya que ahora no está usando la aplicación hello.

      • export FLASK_APP=app
      • flask run

      Al abrir la URL http://127.0.0.1:5000 en su navegador, se mostrará la página del depurador informándole de que no se ha encontrado la plantilla index.html. La línea de código principal en el código responsable de este error se resaltará. En este caso, es la línea return render_template('index.html').

      Si hace clic en esta línea, el depurador revelará más código de forma que tenga más contexto para ayudarle a resolver el problema.

      El Depurador de Flask

      Para solucionar este error, cree un directorio llamado templates dentro de su directorio flask_blog. Dentro de él, abra un archivo llamado index.html para su edición:

      • mkdir templates
      • nano templates/index.html

      A continuación, añada el siguiente código HTML dentro de index.html:

      flask_blog/templates/index.html

      <!DOCTYPE html>
      <html lang="en">
      <head>
          <meta charset="UTF-8">
          <title>FlaskBlog</title>
      </head>
      <body>
         <h1>Welcome to FlaskBlog</h1>
      </body>
      </html>
      

      Guarde el archivo y utilice su navegador para navegar a http://127.0.0.1:500 de nuevo, o actualice la página. Esta vez, el navegador debería mostrar el texto Welcome to FlaskBlog en una etiqueta <h1>.

      Además de la carpeta templates, las aplicaciones web de Flask también tienen normalmente una carpeta estática para albergar archivos, como los archivos CSS, archivos JavaScript, e imágenes que utiliza la aplicación.

      Puede crear un archivo de hoja de estilo style.css para añadir CSS a su aplicación. Primero, cree un directorio llamado static dentro de su directorio flask_blog principal:

      Luego cree otro directorio llamado css dentro del directorio static para albergar los archivos .css. Esto normalmente se hace para organizar archivos estáticos en carpetas dedicadas y, en consecuencia, los archivos JavaScript dentro de un directorio llamado js, las imágenes se ponen en un directorio llamado images (o img), etcétera. El siguiente comando creará el directorio css dentro del directorio static:

      Luego abra un archivo style.css dentro del directorio css para su edición:

      • nano static/css/style.css

      Añada la siguiente regla CSS a su archivo style.css:

      flask_blog/static/css/style.css

      h1 {
          border: 2px #eee solid;
          color: brown;
          text-align: center;
          padding: 10px;
      }
      

      El código CSS añadirá un borde, cambiará el color a marrón, centrará el texto y añadirá un pequeño relleno a las etiquetas <h1>.

      Guarde y cierre el archivo.

      A continuación, abra el archivo de plantilla index.html para su edición:

      • nano templates/index.html

      Añadirá un enlace al archivo style.css dentro de la sección <head> del archivo de plantilla index.html:

      flask_blog/templates/index.html

      . . .
      <head>
          <meta charset="UTF-8">
          <link rel="stylesheet" href="https://www.digitalocean.com/{{ url_for("static', filename="css/style.css") }}">
          <title>FlaskBlog</title>
      </head>
      . . .
      

      Aquí utiliza la función de ayuda url_for() para generar la ubicación apropiada del archivo. El primer argumento especifica que está vinculando a un archivo estático, y el segundo argumento es la ruta del archivo dentro del directorio estático.

      Guarde y cierre el archivo.

      Tras actualizar la página de índice de su aplicación, observará que el texto Welcome to FlaskBlog es ahora marrón, está centrado y se encuadra dentro de un borde.

      Puede utilizar el lenguaje CSS para dar estilo a la aplicación y hacer que sea más atractiva usando su propio diseño. Sin embargo, si no es un diseñador web, o no está familiarizado con CSS, puede usar el kit de herramientas Bootstrap, que ofrece componentes fáciles de usar para dar estilo a su aplicación. En este proyecto, usaremos Bootstrap.

      Quizá haya imaginado que crear otra plantilla HTML significará repetir la mayoría del código HTML que ya escribió en la plantilla index.html. Puede evitar la repetición innecesaria de código con la ayuda de un archivo de plantilla base, desde el cual heredarán todos sus archivos HTML. Consulte Herencia de plantillas en Jinja para obtener más información.

      Para crear una plantilla base, primero cree un archivo llamado base.html dentro de su directorio templates:

      Escriba el siguiente código en su plantilla base.html:

      flask_blog/templates/base.html

      <!doctype html>
      <html lang="en">
        <head>
          <!-- Required meta tags -->
          <meta charset="utf-8">
          <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
      
          <!-- Bootstrap CSS -->
          <link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/css/bootstrap.min.css" integrity="sha384-ggOyR0iXCbMQv3Xipma34MD+dH/1fQ784/j6cY/iJTQUOhcWr7x9JvoRxT2MZw1T" crossorigin="anonymous">
      
          <title>{% block title %} {% endblock %}</title>
        </head>
        <body>
          <nav class="navbar navbar-expand-md navbar-light bg-light">
              <a class="navbar-brand" href="https://www.digitalocean.com/{{ url_for('index')}}">FlaskBlog</a>
              <button class="navbar-toggler" type="button" data-toggle="collapse" data-target="#navbarNav" aria-controls="navbarNav" aria-expanded="false" aria-label="Toggle navigation">
                  <span class="navbar-toggler-icon"></span>
              </button>
              <div class="collapse navbar-collapse" id="navbarNav">
                  <ul class="navbar-nav">
                  <li class="nav-item active">
                      <a class="nav-link" href="https://www.digitalocean.com/#">About</a>
                  </li>
                  </ul>
              </div>
          </nav>
          <div class="container">
              {% block content %} {% endblock %}
          </div>
      
          <!-- Optional JavaScript -->
          <!-- jQuery first, then Popper.js, then Bootstrap JS -->
          <script src="https://code.jquery.com/jquery-3.3.1.slim.min.js" integrity="sha384-q8i/X+965DzO0rT7abK41JStQIAqVgRVzpbzo5smXKp4YfRvH+8abtTE1Pi6jizo" crossorigin="anonymous"></script>
          <script src="https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.14.7/umd/popper.min.js" integrity="sha384-UO2eT0CpHqdSJQ6hJty5KVphtPhzWj9WO1clHTMGa3JDZwrnQq4sF86dIHNDz0W1" crossorigin="anonymous"></script>
          <script src="https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/js/bootstrap.min.js" integrity="sha384-JjSmVgyd0p3pXB1rRibZUAYoIIy6OrQ6VrjIEaFf/nJGzIxFDsf4x0xIM+B07jRM" crossorigin="anonymous"></script>
        </body>
      </html>
      

      Guarde y cierre el archivo una vez que haya terminado de editarlo.

      La mayor parte del código en el bloque anterior es HTML estándar y el código necesario para Bootstrap. Las etiquetas <meta> proporcionan información para el navegador web, la etiqueta <link> enlaza los archivos CSS de Boostrap y las etiquetas <script> son enlaces a código JavaScript que permite algunas funciones adicionales de Boostrap; consulte la documentación de Boostrap para obtener más información.

      Sin embargo, las siguientes partes resaltadas son específicas para el motor de plantillas Jinja:

      • {% block title %} {% endblock %}: un bloque que sirve como marcador de posición para un título. Más adelante lo usará en otras plantillas para dar un título personalizado a cada página de su aplicación sin tener que reescribir toda la sección <head> cada vez.
      • {{ url_for('index')}: una invocación de función que devolverá la URL para la función de vista index(). Esto es diferente a la invocación anterior url_for() que utilizó para vincular a un archivo CSS estático, porque solo requiere un argumento, que es el nombre de la función de vista, y enlaza a la ruta asociada con la función en vez de con un archivo estático.
      • {% block content %} {% endblock %}: otro bloque que se sustituirá por contenido dependiendo de la plantilla secundaria (plantillas que heredan de base.html) que lo anulará.

      Ahora que tiene una plantilla base, puede aprovecharla usando la herencia. Abra el archivo index.html:

      • nano templates/index.html

      Sustituya su contenido con lo siguiente:

      flask_blog/templates/index.html

      {% extends 'base.html' %}
      
      {% block content %}
          <h1>{% block title %} Welcome to FlaskBlog {% endblock %}</h1>
      {% endblock %}
      

      En esta nueva versión de la plantilla index.html, utiliza la etiqueta {% extends %} para que herede de la plantilla base.html. Luego la extiende sustituyendo el bloque content en la plantilla base con lo que está dentro del bloque content en el bloque de código anterior.

      Este bloque content contiene una etiqueta <h1> con el texto Welcome to FlaskBlog dentro de un bloque title, que a su vez sustituye el bloque title original en la plantilla base.html con el texto Welcome to FlaskBlog. De esta forma, puede evitar repetir el mismo texto dos veces, ya que funciona como título para la página y como encabezado que aparece bajo la barra de navegación heredada de la plantilla base.

      La herencia de plantillas también le ofrece la capacidad de reutilizar el código HTML que tiene en otras plantillas (base.html en este caso), sin tener que repetirlo cada vez que sea necesario.

      Guarde y cierre el archivo y actualice la página de índice en su navegador. Verá su página con una barra de navegación y un título con estilo.

      Página de índice con Bootstrap

      Utilizó plantillas HTML y archivos estáticos en Flask. También ha utilizado Bootstrap para comenzar a refinar el aspecto de su página y una plantilla base para evitar la repetición del código. En el siguiente paso, configurará una base de datos que almacenará los datos de su aplicación.

      Paso 4: Configurar la base de datos

      En este paso, configurará una base de datos para almacenar los datos, es decir, las entradas del blog para su aplicación. También completará la base de datos con algunas entradas de ejemplo.

      Usará un archivo de base de datos SQLite para guardar sus datos porque el módulo sqlite3, que utilizaremos para interactuar con la base de datos, está disponible en la biblioteca Python. Para obtener más información sobre SQLite, consulte este tutorial.

      Primero, ya que los datos de SQLite se guardan en tablas y columnas, y ya que sus datos principalmente son entradas de blog, primero deberá crear una tabla llamada posts con las columnas necesarias. Creará un archivo .sql que contiene comandos SQL para crear la tabla posts con algunas columnas. A continuación usará este archivo para crear la base de datos.

      Abra un archivo llamado schema.sql dentro de su directorio flask_blog:

      Escriba los siguientes comandos SQL dentro de este archivo:

      flask_blog/schema.sql

      DROP TABLE IF EXISTS posts;
      
      CREATE TABLE posts (
          id INTEGER PRIMARY KEY AUTOINCREMENT,
          created TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
          title TEXT NOT NULL,
          content TEXT NOT NULL
      );
      

      Guarde y cierre el archivo.

      El primer comando SQL es DROP TABLE IF EXISTS posts;, esto elimina cualquier tabla que exista llamada posts para que no tenga un comportamiento confuso. Observe que esto eliminará todo el contenido que tenga en la base de datos siempre que utilice estos comandos SQL, de forma que asegúrese de no escribir ningún contenido importante en la aplicación web hasta que haya terminado con este tutorial y experimente con el resultado final. A continuación, CREATE TABLE posts se utiliza para crear la tabla posts con las siguientes columnas:

      • id: es un entero que representa una clave primaria. La base de datos asignará un valor único para cada entrada (es decir, una entrada blog).
      • created: la hora en que se creó la entrada de blog. NOT NULL significa que esta columna no debería estar vacía y el valor DEFAULT es el valor CURRENT_TIMESTAMP, que es la hora en la cual la entrada se añadió a la base de datos. Igual que el id, no necesita especificar un valor para esta columna, y que se completará automáticamente.
      • title: el título de la entrada.
      • content: el contenido de la entrada.

      Ahora que tiene un esquema SQL en el archivo schema.sql, lo usará para crear la base de datos usando un archivo Python que generará un archivo base de datos .db de SQLite. Abra un archivo llamado init_db.py dentro del directorio flask_blog usando su editor preferido:

      Y luego añada el siguiente código.

      flask_blog/init_db.py

      import sqlite3
      
      connection = sqlite3.connect('database.db')
      
      
      with open('schema.sql') as f:
          connection.executescript(f.read())
      
      cur = connection.cursor()
      
      cur.execute("INSERT INTO posts (title, content) VALUES (?, ?)",
                  ('First Post', 'Content for the first post')
                  )
      
      cur.execute("INSERT INTO posts (title, content) VALUES (?, ?)",
                  ('Second Post', 'Content for the second post')
                  )
      
      connection.commit()
      connection.close()
      

      Primero importe el módulo sqlite3 y luego abra una conexión con el archivo de la base de datos llamada database.db, que se creará una vez que ejecute el archivo Python. Luego utilice la función open() para abrir el archivo schema.sql. A continuación, ejecute su contenido utilizando el método executescript() que ejecuta múltiples instrucciones SQL a la vez, lo que creará la tabla posts. Crea un objeto Cursor que le permite usar su método execute() para ejecutar dos instrucciones SQL INSERT para permitir dos entradas de blog en su tabla posts. Finalmente, confirma los cambios y cierra la conexión.

      Guarde y cierre el archivo y a continuación ejecútelo en el terminal usando el comando python:

      Una vez que el archivo termine su ejecución, aparecerá un nuevo archivo llamado database.db en su directorio flask_blog. Esto significa que la configurado correctamente su base de datos.

      En el siguiente paso, recuperará las entradas que insertó en su base de datos y las mostrará en la página de inicio de su aplicación.

      Paso 5: Mostrar todas las entradas

      Ahora que ha configurado su base de datos, puede modificar la función de vista index() para mostrar todas las entradas que tiene en su base de datos.

      Abra el archivo app.py para realizar las siguientes modificaciones:

      Para su primera modificación, importará el módulo sqlite3 en la parte superior del archivo:

      flask_blog/app.py

      import sqlite3
      from flask import Flask, render_template
      
      . . .
      

      A continuación, creará una función que cree una conexión de base de datos y la devolverá. Añádala directamente tras las importaciones:

      flask_blog/app.py

      . . .
      from flask import Flask, render_template
      
      def get_db_connection():
          conn = sqlite3.connect('database.db')
          conn.row_factory = sqlite3.Row
          return conn
      
      . . .
      

      Esta función get_db_connection() abra una conexión con el archivo de base de datos database.db, y luego establece el atributo row_factory a sqlite3. Row para poder tener acceso basado en nombre a las columnas. Esto significa que la conexión con la base de datos devolverá filas que se comportan como diccionarios Python regulares. Por último, la función devuelve el objeto de conexión conn que usará para acceder a la base de datos.

      Tras definir la función get_db_connection, modifique la función index() para que tenga el siguiente aspecto:

      flask_blog/app.py

      . . .
      
      @app.route('/')
      def index():
          conn = get_db_connection()
          posts = conn.execute('SELECT * FROM posts').fetchall()
          conn.close()
          return render_template('index.html"https://www.digitalocean.com/, posts=posts)
      

      En esta nueva versión de la función index(), primero abre una conexión de base de datos usando la función get_db_connection() que definió antes. A continuación, ejecuta una consulta SQL para seleccionar todas las entradas de la tabla post. Implementa el método fetchall() para recuperar todas las filas del resultado de la consulta. Esto devolverá una lista de las entradas que insertó en la base de datos en el paso anterior.

      Cierra la conexión con la base de datos usando el método close() y devuelve el resultado de representar la plantilla index.html. También pasará el objeto posts como argumento, que contiene los resultados que obtuvo de la base de datos; esto le permitirá acceder a las entradas del blog en la plantilla index.html.

      Con estas modificaciones implementadas, guarde y cierre el archivo app.py.

      Ahora que ha pasado las entradas que recuperó de la base de datos a la plantilla index.html, puede usar un bucle for para mostrar cada entrada en su página de índice.

      Abra el archivo index.html:

      • nano templates/index.html

      A continuación, modifíquelo como sigue:

      flask_blog/templates/index.html

      {% extends 'base.html' %}
      
      {% block content %}
          <h1>{% block title %} Welcome to FlaskBlog {% endblock %}</h1>
          {% for post in posts %}
              <a href="https://www.digitalocean.com/#">
                  <h2>{{ post['title'] }}</h2>
              </a>
              <span class="badge badge-primary">{{ post['created'] }}</span>
              <hr>
          {% endfor %}
      {% endblock %}
      

      Aquí, la sintaxis {% for post in posts %} es un bucle for Jinja, que es similar a un bucle for Python, excepto que tiene que ser cerrado posteriormente con la sintaxis {% endfor %}. Utiliza esta sintaxis para realizar un bucle sobre cada elemento en la lista posts que se pasó por la función index() en la línea return render_template('index.html', posts=posts). Dentro de este bucle for, muestra el título de la entrada en un encabezado <h2> dentro de una etiqueta <a> (más tarde utilizará esta etiqueta para vincular con cada entrada individualmente).

      Muestra el título utilizando un delimitador variable literal ({{ ... }}). Recuerde que post será un objeto similar a un diccionario, para que pueda acceder al título de la entrada con post['title']​​​. También muestra la fecha de creación de la entrada usando el mismo método.

      Una vez que haya terminado de editar el archivo, guarde y ciérrelo. Luego navegue a la página de índice en su navegador. Verá las dos entradas que añadió a la base de datos en su página.

      Página de índice con entradas mostradas

      Ahora que ha modificado la función de vista index() para mostrar todas las entradas que tiene en la base de datos en la página de inicio de su aplicación, pasará a mostrar cada entrada en una única página y permitirá que los usuarios vinculen a cada entrada individual.

      Paso 6: Mostrar una entrada única

      En este paso, creará una nueva ruta Flask con una función de vista y una nueva plantilla HTML para mostrar una entrada de blog individual por su ID.

      Al final de este paso, la URL http://127.0.0.1:5000/1 será una página que muestra la primera entrada (porque tiene el ID 1). La URL http://127.0.0.1:5000/ID mostrará la entrada con el número ID asociado si existe.

      Abra app.py para su edición:

      Ya que necesitará obtener una entrada de blog por su ID de la base de datos en múltiples ubicación más adelante en este proyecto, creará una función independiente llamada get_post(). Puede invocarla pasándole un ID y recibiendo la entrada de blog asociada con el ID proporcionado, o hacer que Flask responsa con un mensaje 404 Not Found si la entrada de blog no existe.

      Para responder con una página 404, deberá importar la función abort() desde la biblioteca Wekzeug, que se instaló junto con Flask en la parte superior del archivo:

      flask_blog/app.py

      import sqlite3
      from flask import Flask, render_template
      from werkzeug.exceptions import abort
      
      . . .
      

      A continuación, añada la función get_post() justo tras la función get_db_connection() que creó en el paso anterior:

      flask_blog/app.py

      . . .
      
      def get_db_connection():
          conn = sqlite3.connect('database.db')
          conn.row_factory = sqlite3.Row
          return conn
      
      
      def get_post(post_id):
          conn = get_db_connection()
          post = conn.execute('SELECT * FROM posts WHERE id = ?',
                              (post_id,)).fetchone()
          conn.close()
          if post is None:
              abort(404)
          return post
      
      . . .
      

      Esta nueva función tiene el argumento post_id que determina qué entrada de blog recuperar.

      Dentro de la función, utiliza la función get_db_connection() para abrir una conexión de base de datos y ejecutar una consulta SQL para obtener la entada de blog asociada con el valor post_id dado. Añade el método fetchone() para obtener el resultado y almacenarlo en la variable post. Luego, cierre la conexión. Si la variable post tiene el valor None, lo que significa que no se ha encontrado ningún resultado en la base de datos, utiliza la función abort() que importó anteriormente para responder con un código de error 404 y la función terminará la ejecución. Si, sin embargo, se encuentra una entrada, devuelve el valor de la variable post.

      A continuación, añada la siguiente función de vista al final del archivo app.py:

      flask_blog/app.py

      . . .
      
      @app.route('/<int:post_id>')
      def post(post_id):
          post = get_post(post_id)
          return render_template('post.html', post=post)
      

      En esta nueva función de vista, añade una regla de variable <int:post_id> para especificar que la parte tras la barra (/) es un entero positivo (marcado con el conversor int) que necesita para acceder en su función de vista. Flask reconoce esto y pasa su valor al argumento de palabra clave post_id de su función de vista post(). A continuación, utiliza la función get_post() para obtener la entrada de blog asociada con el ID especificado y almacenar el resultado en la variable post, que pasa por una plantilla post.html que pronto creará.

      Guarde el archivo app.py y abra un nuevo archivo de plantilla post.html para su edición:

      Escriba el siguiente código en este nuevo archivo post.html. Esto será similar al archivo index.html, excepto que solo mostrará una única entrada además de mostrar el contenido de la entrada:

      flask_blog/templates/post.html

      {% extends 'base.html' %}
      
      {% block content %}
          <h2>{% block title %} {{ post['title'] }} {% endblock %}</h2>
          <span class="badge badge-primary">{{ post['created'] }}</span>
          <p>{{ post['content'] }}</p>
      {% endblock %}
      

      Añade el bloque title que definió en la plantilla base.html para hacer que el título de la página refleje el título de la entrada que se muestra en un encabezado <h2> al mismo tiempo.

      Guarde y cierre el archivo.

      Ahora puede navegar a la siguiente URL para ver las dos entradas que tiene en su base de datos, junto con una página que indica al usuario que no se encontró la entrada del blog solicitada (ya que no hay una entrada con el número de ID 3 hasta ahora):

      http://127.0.0.1:5000/1
      http://127.0.0.1:5000/2
      http://127.0.0.1:5000/3
      

      Volviendo a la página de índice, creará cada enlace al título de la entrada a su página respectiva. Hará esto usando la función url_for(). Primero, abra la plantilla index.html para su edición:

      • nano templates/index.html

      A continuación cambie el valor del atributo href de # a {{ url_for('post', post_id=post['id']) }} de forma que el bucle for tendrá el siguiente aspecto:

      flask_blog/templates/index.html

      {% for post in posts %}
          <a href="https://www.digitalocean.com/{{ url_for('post', post_id=post['id']) }}">
              <h2>{{ post['title'] }}</h2>
          </a>
          <span class="badge badge-primary">{{ post['created'] }}</span>
          <hr>
      {% endfor %}
      

      Aquí, pasa 'post' a la función url_for() como primer argumento. Este es el nombre de la función de vista post() y ya que acepta un argumento post_id, le da el valor post['id']. La función url_for() devolverá la URL adecuada para cada entrada según su ID.

      Guarde y cierre el archivo.

      Los enlaces en la página de índice ahora funcionarán como se espera. Con esto, ha terminado de crear la parte de la aplicación responsable de mostrar las entradas de blog en su base de datos. A continuación, añadirá la capacidad de crear, editar y eliminar entradas de blog en su aplicación.

      Paso 7: Modificar entradas

      Ahora que ha terminado de mostrar las entradas de blog que están presentes en la base de datos en la aplicación web, deberá permitir a los usuarios de su aplicación escribir nuevas entradas de blog y añadirlas a la base de datos, editar las existentes y eliminar las entradas de blog innecesarias.

      Crear una nueva entrada

      Hasta este momento, tiene una aplicación que muestra las entradas en su base de datos pero no ofrece ninguna forma de añadir una nueva entrada a menos que conecte directamente con la base de datos SQLite y añada una manualmente. En esta sección, creará una página sobre la cual podrá crear una entrada proporcionando su título y contenido.

      Abra el archivo app.py para su edición:

      Primero, importará lo siguiente desde el marco Flask:

      • El objeto global request para acceder a los datos de solicitud entrantes que se enviarán a través de un formato HTML.
      • La función url_for() para generar URLs.
      • La función flash() para mostrar un mensaje cuando se procesa una solicitud.
      • La función redirect() para redirigir al cliente a una ubicación diferente.

      Añada las importaciones a su archivo de la siguiente forma:

      flask_blog/app.py

      import sqlite3
      from flask import Flask, render_template, request, url_for, flash, redirect
      from werkzeug.exceptions import abort
      
      . . .
      

      La función flash() almacena los mensajes mostrados en la sesión del navegador del cliente, lo que requiere configurar una clave secreta. Esta clave secreta se utiliza para proteger las sesiones, lo que permite a Flask recordar la información de una solicitud a la otra, como pasar desde la nueva página de entrada a la página de índice. El usuario puede acceder a la información almacenada en la sesión, pero no puede modificarla a menos que tenga la clave secreta, de forma que nunca permita que nadie acceda a la clave secreta. Consulte la documentación de Flask para sesiones para obtener más información.

      Para establecer una_ clave_ secreta, añadirá una configuración SECRET_KEY a su aplicación a través del objeto app.config. Añádala directamente siguiendo la definición app antes de definir la función de vista index():

      flask_blog/app.py

      . . .
      app = Flask(__name__)
      app.config['SECRET_KEY'] = 'your secret key'
      
      
      @app.route('/')
      def index():
          conn = get_db_connection()
          posts = conn.execute('SELECT * FROM posts').fetchall()
          conn.close()
          return render_template('index.html', posts=posts)
      
      . . .
      

      Recuerde que la clave secreta debería ser una cadena aleatoria larga.

      Tras establecer una clave secreta, creará una función de vista que mostrará una plantilla que muestra un formulario que puede completar para crear una nueva entrada de blog. Añada esta nueva función en la parte inferior del archivo:

      flask_blog/app.py

      . . .
      
      @app.route('/create', methods=('GET', 'POST'))
      def create():
          return render_template('create.html')
      

      Esto crea una ruta /create que acepta las solicitudes GET y POST. Las solicitudes GET se aceptan por defecto. También acepta las solicitudes POST que se envía el navegador cuando se envían formularios. Pasará un tuple con los tipos de solicitudes aceptadas al argumento methods del decorador @app.route().

      Guarde y cierre el archivo.

      Para crear la plantilla, abra un archivo llamado create.html dentro de su carpeta templates:

      • nano templates/create.html

      Añada el siguiente código dentro de este nuevo archivo:

      flask_blog/templates/create.html

      {% extends 'base.html' %}
      
      {% block content %}
      <h1>{% block title %} Create a New Post {% endblock %}</h1>
      
      <form method="post">
          <div class="form-group">
              <label for="title">Title</label>
              <input type="text" name="title"
                     placeholder="Post title" class="form-control"
                     value="{{ request.form['title'] }}"></input>
          </div>
      
          <div class="form-group">
              <label for="content">Content</label>
              <textarea name="content" placeholder="Post content"
                        class="form-control">{{ request.form['content'] }}</textarea>
          </div>
          <div class="form-group">
              <button type="submit" class="btn btn-primary">Submit</button>
          </div>
      </form>
      {% endblock %}
      

      La mayor parte de este código es HTML estándar. Mostrará un cuadro de entrada para el título de la entrada, un área de texto para el contenido de la entrada y un botón para enviar el formulario.

      El valor de la entrada del título de la entrada es {{ request.form['title'] }},​​​ y el área de texto tiene el valor {{ request.form['content'] }}. Esto se hace para que los datos que introduce no se pierdan si algo sale mal. Por ejemplo, si escribe una entrada larga y olvida darle un título, se mostrará un mensaje que le informa de que el título es necesario. Esto sucederá sin perder la entrada que escribió ya que se almacenará en el objeto global request al que tiene acceso en sus plantillas.

      Ahora, con el servidor de desarrollo en ejecución, utilice su navegador para navegar a la ruta /create:

      http://127.0.0.1:5000/create
      

      Verá una página Create a New Post con un cuadro para un título y contenido:

      Crear una nueva página de entrada

      Este formulario envía una solicitud POST a su función de vista create(). Sin embargo, aún no hay ningún código para gestionar una solicitud POST en la función, de forma que no sucederá nada tras completar el formulario y enviarlo.

      Gestionará la solicitud POST entrante cuando se envía un formulario. Lo hará dentro de la función de vista create(). Puede gestionar por separado la solicitud POST comprobando el valor de request.method. Cuando su valor se establece a 'POST', significa que la solicitud es una solicitud POST. Luego extraerá los datos enviados, los validará y los insertará en su base de datos.

      Abra el archivo app.py para su edición:

      Modifique la función de vista create() para que tenga exactamente el siguiente aspecto:

      flask_blog/app.py

      . . .
      
      @app.route('/create', methods=('GET', 'POST'))
      def create():
          if request.method == 'POST':
              title = request.form['title']
              content = request.form['content']
      
              if not title:
                  flash('Title is required!')
              else:
                  conn = get_db_connection()
                  conn.execute('INSERT INTO posts (title, content) VALUES (?, ?)',
                               (title, content))
                  conn.commit()
                  conn.close()
                  return redirect(url_for('index'))
      
          return render_template('create.html')
      

      En la instrucción if asegura que el código que le sigue solo se ejecuta cuando la solicitud es una solicitud POST a través de la comparativa request.method == 'POST'.

      A continuación, extrae el título enviado y el contenido desde el objeto request.form que le proporciona acceso a los datos del formulario en la solicitud. Si no se proporciona el título, la condición if not title se cumplirá, mostrando un mensaje al usuario informándole de que el título es obligatorio. Si, por otro lado, se proporciona el título, abrirá una conexión con la función get_db_connection() e insertará el título y el contenido que recibió en la tabla posts.

      Luego confirma los cambios en la base de datos y cierra la conexión. Tras añadir la entrada de blog a la base de datos, redirige al cliente a la página de índice usando la función redirect() pasándole la URL generada por la función url_for() con el valor 'index' como argumento.

      Guarde y cierre el archivo.

      Ahora, navegue a la ruta /create usando su navegador web:

      http://127.0.0.1:5000/create
      

      Complete el formulario con un título y algo de contenido. Una vez que envíe el formulario, verá la nueva entrada listada en la página de índice.

      Por último, mostrará los mensajes generados y añadirá un enlace a la barra de navegación en la plantilla base.html para tener un acceso fácil a esta nueva página. Abra el archivo de plantilla:

      Edite el archivo añadiendo una nueva etiqueta <li> tras el enlace About dentro de la etiqueta <nav>. A continuación, añada un nuevo bucle for directamente sobre el bloque content para mostrar los mensajes generados bajo la barra de navegación. Estos mensajes están disponibles en la función especial get_flashed_messages() que Flask ofrece:

      flask_blog/templates/base.html

      <nav class="navbar navbar-expand-md navbar-light bg-light">
          <a class="navbar-brand" href="https://www.digitalocean.com/{{ url_for("index')}}">FlaskBlog</a>
          <button class="navbar-toggler" type="button" data-toggle="collapse" data-target="#navbarNav" aria-controls="navbarNav" aria-expanded="false" aria-label="Toggle navigation">
              <span class="navbar-toggler-icon"></span>
          </button>
          <div class="collapse navbar-collapse" id="navbarNav">
              <ul class="navbar-nav">
              <li class="nav-item">
                  <a class="nav-link" href="https://www.digitalocean.com/#">About</a>
              </li>
              <li class="nav-item">
                  <a class="nav-link" href="https://www.digitalocean.com/{{url_for("create')}}">New Post</a>
              </li>
              </ul>
          </div>
      </nav>
      <div class="container">
          {% for message in get_flashed_messages() %}
              <div class="alert alert-danger">{{ message }}</div>
          {% endfor %}
          {% block content %} {% endblock %}
      </div>
      

      Guarde y cierre el archivo. La barra de navegación ahora tendrá un elemento New Post que enlaza a la ruta /create.

      Editar una entrada

      Para que un blog esté actualizado, deberá poder editar sus entradas existentes. Esta sección le guiará en la creación de una nueva página en su aplicación para simplificar el proceso de editar una entrada.

      Primero, añadirá una nueva ruta al archivo app.py. Su función de vista recibirá el ID de la entrada que debe ser editada, la URL estará en el formato /post_id/edit con la variable post_id siendo el ID de la entrada. Abra el archivo app.py para su edición:

      A continuación, añada la siguiente función de vista edit() al final del archivo. Editar una entrada existente es similar a crear una nueva, de forma que esta función de vista será similar a la función de vista create():

      flask_blog/app.py

      . . .
      
      @app.route('/<int:id>/edit', methods=('GET', 'POST'))
      def edit(id):
          post = get_post(id)
      
          if request.method == 'POST':
              title = request.form['title']
              content = request.form['content']
      
              if not title:
                  flash('Title is required!')
              else:
                  conn = get_db_connection()
                  conn.execute('UPDATE posts SET title = ?, content = ?'
                               ' WHERE id = ?',
                               (title, content, id))
                  conn.commit()
                  conn.close()
                  return redirect(url_for('index'))
      
          return render_template('edit.html', post=post)
      

      La entrada que edita viene determinada por la URL y Flask pasará el número de ID a la función edit() a través del argumento id. Añade este valor a la función get_post() para recuperar la entrada asociada con el ID proporcionado desde la base de datos. Los nuevos datos vendrán en una solicitud POST, que se gestiona dentro de la condición if request.method == 'POST'.

      Igual que cuando creó una nueva entrada, primero extrae los datos del objeto request.form, luego muestra un mensaje si el título tiene un valor vacío, de lo contrario, abre una conexión con la base de datos. Luego actualiza la tabla posts estableciendo un nuevo título y nuevo contenido donde el ID de la entrada en la base de datos es igual al ID que estaba en la URL.

      En el caso de una solicitud GET, representa una plantilla edit.html pasando la variable post que alberga el valor devuelto de la función get_post(). Usará esto para mostrar el título existente y el contenido en la página de edición.

      Guarde y cierre el archivo, y cree una nueva plantilla edit.html:

      Escriba el siguiente código dentro de este nuevo archivo:

      flask_blog/templates/edit.html

      {% extends 'base.html' %}
      
      {% block content %}
      <h1>{% block title %} Edit "{{ post['title'] }}" {% endblock %}</h1>
      
      <form method="post">
          <div class="form-group">
              <label for="title">Title</label>
              <input type="text" name="title" placeholder="Post title"
                     class="form-control"
                     value="{{ request.form['title'] or post['title'] }}">
              </input>
          </div>
      
          <div class="form-group">
              <label for="content">Content</label>
              <textarea name="content" placeholder="Post content"
                        class="form-control">{{ request.form['content'] or post['content'] }}</textarea>
          </div>
          <div class="form-group">
              <button type="submit" class="btn btn-primary">Submit</button>
          </div>
      </form>
      <hr>
      {% endblock %}
      

      Guarde y cierre el archivo.

      Este código sigue el mismo patrón excepto por la sintaxis {{ request.form['title'] or post['title'] }} y {{ request.form['content'] or post['content'] }}. Esto muestra los datos guardados en la solicitud si existe, de lo contrario, muestra los datos de la variable post que se pasó a la plantilla que contiene los datos actuales de la base de datos.

      Ahora, navegue a la siguiente URL para editar la primera entrada:

      http://127.0.0.1:5000/1/edit
      

      Verá una página Edit “First Post”.

      Editar la página de una entrada

      Edite la entrada y envíe el formulario, luego asegúrese de que la entrada se ha actualizado.

      Ahora necesita añadir un enlace que apunte a la página de edición para cada entrada en la página de índice. Abra el archivo de plantilla index.html:

      • nano templates/index.html

      Edite el archivo para que tenga exactamente el siguiente aspecto:

      flask_blog/templates/index.html

      {% extends 'base.html' %}
      
      {% block content %}
          <h1>{% block title %} Welcome to FlaskBlog {% endblock %}</h1>
          {% for post in posts %}
              <a href="https://www.digitalocean.com/{{ url_for("post', post_id=post['id']) }}">
                  <h2>{{ post['title'] }}</h2>
              </a>
              <span class="badge badge-primary">{{ post['created'] }}</span>
              <a href="https://www.digitalocean.com/{{ url_for("edit', id=post['id']) }}">
                  <span class="badge badge-warning">Edit</span>
              </a>
              <hr>
          {% endfor %}
      {% endblock %}
      

      Guarde y cierre el archivo.

      Aquí añade una etiqueta <a> para enlazar a la función de vista edit(), pasando el valor post['id'] al enlace para editar la página de cada entrada con el enlace Edit.

      Eliminar una entrada

      A veces, no es necesario que una entrada siga estando disponible públicamente, y por eso la funcionalidad de eliminar una entrada es crucial. En este paso, añadirá la funcionalidad de eliminación a su aplicación.

      Primero, añadirá una nueva ruta /ID/delete que acepta solicitudes POST, de forma similar a la función de vista edit(). Su nueva función de vista delete() recibirá el ID de la entrada que será eliminada de la URL. Abra el archivo app.py:

      Añada la siguiente función de vista en la parte inferior del archivo:

      flask_blog/app.py

      # ....
      
      @app.route('/<int:id>/delete', methods=('POST',))
      def delete(id):
          post = get_post(id)
          conn = get_db_connection()
          conn.execute('DELETE FROM posts WHERE id = ?', (id,))
          conn.commit()
          conn.close()
          flash('"{}" was successfully deleted!'.format(post['title']))
          return redirect(url_for('index'))
      

      Esta función de vista solo acepta solicitudes POST. Esto significa que navegar a la ruta /ID/delete en su navegador devolverá un error porque los navegadores web van de forma predeterminada a las solicitudes GET.

      Sin embargo, puede acceder a esta ruta mediante un formulario que envía una solicitud POST pasando el ID de la entrada que desea eliminar. La función recibirá el valor de ID y lo usará para obtener la entrada de la base de datos con la función get_post().

      A continuación, abrirá una conexión de base de datos y ejecutará un comando SQL DELETE FROM para eliminar la entrada. Confirma el cambio en la base de datos y cierra la conexión mientras muestra un mensaje para informar al usuario de que la entrada se eliminó correctamente y redirigirlo a la página de índice.

      Observe que no reproduce un archivo de plantilla. Esto es porque añadirá un botón Delete a la página de edición.

      Abra el archivo de plantilla edit.html:

      Luego, añada la siguiente etiqueta <form> tras la etiqueta <hr> y directamente antes de la línea {% endblock %}:

      flask_blog/templates/edit.html

      <hr>
      
      <form action="https://www.digitalocean.com/{{ url_for("delete', id=post['id']) }}" method="POST">
          <input type="submit" value="Delete Post"
                  class="btn btn-danger btn-sm"
                  onclick="return confirm('Are you sure you want to delete this post?')">
      </form>
      
      {% endblock %}
      

      Utiliza el método confirm() para mostrar un mensaje de confirmación antes de enviar la solicitud.

      Ahora, navegue de nuevo a la página de edición de una entrada de blog e intente eliminarla:

      http://127.0.0.1:5000/1/edit
      

      Al final de este paso, el código fuente de su proyecto tendrá el aspecto del código en esta página.

      Con esto, los usuarios de su aplicación pueden escribir nuevas entradas de blog y añadirlas a la base de datos, editar entradas y eliminar las entradas existentes.

      Conclusión

      Este tutorial ha introducido los conceptos esenciales del marco Flask Python. Ha aprendido a crear una pequeña aplicación web, ejecutarla en un servidor de desarrollo y a permitir al usuario proporcionar datos personalizados a través de parámetros URL y formularios web. También utilizó el motor de plantillas Jinja para reutilizar los archivos HTMP y usar lógica en ellos. Al final de este tutorial, tendrá un blog web completamente funcional que interactúa con una base de datos SQLite para crear, mostrar, editar y eliminar entradas de blog usando el lenguaje Python y las consultas SQL.

      Puede seguir desarrollando esta aplicación añadiendo autenticación del usuario de forma que solo los usuarios registrados puedan crear o modificar entradas de blog. También puede añadir comentarios y etiquetas para cada entrada de blog, y añadir subidas de archivos para dar a los usuarios la capacidad de incluir imágenes en la entrada del blog. Consulte la documentación de Flask para obtener más información.

      Flask cuenta con muchas extensiones Flask creadas por la comunidad. A continuación verá una lista de extensiones que quizá quiera considerar usar para que su proceso de desarrollo sea más fácil:

      • Flask-Login: gestiona la sesión del usuario y el inicio y cierre de sesión, además de recordar a los usuarios con sesión iniciada.
      • Flask-SQLAlchemy: simplifica el uso de Flask con SQLAlchemy, un kit de herramientas Python SQL y un Asignador relacional de objetos para interactuar con bases de datos SQL.
      • Flask-Mail: ayuda con la tarea de enviar mensajes de correo electrónico en su aplicación Flask.



      Source link

      Cómo agregar Sidekiq y Redis a una aplicación de Ruby on Rails


      Introducción

      Al desarrollar una aplicación de Ruby on Rails, es posible que encuentre tareas de aplicación que se deben realizar de forma asíncrona. El procesamiento de datos, el envío de correos electrónicos por lotes o la interacción con API externas son ejemplos de trabajo que se pueden realizar de forma asincrónica con tareas en segundo plano. Con tareas en segundo plano, puede mejorar el rendimiento de su aplicación descargando tareas que pueden demandar gran cantidad de tiempo a una cola de procesamiento en segundo plano y, así, liberar el ciclo de solicitud o respuesta original

      Sidekiq es uno de los marcos de tareas en segundo plano más utilizados que se pueden implementar en una aplicación de Rails. Cuenta con el respaldo de Redis, un sistema de almacenamiento de clave-valor en memoria, conocido por su flexibilidad y rendimiento. Sidekiq utiliza Redis como almacén de administración de tareas para procesar miles de tareas por segundo.

      En este tutorial, agregaremos Redis y Sidekiq a una aplicación de Rails existente. Crearemos un conjunto de clases y métodos de trabajadores de Sidekiq para gestionar lo siguiente:

      • Una carga en lote de información sobre tiburones en peligro a la base de datos de aplicación desde un archivo CSV en el repositorio del proyecto.
      • La eliminación de estos datos.

      Cuando haya terminado, tendrá una aplicación de muestra que utilizará trabajadores y tareas para procesar tareas de forma asíncrona. Esta será una buena base para añadir trabajadores y trabajos a su aplicación utilizando este tutorial como punto de partida.

      Requisitos previos

      Para este tutorial, necesitará lo siguiente:

      Paso 1: Clonar el proyecto e instalar dependencias

      Nuestro primer paso será clonar el repositorio de rails-bootstrap de la cuenta de GitHub comunitaria de DigitalOcean. En este repositorio se incluye el código de la configuración descrita en el artículo Cómo agregar Bootstrap a una aplicación de Ruby on Rails, en el que se explica la forma de añadir Bootstrap a un proyecto de Rails 5 existente.

      Clone el repositorio en un directorio llamado rails-sidekiq:

      • git clone https://github.com/do-community/rails-bootstrap.git rails-sidekiq

      Diríjase al directorio rails-sidekiq:

      Para trabajar con el código, primero deberá instalar las dependencias del proyecto que se enumeran en el Gemfile de este. También deberá añadir la gema sidekiq al proyecto para trabajar con Sidekiq y Redis.

      Abra el archivo de Gemfile del proyecto para editarlo con nano o su editor favorito:

      Añada la gema en cualquier punto de las dependencias del proyecto principal (encima de las dependencias de desarrollo):

      ~/rails-sidekiq/Gemfile

      . . .
      # Reduces boot times through caching; required in config/boot.rb
      gem 'bootsnap', '>= 1.1.0', require: false
      gem 'sidekiq', '~>6.0.0'
      
      group :development, :test do
      . . .
      

      Guarde y cierre el archivo cuando termine de añadir la gema.

      Utilice el siguiente comando para instalar las gemas:

      Veremos en el resultado que la gema de redis también se instala como requisito para sidekiq.

      Luego, instalará sus dependencias de Yarn. Debido a que este proyecto de Rails 5 se modificó para proporcionar recursos con webpack, Yarn ahora gestiona sus dependencias de JavaScript. Esto significa que es necesario instalar y verificar las dependencias que se enumeran en el archivo package.json del proyecto.

      Ejecute yarn install para instalar estas dependencias:

      Luego, ejecute las migraciones de su base de datos:

      Una vez que hayan terminado sus migraciones, puede probar la aplicación para asegurarse de que funcione como se espera. Inicie su servidor en el contexto de su paquete local con el siguiente comando si trabaja a nivel local:

      Si trabaja en un servidor de desarrollo, puede iniciar la aplicación con lo siguiente:

      • bundle exec rails s --binding=your_server_ip

      Diríjase a localhost:3000 o http://your_server_ip:3000. Visualizará la siguiente página de destino:

      Página de inicio de la aplicación

      Para crear un nuevo tiburón, haga clic en el botón Get Shark Info, que lo dirigirá a la ruta sharks/index:

      Ruta del índice de tiburones

      Para verificar que la aplicación funcione, podemos añadirle información de prueba. Haga clic en New Shark. Gracias a la configuración de autenticación del proyecto, se le solicitará un nombre de usuario (sammy) y una contraseña (tiburón).

      En la página New Shark, ingrese “Gran tiburón blanco” en el campo Name y “Terrorífico” en el campo Facts.

      Creación de un tiburón

      Haga clic en el botón Create Shark para crear el tiburón. Una vez que haya creado el tiburón, podrá detener el servidor con CTRL+C.

      Con esto, habrá instalado las dependencias necesarias para su proyecto y comprobado que funciona. Luego, puede realizar algunos cambios en la aplicación de Rails para trabajar con sus recursos relacionados con los tiburones en peligro.

      Paso 2: Generar un controlador para recursos relacionados con tiburones en peligro

      Para trabajar con nuestros recursos relacionados con tiburones en peligro, añadiremos un nuevo modelo a la aplicación y un controlador que regulará la manera en que se presente a los usuarios la información sobre tiburones en peligro. Nuestro objetivo final es permitir que los usuarios carguen un gran lote de información sobre tiburones en peligro, sin bloquear la función general de nuestra aplicación, y eliminen dicha información cuando ya no la necesiten.

      Primero, crearemos un modelo Endangered para nuestros tiburones en peligro. Incluiremos un campo de cadena a la tabla de nuestra base de datos para el nombre del tiburón y otro campo de cadena para las categorías de la Unión Internacional para la Conservación de la Naturaleza (UICN) que determinan el grado de riesgo en el que se encuentra cada tiburón.

      En última instancia, la estructura de nuestro modelo coincidirá con las columnas del archivo CSV que usaremos para crear nuestra carga en lote. Este archivo se encuentra en el directorio db y puede verificar su contenido con el siguiente comando:

      El archivo contiene una lista de 73 tiburones en peligro y sus situaciones según la IUCN: vu significa vulnerable, en significa en peligro y cr significa en peligro crítico.

      Nuestro modelo Endangered se correlacionará con estos datos, lo que nos permitirá crear nuevas instancias de Endangered desde este archivo CSV. Cree el modelo con el siguiente comando:

      • rails generate model Endangered name:string iucn:string

      Luego, genere un controlador Endangered con una acción index:

      • rails generate controller endangered index

      Esto nos proporcionará un punto de partida para crear la función de nuestra aplicación, aunque también tendremos que añadir métodos personalizados al archivo del controlador que Rails generó para nosotros.

      Abra el archivo ahora:

      • nano app/controllers/endangered_controller.rb

      Rails nos proporciona un esquema de borrador que podemos comenzar a completar.

      Primero, debemos determinar las rutas que necesitamos para trabajar con nuestros datos. Gracias al comando generate controller​​​​​​, contamos con un mé todo index para comenzar. Esto se correlacionará con una vista de index, en la que presentaremos a los usuarios la opción para cargar tiburones en peligro.

      Sin embargo, también nos convendrá tratar los casos en los que los usuarios ya hayan cargado tiburones; no necesitarán una opción de carga en este caso. De alguna forma, tendremos que evaluar la cantidad existente de casos de la clase Endangered, puesto que más de una indica que ya se produjo la carga del lote.

      Empecemos creando un método set_endangered private​​​ que tomará cada instancia de nuestra clase Endangered de la base de datos. Añada el siguiente código al archivo:

      ~/rails-sidekiq/app/controllers/endangered_controller.rb

      class EndangeredController < ApplicationController
        before_action :set_endangered, only: [:index, :data]
      
        def index
        end
      
        private
      
          def set_endangered
            @endangered = Endangered.all
          end
      
      end
      

      Tenga en cuenta que el filtro before_action garantizará que el valor de @endangered solo esté configurado para las rutas index y data, donde gestionaremos los datos de tiburones en peligro.

      Después, añada el siguiente código al método index a fin de determinar la ruta correcta para los usuarios que visitan esta parte de la aplicación:

      ~/rails-sidekiq/app/controllers/endangered_controller.rb

      class EndangeredController < ApplicationController
        before_action :set_endangered, only: [:index, :data]
      
        def index          
          if @endangered.length > 0
            redirect_to endangered_data_path
          else
            render 'index'
          end
        end
      . . .
      

      Si hay más de 0 instancias de nuestra clase Endangered, redirigiremos a los usuarios a la ruta data, donde podrán ver información sobre los tiburones que crearon. De lo contrario, verán la vista de index.

      Luego, debajo del método index, añada un método data, que se correlacionará con una vista de data:

      ~/rails-sidekiq/app/controllers/endangered_controller.rb

      . . .
        def index          
          if @endangered.length > 0
            redirect_to endangered_data_path
          else
            render 'index'
          end
        end
      
        def data
        end
      . . .
      

      A continuación, añadiremos un método para gestionar la carga de datos. Llamaremos a este método upload e invocará una clase y método de trabajador Sidekiq para realizar la carga de datos desde el archivo CSV. En el siguiente paso, crearemos la definición de esta clase de trabajador: AddEndangeredWorker.

      Por ahora, añada el siguiente código al archivo para invocar al trabajador Sidekiq y realizar la carga:

      ~/rails-sidekiq/app/controllers/endangered_controller.rb

      . . .
        def data
        end
      
        def upload
          csv_file = File.join Rails.root, 'db', 'sharks.csv'   
          AddEndangeredWorker.perform_async(csv_file)
          redirect_to endangered_data_path, notice: 'Endangered sharks have been uploaded!'
        end
      . . .
      

      Al invocar el método perform_async en la clase AddEndangeredWorker usando el archivo CSV como argumento, este código garantiza que los datos de tiburones y la tarea de carga se transfieran a Redis. Los trabajadores Sidekiq que configuraremos monitorean la cola de trabajo y responderán cuando surjan nuevos trabajo.

      Después de invocar perform_async, nuestro método upload​​​ aplica redireccionamiento a la ruta data, en la que los usuarios podrán ver los tiburones cargados.

      Luego, añadiremos un método destroy para destruir los datos. Añada el siguiente código que está debajo del método upload:

      ~/rails-sidekiq/app/controllers/endangered_controller.rb

      . . .
        def upload
          csv_file = File.join Rails.root, 'db', 'sharks.csv'   
          AddEndangeredWorker.perform_async(csv_file)
          redirect_to endangered_data_path, notice: 'Endangered sharks have been uploaded!'
        end
      
        def destroy
          RemoveEndangeredWorker.perform_async
          redirect_to root_path
        end
      . . .
      

      Al igual que nuestro método upload, nuestro método destroy incluye la invocación de perform_async en una clase RemoveEndangeredWorker: el otro trabajador Sidekiq que crearemos. Después de invocar este método, redirecciona a los usuarios a la ruta de la aplicación root.

      El archivo terminado tendrá este aspecto:

      ~/rails-sidekiq/app/controllers/endangered_controller.rb

      class EndangeredController < ApplicationController
        before_action :set_endangered, only: [:index, :data]
      
        def index          
          if @endangered.length > 0
            redirect_to endangered_data_path
          else
            render 'index'
          end
        end
      
        def data
        end
      
        def upload
          csv_file = File.join Rails.root, 'db', 'sharks.csv'   
          AddEndangeredWorker.perform_async(csv_file)
          redirect_to endangered_data_path, notice: 'Endangered sharks have been uploaded!'
        end
      
        def destroy
          RemoveEndangeredWorker.perform_async
          redirect_to root_path
        end
      
        private
      
          def set_endangered
            @endangered = Endangered.all
          end
      
      end
      

      Guarde y cierre el archivo cuando concluya la edición.

      Como paso final para afianzar las rutas de nuestra aplicación, modificaremos el código de config/routes.rb, el archivo en el que residen nuestras instrucciones de ruta.

      Abra el archivo ahora:

      El archivo actualmente tiene este aspecto:

      ~/rails-sidekiq/config/routes.rb

      Rails.application.routes.draw do
        get 'endangered/index'
        get 'home/index'
        resources :sharks do
                resources :posts
        end
        root 'home#index'
        # For details on the DSL available within this file, see http://guides.rubyonrails.org/routing.html
      end
      

      Será necesario actualizar el archivo para incluir las rutas que definimos en nuestro controlador: data, upload y destroy. Nuestra ruta data coincidirá con una solicitud GET para obtener los datos de tiburones, mientras que nuestras rutas upload y destroy se asignarán a las solicitudes POST que cargan y destruyen esos datos.

      Añada el siguiente código al archivo para definir estas rutas:

      ~/rails-sidekiq/config/routes.rb

      Rails.application.routes.draw do
        get 'endangered/index'
        get 'endangered/data', to: 'endangered#data'
        post 'endangered/upload', to: 'endangered#upload'
        post 'endangered/destroy', to: 'endangered#destroy'
        get 'home/index'
        resources :sharks do
                resources :posts
        end
        root 'home#index'
        # For details on the DSL available within this file, see http://guides.rubyonrails.org/routing.html
      end
      

      Guarde y cierre el archivo cuando concluya la edición.

      Con su modelo y controlador Endangered, ahora puede proceder a definir sus clases de trabajadores Sidekiq.

      Paso 3: Definir trabajadores Sidekiq

      Invocamos los métodos perform_async en nuestros trabajadores Sidekiq de nuestro controlador, pero aún debemos crear los trabajadores.

      Primero, cree un directorio workers para los trabajadores:

      Abra un archivo para el trabajador AddEndangeredWorker:

      • nano app/workers/add_endangered_worker.rb

      En este archivo, añadiremos el código que nos permitirá trabajar con los datos de nuestro archivo CSV. Primero, añada el código al archivo que creará la clase, incluya la biblioteca CSV Ruby y asegúrese que esta clase funcione como trabajador Sidekiq:

      ~/rails-sidekiq/app/workers/add_endangered_worker.rb

      class AddEndangeredWorker
        require 'csv'
        include Sidekiq::Worker
        sidekiq_options retry: false
      
      end
      

      También incluiremos la opción retry:false para garantizar que Sidekiq no vuelva a intentar realizar la carga ante una falla.

      Luego, añada el código para la función perform:

      ~/rails-sidekiq/app/workers/add_endangered_worker.rb

      class AddEndangeredWorker
        require 'csv'
        include Sidekiq::Worker
        sidekiq_options retry: false
      
        def perform(csv_file)
          CSV.foreach(csv_file, headers: true) do |shark|
          Endangered.create(name: shark[0], iucn: shark[1])
        end
       end
      
      end
      

      El método perform recibe los argumentos del método perform_async definido en el controlador, por lo que es importante que los valores de argumento estén nivelados. Aquí, pasamos en csv_file, la variable que definimos en el controlador y usamos el método foreach de la biblioteca CSV para leer los valores del archivo. Configurar headers:true para este bucle garantiza que se trate a la primera fila del archivo como a una fila de encabezados.

      Luego, el bloque luego lee los valores del archivo en las columnas que configuramos para nuestro modelo Endangered: name e iucn. Ejecutar este bucle creará instancias de Endangered para cada una de las entradas de nuestro archivo CSV.

      Una vez que finalice la edición, guarde y cierre el archivo.

      Luego, crearemos un trabajador para que elimine estos datos. Abra un archivo para la clase RemoveEndangeredWorker:

      • nano app/workers/remove_endangered_worker.rb

      Añada el código para definir la clase y para garantizar que utilice la biblioteca CSV y las funciones como un trabajador Sidekiq:

      ~/rails-sidekiq/app/workers/remove_endangered_worker.rb

      class RemoveEndangeredWorker
        include Sidekiq::Worker
        sidekiq_options retry: false
      
      end
      

      Luego, añada un método perform para gestionar la destrucción de los datos de tiburones en peligro:

      ~/rails-sidekiq/app/workers/remove_endangered_worker.rb

      class RemoveEndangeredWorker
        include Sidekiq::Worker
        sidekiq_options retry: false
      
        def perform
          Endangered.destroy_all
        end
      
      end
      

      El método perform invoca a destroy_all en la clase Endangered, que eliminará todas las instancias de esa clase de la base de datos.

      Guarde y cierre el archivo cuando concluya la edición.

      Una vez que sus trabajadores estén activos, podrá proceder a crear un diseño para sus vistas de endangered, así como plantillas para sus vistas de index y data, para que los usuarios puedan cargar y ver tiburones en peligro.

      Paso 4: Agregar diseños y plantillas de vistas

      Para que los usuarios disfruten de su propia información de tiburones en peligro, tendremos que abordar dos aspectos: el diseño de las vistas definidas en nuestro controlador endangered y las plantillas de vista de las vistas de index y data.

      Actualmente, nuestra aplicación utiliza un diseño integral, ubicado en app/views/layouts/application.html.erb, uno parcial de navegación y uno para las vistas de sharks. El diseño de la aplicación busca un bloque de contenido, que nos permite cargar diferentes diseños basados en la parte de la aplicación con la que nuestro usuario interactúa: para la página home​​​ index, este verá un diseño y para cualquier vista que se relacione con tiburones individuales verá otro.

      Podemos reutilizar el diseño de sharks para nuestras vistas de endangered, ya que este formato también funcionará para presentar datos de tiburones de forma masiva.

      Copie el archivo de diseño de sharks para crear un diseño de endangered:

      • cp app/views/layouts/sharks.html.erb app/views/layouts/endangered.html.erb

      Luego, trabajaremos en crear las plantillas de vista para nuestras vistas de index y data.

      Abra la plantilla index primero:

      • nano app/views/endangered/index.html.erb

      Elimine el código estándar y añada en su lugar el siguiente código, que proporcionará a los usuarios información general sobre las categorías en peligro y les dará la opción de cargar información sobre tiburones en peligro:

      ~/rails-sidekiq/app/views/endangered/index.html.erb

      <p id="notice"><%= notice %></p>
      
      <h1>Endangered Sharks</h1>
      
      <p>International Union for Conservation of Nature (ICUN) statuses: <b>vu:</b> Vulnerable, <b>en:</b> Endangered, <b>cr:</b> Critically Endangered </p>
      
      <br>
      
        <%= form_tag endangered_upload_path do %>
        <%= submit_tag "Import Endangered Sharks" %>
        <% end %>
      
        <br>
      
      <%= link_to 'New Shark', new_shark_path, :class => "btn btn-primary btn-sm" %> <%= link_to 'Home', home_index_path, :class => "btn btn-primary btn-sm" %>
      

      Un form_tag hace posible la carga de datos al apuntar una acción posterior a endangered_upload_path, la ruta que definimos para nuestras cargas. El botón de envío, creado con submit_tag, solicita a los usuarios a “Import Endangered Sharks”.

      Además de este código, incluimos información general sobre códigos de ICUN, para que los usuarios puedan interpretar los datos que verán.

      Guarde y cierre el archivo cuando concluya la edición.

      Luego, abra un archivo para la vista de data:

      • nano app/views/endangered/data.html.erb

      Añada el siguiente código, que agregará una tabla con los datos de tiburones en peligro:

      ~/rails-sidekiq/app/views/endangered/data.html.erb

      <p id="notice"><%= notice %></p>
      
      <h1>Endangered Sharks</h1>
      
      <p>International Union for Conservation of Nature (ICUN) statuses: <b>vu:</b> Vulnerable, <b>en:</b> Endangered, <b>cr:</b> Critically Endangered </p>
      
      <div class="table-responsive">
      <table class="table table-striped table-dark">
        <thead>
          <tr>
            <th>Name</th>
            <th>IUCN Status</th>
            <th colspan="3"></th>
          </tr>
        </thead>
      
        <tbody>
          <% @endangered.each do |shark| %>
            <tr>
              <td><%= shark.name %></td>
              <td><%= shark.iucn %></td>
            </tr>
          <% end %>
        </tbody>
      </table>
      </div>
      
      <br>
      
        <%= form_tag endangered_destroy_path do %>
        <%= submit_tag "Delete Endangered Sharks" %>
        <% end %>
      
        <br>
      
      <%= link_to 'New Shark', new_shark_path, :class => "btn btn-primary btn-sm" %> <%= link_to 'Home', home_index_path, :class => "btn btn-primary btn-sm" %>
      

      Este código incluye de nuevo los códigos de estado de la ICUN y una tabla de Bootstrap para los datos emitidos. Al recorrer nuestra variable @endangered, mostramos el nombre y la situación de cada tiburón según la ICUN en la tabla.

      Debajo de la tabla, tenemos otro conjunto de form_tags y submit_tags, que se publican a la ruta destroy ofreciendo a los usuarios la opción de "Delete Endangered Sharks”.

      Guarde y cierre el archivo cuando concluya la edición.

      La última modificación que aplicaremos a nuestras vistas se hará en la vista de index asociada con nuestro controlador home. Como podrá recordar, esta vista se configura como root de la aplicación en config/routes.rb.

      Abra este archivo para editarlo:

      • nano app/views/home/index.html.erb

      Encuentre la columna en la fila Sharks are ancient:

      ~/rails-sidekiq/app/views/home/index.html.erb

      . . .
              <div class="col-lg-6">
                  <h3>Sharks are ancient</h3>
                  <p>There is evidence to suggest that sharks lived up to 400 million years ago.
                  </p>
              </div>
          </div>
      </div>
      

      Añada el siguiente código al archivo:

      ~/rails-sidekiq/app/views/home/index.html.erb

      . . .
              <div class="col-lg-6">
                  <h3>Sharks are ancient and SOME are in danger</h3>
                  <p>There is evidence to suggest that sharks lived up to 400 million years ago. Without our help, some could disappear soon.</p>
                  <p><%= button_to 'Which Sharks Are in Danger?', endangered_index_path, :method => :get,  :class => "btn btn-primary btn-sm"%>
                  </p>
              </div>
          </div>
      </div>
      

      Incluimos un llamado a la acción para que los usuarios aprendan más sobre tiburones en peligro compartiendo, primero, un mensaje fuerte y, luego, añadiendo un asistente button_to que envía una solicitud GET a nuestra ruta index endangered y permite que los usuarios accedan a esa parte de la aplicación. Desde allí, podrán cargar y ver información de tiburones en peligro.

      Guarde y cierre el archivo cuando concluya la edición.

      Una vez que esté listo su código, estará preparado para iniciar la aplicación y cargar algunos tiburones.

      Paso 5: Iniciar Sidekiq y probar la aplicación

      Antes de iniciar la aplicación, necesitaremos ejecutar migraciones en nuestra base de datos e iniciar Sidekiq para habilitar nuestros trabajadores. Redis ya se debería estar ejecutando en el servidor, pero podemos verificarlo para estar seguros. Una vez que hagamos todo esto, estaremos listos para probar la aplicación.

      Primero, verifique que Redis esté en ejecución:

      Debería ver un resultado como el siguiente:

      Output

      ● redis-server.service - Advanced key-value store Loaded: loaded (/lib/systemd/system/redis-server.service; enabled; vendor preset: enabled) Active: active (running) since Tue 2019-11-12 20:37:13 UTC; 1 weeks 0 days ago

      Luego, ejecute las migraciones de su base de datos:

      Ahora puede iniciar Sidekiq en el contexto de su paquete de proyectos actual usando el comando bundle exec sidekiq:

      Verá un resultado como este, que indica que Sidekiq está listo para procesar tareas:

      Output

      m, `$b .ss, $$: .,d$ `$$P,d$P' .,md$P"' ,$$$$$b/md$$$P^' .d$$$$$$/$$$P' $$^' `"/$$$' ____ _ _ _ _ $: ,$$: / ___|(_) __| | ___| | _(_) __ _ `b :$$ ___ | |/ _` |/ _ |/ / |/ _` | $$: ___) | | (_| | __/ <| | (_| | $$ |____/|_|__,_|___|_|__|__, | .d$$ |_| 2019-11-19T21:43:00.540Z pid=17621 tid=gpiqiesdl INFO: Running in ruby 2.5.1p57 (2018-03-29 revision 63029) [x86_64-linux] 2019-11-19T21:43:00.540Z pid=17621 tid=gpiqiesdl INFO: See LICENSE and the LGPL-3.0 for licensing details. 2019-11-19T21:43:00.540Z pid=17621 tid=gpiqiesdl INFO: Upgrade to Sidekiq Pro for more features and support: http://sidekiq.org 2019-11-19T21:43:00.540Z pid=17621 tid=gpiqiesdl INFO: Booting Sidekiq 6.0.3 with redis options {:id=>"Sidekiq-server-PID-17621", :url=>nil} 2019-11-19T21:43:00.543Z pid=17621 tid=gpiqiesdl INFO: Starting processing, hit Ctrl-C to stop

      Abra una segunda ventana de terminal, diríjase al directorio rails-sidekiq e inicie el servidor de aplicaciones.

      Si ejecuta la aplicación a nivel local, utilice el siguiente comando:

      Si trabaja con un servidor de desarrollo, ejecute lo siguiente:

      • bundle exec rails s --binding=your_server_ip

      Diríjase a localhost:3000 o a http://your_server_ip:3000 en el navegador. Visualizará la siguiente página de destino:

      Inicio de la aplicación de Sidekiq

      Haga clic en el botón** ¿Which Sharks Are In Danger?** . Debido a que no cargó ningún tiburón en peligro, esto lo dirigirá a la vista de endangered index:

      Vista del índice de tiburones en peligro

      Haga clic en Import Endangered Sharks para importar los tiburones. Verá un mensaje de estado que le indicará que los tiburones se importaron:

      Inicio de la importación

      También verá el comienzo de la importación. Actualice su página para ver la tabla entera:

      Actualización de tabla

      Gracias a Sidekiq, realizamos la carga del gran lote de tiburones en peligro sin cerrar el navegador ni interferir con otras funciones de la aplicación.

      Haga clic en el botón Home en la parte inferior de la página, que lo llevará de vuelta a la página principal de la aplicación:

      Inicio de la aplicación de Sidekiq

      Aquí, haga clic en Which Sharks Are in Danger? nuevamente. Esto lo llevará directamente a la vista de data, porque ya había cargado los tiburones.

      Para probar la función de eliminación, haga clic en el botón Delete Endangered Sharks que se encuentra debajo de la tabla. Una vez más, debería ser redireccionado a la página de inicio de la aplicación. Si have clic en Which Sharks Are in Danger? una última vez, lo llevará de regreso a la vista de index, en la que tendrá la opción de cargar tiburones de nuevo:

      Vista del índice de tiburones en peligro

      Su aplicación ahora se está ejecutando con los trabajadores de Sidekiq, que están listos para procesar trabajos y garantizar que los usuarios tengan una buena experiencia trabajando con su aplicación.

      Conclusión

      Ahora dispone de una aplicación Rails en funcionamiento con Sidekiq habilitado, que le permitirá descargar operaciones complejas a una cola de trabajo administrada por Sidekiq y respaldada por Redis. Esto le permitirá mejorar la velocidad y la funcionalidad de su sitio a medida que realice el desarrollo.

      Si desea obtener más información sobre Sidekiq, se recomienda comenzar consultando los docs.

      Para obtener más información sobre Redis, consulte nuestra biblioteca de recursos de Redis. También puede obtener más información sobre cómo ejecutar un clúster de Redis administrado en DigitalOcean consultando la documentación del producto.



      Source link

      Cómo crear una aplicación de citas inspiradoras con AdonisJs y MySQL


      El autor seleccionó el Tech Education Fund para que recibiese una donación como parte del programa Write for DOnations.

      Introducción

      AdonisJs es un marco web simple de Node.js escrito en JavaScript que funciona en los principales sistemas operativos. Utiliza el popular patrón de diseño MVC (modelo-vista-controlador) y ofrece un ecosistema estable para escribir aplicaciones web del lado del servidor. El marco presenta características de autenticación sin inconvenientes, SQL ORM (mapeo de objetos a datos relacionales), migraciones y propagación de bases de datos. AdonisJs tiene una arquitectura similar a la del marco PHP de aplicaciones web Laravel e incluye la misma estructura de carpeta y varios conceptos de configuración compartidos.

      Por defecto, AdonisJs usa el motor de plantillas de Edge que está diseñado para usarse de forma intuitiva. Al igual que Laravel, AdonisJs incluye una ORM llamada Lucid que sirve como interfaz para la comunicación entre los modelos de una aplicación y la base de datos. Con AdonisJs, los desarrolladores pueden crear una aplicación completa en la que el servidor de backend se encargará de aplicar la lógica de negocios, realizar direccionamientos y mostrar todas las páginas de la aplicación. También es posible crear una API de servicio web para mostrar respuestas de JSON desde un controlador; estos servicios web pueden utilizarse en frameworks de frontend, como Vue.js, React y Angular.

      En este tutorial, creará una aplicación con AdonisJs usando su CLI. Creará rutas, controladores, modelos y vistas en su aplicación, y ejecutará validaciones de formularios. El ejemplo de este tutorial será una aplicación de citas inspiradoras en la que un usuario puede registrarse e iniciar sesión para crear una cita inspiradora. Esta aplicación de prueba le dará la oportunidad de realizar operaciones CURD (crear, leer, actualizar y norrar”).

      Requisitos previos

      Para comenzar a seguir los pasos de esta guía, necesitará lo siguiente:

      Nota: En este tutorial se usa una máquina macOS para el desarrollo. Si usa otro sistema operativo, es posible que deba usar sudo para comandos npm en los primeros pasos.

      Paso 1: Instalar Adonis CLI

      En esta sección, instalará Adonis CLI y todos sus paquetes necesarios en su máquina local. El CLI le permitirá escribir un nuevo proyecto AdonisJs, así como crear y generar código estándar para los controladores, los middlewares y los modelos de su aplicación. También creará su base de datos para el proyecto.

      Ejecute el siguiente comando para instalar AdonisJs CLI de forma global en su máquina a través de npm:

      Una vez que se complete el proceso de instalación, ingrese el siguiente comando en la terminal para confirmar la instalación de AdonisJs y ver la versión actual:

      Verá un resultado que mostrará la versión actual de AdonisJs:

      Output

      4.1.0

      Con la instalación correcta de AdonisJs CLI, ahora tendrá acceso al comando adonis y podrá usarlo para crear nuevas instalaciones de un proyecto de AdonisJs, administrar su proyecto y generar archivos pertinentes, como controladores y modelos, entre otros.

      Ahora, puede proceder a crear un nuevo proyecto de AdonisJs usando el comando adonis como se muestra aquí:

      • adonis new adonis-quotes-app

      Con el comando anterior se creará una aplicación llamada adonis-quotes-app en un nuevo directorio con el mismo nombre del directorio de su proyecto local, con la estructura MVC pertinente de AdonisJs.

      Diríjase a la nueva carpeta de la aplicación:

      Luego, inicie su aplicación ejecutando lo siguiente:

      Esto iniciará el servidor de desarrollo en el puerto predeterminado 3333, como se especifica dentro del archivo root .env para su aplicación. Diríjase a http://localhost:3333 para ver la página de bienvenida de AdonisJs.

      Página de bienvenida de AdonisJs

      A continuación, completará la configuración de su base de datos. Aquí, instalará el controlador mysql para establecer conexión con su servidor de MySQL desde su aplicación de Node.js a través de npm. Para comenzar, vuelva a su terminal, donde la aplicación esté actualmente en ejecución, detenga el proceso con CTRL + C y ejecute el siguiente comando:

      Ahora que ha instaló correctamente el controlador de Node.js de MySQL para esta aplicación, deberá crear la base de datos de la aplicación y configurar la conexión correspondiente.

      En la última versión de MySQL que instaló en el tutorial de los requisitos previos se utiliza un complemento de autenticación predeterminado llamado caching_sha2_password. Actualmente, esta no es compatible con los controladores de Node.js de MySQL. Para evitar cualquier problema de conexión de la base de datos de su aplicación, deberá crear un nuevo usuario de MySQL y usar el complemento de autenticación que se admite actualmente: mysql_native_password.

      Para comenzar, acceda al cliente MySQL usando la cuenta root:

      Se le solicitará ingresar la contraseña de su cuenta root configurada durante la instalación de MySQL.

      Luego, cree el usuario y la contraseña usando el complemento mysql_native_password:

      • CREATE USER 'sammy'@'localhost' IDENTIFIED WITH mysql_native_password BY 'password';

      Verá lo siguiente:

      Output

      Query OK, 0 rows affected (0.02 sec)

      Luego, cree una base de datos para la aplicación con lo siguiente:

      Verá lo siguiente:

      Output

      Query OK, 1 row affected (0.03 sec)

      Con esto, habrá creado con éxito la base de datos para esta aplicación.

      Ahora, habilite el acceso a la base de datos creada para el nuevo usuario de MySQL. Ejecute el siguiente comando para conceder todos los privilegios al usuario en la base de datos:

      • GRANT ALL PRIVILEGES ON adonis.* TO 'sammy'@'localhost';

      Vuelva a cargar las tablas de concesión ejecutando el siguiente comando para aplicar los cambios que acaba de realizar:

      Verá lo siguiente:

      Output

      Query OK, 0 rows affected (0.00 sec)

      Cierre el cliente MySQL con lo siguiente:

      Instaló el AdonisJs CLI, creó un nuevo proyecto de AdonisJs e instaló mysql​​​​ a través de npm con éxito. También creó la base de datos para esta aplicación y configuró un usuario MySQL con los privilegios correspondientes. Esta es la configuración básica para su aplicación y, en la siguiente sección, comenzará a crear las vistas necesarias para ella.

      Paso 2: Usar el motor de creación de plantillas Edge

      AdonisJs se envía con su propio motor de plantillas llamado Edge. Le permite crear una plantilla HTML reutilizable y permite la introducción de lógica de frontend en su aplicación con un volumen de código mínimo. Edge proporciona a los desarrolladores de JavaScript las herramientas mientras desarrollan una aplicación para crear un diseño basado en componentes, escribir condicionales, usar iteraciones y crear diseños de vista para contener lógicas. Todos los archivos de plantilla llevan la extensión .edge y se almacenan en el directorio resources/views.

      Las siguientes son las vistas que su aplicación necesitará para funcionar correctamente:

      • Diseño maestro: con Edge, puede crear una página que contendrá el CSS, los archivos de JavaScript comunes, jQuery y partes comunes de la interfaz de usuario que se mantendrán sin cambios en toda la aplicación; por ejemplo, la barra de navegación, el logo y el encabezado. Una vez que haya establecido la página de diseño maestro, otras vistas (páginas) en su aplicación la heredarán.
      • Vista de índice: esta página usará el diseño maestro para heredar archivos comunes y también mostrará el contenido de la página de inicio de la aplicación.
      • Página de inicio de sesión: en esta página también se usará el diseño maestro y se mostrará el formulario con los campos de entrada para el nombre de usuario y la contraseña, para que los usuarios inicien sesión.
      • Página de registro: en ella, los usuarios verán un formulario para registrarse y sus datos se guardarán en la base de datos.
      • Página de creación de citas: los usuarios usarán esta página para crear una cita inspiradora.
      • Página de edición de citas: los usuarios usarán esta página para editar una cita.
      • Página de visualización de citas: los usuarios usarán esta página para ver una cita en particular.

      Para comenzar, utilice el comando adonis para crear la página de diseño maestro ejecutando el siguiente comando:

      • adonis make:view layouts/master

      Visualizará un resultado similar al siguiente:

      Output

      ✔ create resources/views/layouts/master.edge

      Este comando creará automáticamente un archivo master.edge en su carpeta resources/views/layouts. Abra el nuevo archivo:

      • nano resources/views/layouts/master.edge

      Añádale el siguiente código:

      /resources/views/layouts/master.edge

      <!DOCTYPE html>
      <html lang="en">
      <head>
          <meta charset="UTF-8">
          <meta name="viewport" content="width=device-width, initial-scale=1.0">
          <meta http-equiv="X-UA-Compatible" content="ie=edge">
          <title>adonis-quotes-app</title>
          {{ style('https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/css/bootstrap.min.css') }}
          {{ style('style') }}
          {{ script('https://code.jquery.com/jquery-3.3.1.slim.min.js') }}
      </head>
      <body>
          <div class="container-fliud">
              @include('navbar')
              @!section('content')
          </div>
          {{ script('https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/js/bootstrap.min.js') }}
      
      </body>
      </html>
      

      En este archivo, incluya los archivos de CDN para Bootstrap CSS, Bootstrap JavaScript y jQuery. Se agrega un nombre de archivo CSS global style.css y, dentro del div, se incluye un archivo partial llamado navbar. Para volver a la utilizar los fragmentos de código HTML que necesita en varias páginas de su aplicación, como nav o footer, puede incorporar parciales. Estos son archivos más pequeños que contienen el código repetido y agilizan su actualización para estos elementos en un lugar, en vez de hacerlo en cada instancia en la que aparece. La navbar contiene marcado para los botones de Login y Register, un logo y un enlace de inicio.

      Una vez hecho esto, todas las páginas posteriores que se crearán para esta aplicación pueden extender el diseño maestro y mostrar la navbar sin la necesidad de volver a escribir el contenido. Creará este archivo navbar más tarde en el tutorial.

      Por último, se define una etiqueta de sección @! section() para incluir contenido de otras páginas y que el diseño maestro las muestre. Para que esto funcione como se espera, todas las nuevas páginas que ampliarán el diseño maestro también deben definir una etiqueta de sección con el mismo nombre (es decir, @section('content')).

      Guarde y cierre el archivo una vez que termine de editarlo.

      Luego, use el comando adonis para crear la barra de navegación:

      Verá un resultado similar a lo siguiente:

      Output

      ✔ create resources/views/navbar.edge

      Abra el archivo recién creado:

      • nano resources/views/navbar.edge

      Luego, añádale el siguiente código:

      /resources/views/navbar.edge

      <nav class="navbar navbar-expand-lg navbar-dark text-white">
          <a class="navbar-brand" >LOGO</a>
          <button class="navbar-toggler" type="button" data-toggle="collapse" data-target="#navbarNav" aria-controls="navbarNav" aria-expanded="false" aria-label="Toggle navigation">
              <span class="navbar-toggler-icon"></span>
          </button>
      
          <div class="collapse navbar-collapse" id="navbarNav">
              <ul class="navbar-nav">
                  <li class="nav-item active ">
                      <a class="btn text-white" href="https://www.digitalocean.com/">Home</a>
                  </li>
              </ul>
          </div>
          <div class="navbar-right" id="navbarNav">
              @loggedIn
                  <ul class="navbar-nav">
                          <li>
                              <div class="text-right">
                                   <a href="{{route('create.quote')}}" class="btn btn-outline-primary">Create Quote</a>
                              </div>
                          </li>
      
                      <li class="nav-item dropdown">
                          <a class="nav-link dropdown-toggle" href="https://www.digitalocean.com/#" id="navbarDropdownMenuLink" role="button" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
                             {{ auth.user.username}}
                          </a>
                          <div class="dropdown-menu" aria-labelledby="navbarDropdownMenuLink">
                              <form method="POST" action="{{route('logout')}}">
                                  {{ csrfField() }}
                                    <button  type="submit" class="dropdown-item" href="">logout</button>
                              </form>
                          </div>
                      </li>
                  </ul>
              @else
                  <ul class="navbar-nav">
                      <li class="nav-item active pr-2">
                          <a href="{{route('login.create')}}" class="btn btn-outline-danger">
                            login
                          </a>
                      </li>
                      <li class="nav-item active pr-2">
                          <a href="{{route('register.create')}}" class="btn btn-outline-primary">
                              Register
                          </a>
                      </li>
                  </ul>
              @endloggedIn
          </div>
      </nav>
      

      Además de definir los enlaces a la página de inicio de sesión y los botones para registrarse e iniciar sesión, añada la etiqueta @loggedIn. Después de hacer hecho esto, puede escribir una instrucción condicional para el usuario autenticado y mostrar los contenidos apropiados cuando sea necesario. Para un usuario autenticado, la aplicación mostrará su nombre de usuario y un botón para crear una nueva cita. Si el usuario no inició sesión, su aplicación mostrará un botón para hacerlo o para registrarse. Esta página se incluirá como parcial en cada página, al igual que en el diseño maestro anterior de esta aplicación.

      Guarde el archivo y ciérrelo.

      Ahora, deberá crear la página de índice que usará para la página de inicio de la aplicación. Esta brindará y mostrará la lista de todas las citas inspiradoras que los usuarios escriban:

      Visualizará un resultado similar al siguiente:

      Output

      ✔ create resources/views/index.edge

      El archivo creado aquí estará ubicado en resources/views/index.edge. Abra el archivo:

      • nano resources/views/index.edge

      Luego, añada el siguiente código:

      /resources/views/index.edge

      @layout('layouts/master')
      @section('content')
      
      <div class="container">
          <div class="text-center">
              @if(flashMessage('successmessage'))
                  <span class="alert alert-success p-1">{{ flashMessage('successmessage') }}</span>
              @endif
          </div>
          <div class="row">
              @each(quote in quotes)
                  <div class="col-md-4 mb-4 quote-wrapper">
                      <a href="https://www.digitalocean.com/view-quote/{{quote.id}}" class="w-100">
                          <div class="card shadow-lg bg-dark text-white">
                              <div class="card-body">
                                  <blockquote class="blockquote mb-0">
                                      <p>{{quote.body}}</p>
                                      <footer class="blockquote-footer">
                                          <cite title="Source Title"> {{quote.username}}</cite>
                                      </footer>
                                  </blockquote>
                                  @if(auth.user.id == quote.user_id)
                                    <div>
                                      <a  href="http://www.digitalocean.com/edit-quote/{{quote.id}}" class="btn btn-primary">edit</a>
                                      <a href="http://www.digitalocean.com/delete-quote/{{quote.id}}" class="btn btn-danger">delete</a>
                                    </div>
                                  @endif
                              </div>
                          </div>
                      </a>
                  </div>
              @else
               <div class="col-md-12 empty-quote text-center">
                      <p>No inspirational quote has been created</p>
               </div>
              @endeach
          </div>
      </div>
      @endsection
      

      Aquí, debe indicar que esta vista usará el diseño master extendiéndolo. Esta página ahora tendrá acceso a todas las bibliotecas, las hojas de estilos y la navbar​​ incluida en el diseño master. Luego, debe iterar la matriz de quotes​​​ usando la etiqueta integrada @each. La matriz de quotes pasará a esta vista desde el QuoteController que creará más tarde en este tutorial. Si no hay citas, se mostrará un mensaje apropiado.

      Guarde este archivo y ciérrelo.

      Para crear la página de inicio de sesión, ejecute el siguiente comando desde la terminal:

      • adonis make:view auth/login

      Verá un resultado similar a este:

      Output

      ✔ create resources/views/auth/login.edge

      Esto creará automáticamente una carpeta auth dentro de resources/views y también un archivo login.edge dentro de ella. Abra el archivo login.edge:

      • nano resources/views/auth/login.edge

      Añada el siguiente contenido:

      /resources/views/auth/login.edge

      @layout('layouts/master')
      @section('content')
        <div class="container">
          <div class="row">
            <div class="col-md-4 shadow bg-white mt-5 rounded offset-md-4">
              <form method="POST" action="{{route('login.store')}}">
                {{ csrfField() }}
                  <div>
                    @if(flashMessage('successmessage'))
                      <span class="alert alert-success p-1">{{ flashMessage('successmessage') }}</span>
                    @endif
                  </div>
                  <div class="form-group">
                    <label for="email">Email address</label>
                    <input type="email" class="form-control" id="email" name="email" value="{{old('email','')}}"  placeholder="Enter email">
                    {{ elIf('<span class=text-danger>$self</span>', getErrorFor('email'), hasErrorFor('email')) }}
                  </div>
                  <div class="form-group">
                    <label for="pasword">Password</label>
                    <input type="password" class="form-control" id="password" name="password" value="{{old('password','')}}" placeholder="Password">
                    {{ elIf('<span class=text-danger>$self</span>', getErrorFor('password'), hasErrorFor('password')) }}
                  </div>
      
                  <div class="text-center">
                    <button type="submit" class="btn btn-primary">Submit</button>
                  </div>
              </form>
            </div>
          </div>
        </div>
      @endsection
      

      Este archivo posee un formulario que contendrá elementos de entrada que se utilizarán a fin de recopilar el nombre de usuario y la contraseña de un usuario registrado para su correcta autenticación y comenzar a crear citas. Otro elemento importante que se debe tener en cuenta en esta página es {{ csrf Field()}. Es una variable global que AdonisJs usará para pasar el token de acceso CSRF al enviar una solicitud POST, PUT y DELETE desde su aplicación.

      Esto se implementó para proteger su aplicación contra ataques de falsificación de petición en sitios cruzados (CSRF). Funciona generando un secreto de CSRF único para cada usuario que visite su sitio web y una vez que sus usuarios envían una solicitud HTTP del frontend, se genera un token secreto correspondiente que se pasa junto con la solicitud. Esto permitirá que el middleware creado para esta solicitud en AdonisJs verifique que el token y el secreto de CSRF sean válidos y pertenezcan al usuario autenticado.

      Guarde y cierre el archivo una vez que termine.

      Luego, cree la página de registro con este comando:

      • adonis make:view auth/register

      Verá un resultado similar a este:

      Output

      ✔ create resources/views/auth/register.edge

      Ubique y abra el archivo recién creado en resources/views/auth/register.edge:

      • nano resources/views/auth/register.edge

      Añada el siguiente código:

      resources/views/auth/register.edge

      @layout('layouts/master')
      @section('content')
        <div class="container ">
          <div class="row">
              <div class="col-md-4  bg-white p-3 mt-5 shadow no-border rounded offset-md-4">
                <form method="POST" action="{{route('register.store')}}">
                  {{ csrfField() }}
                    <div class="form-group">
                      <label for="name">Fullname</label>
                      <input type="text" class="form-control" id="name" name="name"  value="{{old('name','')}}" placeholder="Enter Fullname">
                      {{ elIf('<span class=text-danger>$self</span>', getErrorFor('name'), hasErrorFor('name')) }}
                    </div>
                    <div class="form-group">
                      <label for="email">Email address</label>
                      <input type="email" class="form-control" id="email"  name="email" value="{{old('email','')}}" placeholder="Enter email">
                      {{ elIf('<span class=text-danger>$self</span>', getErrorFor('email'), hasErrorFor('email')) }}
                    </div>
                    <div class="form-group">
                      <label for="pasword">Password</label>
                      <input type="password" class="form-control" id="password" name="password" placeholder="Password">
                      {{ elIf('<span class=text-danger>$self</span>', getErrorFor('password'), hasErrorFor('password')) }}
                    </div>
                    <div class="text-center">
                        <button type="submit" class="btn btn-primary">Submit</button>
                    </div>
                </form>
              </div>
          </div>
        </div>
      @endsection
      

      De manera similar a lo que figura en la página de inicio de sesión, este archivo contiene un formulario HTML con campos de entrada para recopilar el name, el email y la password del usuario durante el proceso de registro. También se incluye el {{ csrfField()}}​​, debido que este se requiere para cada solicitud post de una aplicación de AdonisJs.

      Guarde el archivo y ciérrelo.

      Ahora, debe generar un nuevo archivo para crear una cita inspiradora ejecutando el siguiente comando desde la terminal:

      • adonis make:view quotes/create-quote

      Verá un resultado como este:

      Output

      ✔ create resources/views/quotes/create-quote.edge

      Abra resources/views/quotes/create-quote.edge:

      • nano resources/views/quotes/create-quote.edge

      Añada a este el siguiente contenido:

      /resources/views/quotes/create-quote.edge

      @layout('layouts/master')
      @section('content')
      <div class="container">
          <div class="row">
              <div class="col-md-3"></div>
              <div class="col-md-6 shadow bg-white mt-5 rounded p-3">
                  <div class="float-right">
                      <a href="https://www.digitalocean.com/" class="btn btn-outline-dark ">back</a>
                  </div>
                      <br>
      
                  <div class="clear-fix"></div>
                      <form method="POST" action="{{route('store.quote')}}">
                          {{ csrfField() }}
                          <div class="form-group">
                              <label for="quote">Create Quote</label>
                              <textarea type="text" rows="5"  name='body' id="body" class="form-control" id="quote" placeholder="Write an inspirational quote"></textarea>
                          </div>
      
                          <div class="text-center">
                              <button type="submit" class="btn btn-primary">Submit</button>
                          </div>
                      </form>
                  </div>
              </div>
              <div class="col-md-3"></div>
          </div>
      </div>
      @endsection
      

      Esta página extiende el diseño maestro y contiene un formulario HTML con un elemento de área de texto que permite al usuario ingresar texto en varias filas antes de su publicación y su manipulación a través de la ruta correspondiente.

      Guarde y cierre el archivo una vez que termine.

      Luego, cree una página para editar una cita en particular. Ejecute el siguiente comando desde la terminal:

      • adonis make:view quotes/edit-quote

      Verá lo siguiente:

      Output

      ✔ create resources/views/quotes/edit-quote.edge

      Abra el archivo con lo siguiente:

      • nano resources/views/quotes/edit-quote.edge

      Añada el siguiente contenido a resources/views/quotes/edit-quote:

      /resources/views/quotes/edit-quote.edge

      @layout('layouts/master')
      @section('content')
      <div class="container">
          <div class="row">
              <div class="col-md-6 shadow bg-white rounded p-3 offset-md-3">
                  <div class="float-right">
                      <a href="https://www.digitalocean.com/" class="btn btn-outline-dark ">back</a>
                  </div>
                  <br>
      
                  <div class="clear-fix"></div>
                  <form method="POST" action="/update-quote/{{quote.id}}">
                      {{ csrfField() }}
                      <div class="form-group">
                          <label for="pasword">Edit Quote</label>
                          <textarea type="text" rows="5"  name='body' id="body" class="form-control" id="quote" placeholder="write the inspirational quote">{{quote.body}}</textarea>
                      </div>
                      <div class="text-center">
                          <button type="submit" class="btn btn-primary">Update</button>
                      </div>
      
                  </form>
              </div>
          </div>
      </div>
      @endsection
      

      Esta página tiene contenido similar al del archivo create-quote.edge. La diferencia radica en que que contiene los detalles de una cita en particular que es necesario editar, <form method="POST" action="/update-quote/{{quote.id}}">​​​.

      Guarde el archivo y ciérrelo.

      Por último, genere una página para ver una única cita inspiradora:

      • adonis make:view quotes/quote

      Verá un resultado similar a este:

      Output

      ✔ create resources/views/quotes/quote.edge

      Abra el archivo con lo siguiente:

      • nano resources/views/quotes/quote.edge

      Añada el siguiente código:

      /resources/views/quotes/quote.edge

      @layout('layouts/master')
      @section('content')
      <div class="container">
          <div class="row">
              <div class="col-md-6 offset-md-3">
                  <div class="card shadow-lg bg-dark text-white">
                      <div class="card-body">
                          <div class="float-right">
                              <a href="https://www.digitalocean.com/" class="btn btn-outline-primary ">back</a>
                          </div>
                              <br>
                          <div class="clear-fix"></div>
                          <blockquote class="blockquote mb-0">
                              <p>{{quote.body}}</p>
                              <footer class="blockquote-footer">
                                  <cite title="Source Title">{{quote.username}}</cite>
                              </footer>
                          </blockquote>
                      </div>
                  </div>
              </div>
          </div>
      </div>
      @endsection
      

      Esta página brinda los detalles de una cita en particular, que incluye el cuerpo de la cita, quote.body y el nombre del autor que la creó, quote.username.

      Una vez que terme con el archivo, guárdelo y ciérrelo.

      Creó todas las páginas necesarias para su aplicación usando el motor de plantillas Edge. Luego, configure y cree una conexión con la base de datos de su aplicación.

      Paso 3: Crear un esquema de base de datos

      Si utiliza su aplicación ahora, generará un error porque aún no conectó la aplicación con una base de datos. En esta sección, configurará una conexión a la base de datos y luego usará el comando adonis a fin de generar un archivo de migración que se usará para crear las tablas.

      AdonisJs usa una ORM llamada Lucid ORM, que proporciona la implementación de active record para trabajar con su base de datos. Elimina la molestia de escribir consultas SQL que recuperan datos de la base de datos en tiempo real. Es especialmente útil cuando se trabaja en una aplicación compleja que requiere muchas consultas. Como ejemplo, puede recuperar todas las citas de su aplicación escribiendo lo siguiente:

      const quotes = await Quote.all()
      

      Para proceder con la configuración apropiada de la base de datos de su aplicación, asegúrese de estar aún posicionado en el directorio root de su aplicación y cree un archivo .env:

      Abra el archivo recién creado y añada el siguiente contenido:

      .env

      HOST=127.0.0.1
      PORT=3333
      NODE_ENV=development
      APP_URL=http://${HOST}:${PORT}
      CACHE_VIEWS=false
      APP_KEY=bTVOEgUvmTCfkvgrK8gEBC3Qxt1xSYr0
      DB_CONNECTION=mysql
      DB_HOST=127.0.0.1
      DB_PORT=3306
      DB_USER=sammy
      DB_PASSWORD=password
      DB_DATABASE=adonis
      SESSION_DRIVER=cookie
      HASH_DRIVER=bcrypt
      

      Por defecto, la conexión de la base de datos para una aplicación AdonisJs es SQLite, que actualizará a MySQL aquí. Especifique también el PORT para la aplicación, el entorno de la aplicación y las credenciales de su base de datos. Asegúrese de sustituir el DB_USER, DB_PASSWORD y el marcador de posición DB_DATABASE por sus credenciales.

      Luego, creará el modelo y un archivo de migración para Quote usando Adonis CLI. Para hacer esto, ejecute el siguiente comando:

      • adonis make:model Quote --migration

      Visualizará un resultado similar al siguiente:

      Output

      ✔ create app/Models/Quote.js ✔ create database/migrations/1568209992854_quote_schema.js

      Este comando creará un modelo para Quote en la carpeta app/Models y un archivo de esquema en la carpeta database/migrations​​​. El archivo de esquema recién creado llevará la marca de tiempo actual como prefijo. Abra el archivo de esquema con lo siguiente:

      • nano database/migrations/1568209992854_quote_schema.js

      Actualice su contenido con el siguiente código:

      database/migrations/…quote_schema.js

      'use strict'
      /** @type {import('@adonisjs/lucid/src/Schema')} */
      const Schema = use('Schema')
      class QuoteSchema extends Schema {
        up () {
          this.create('quotes', (table) => {
            table.increments()
            table.integer('user_id').notNullable()
            table.string('username', 80).notNullable()
            table.string('body').notNullable()
            table.timestamps()
          })
        }
        down () {
          this.drop('quotes')
        }
      }
      module.exports = QuoteSchema
      

      Un archivo de esquema de AdonisJs requiere dos métodos diferentes, que son los siguientes:

      • up: Se utiliza para crear una nueva tabla o modificar una tabla existente.
      • down: se utiliza para revertir los cambios aplicados en el método up.

      Además de los campos timestamps() y increments(), debe el contenido del archivo de esquema con los atributos de campo user_id​​​, username y el body de la cita que se creará Los campos user_id y username hacen referencia a los detalles del usuario que crea una cita en particular. Esto define una relación de uno a muchos y significa que un usuario puede poseer un número infinito de citas mientras que una sola cita solo puede pertenecer a un usuario.

      Guarde el archivo y ciérrelo.

      AdonisJs viene instalado con un modelo User y su archivo de migración por defecto, el cual requiere solo una pequeña modificación para establecer la relación entre el modelo User y Quote.

      Abra el modelo User en app/Models/User.js:

      Añada este método de inmediato después del método tokens():

      app/Models/User.js

      ...
      class User extends Model {
        ...
        tokens () {
          return this.hasMany('App/Models/Token')
        }
      
        quote () {
          return this.hasMany('App/Models/Quote')
        }
      }
      
      module.exports = User
      

      Esto establecerá una relación de uno a muchos con la tabla Quote usando user_id como la clave externa.

      Guarde el archivo y ciérrelo.

      Para finalizar esta sección, utilice el siguiente comando para ejecutar migraciones, el cual ejecutará el método up() de todos los archivos de migración:

      El resultado debe ser similar a lo siguiente:

      Output

      migrate: 1503248427885_user.js migrate: 1503248427886_token.js migrate: 1568209992854_quote_schema.js Database migrated successfully in 3.42 s

      Ya configuró y protegió una conexión con su base de datos. También creó un modelo Quote con su archivo de esquema correspondiente y una relación uno a muchos entre User y Quote. Luego, debe generar las rutas y crear controladores para gestionar solicitudes HTTP y la lógica de negocios para crear, editar y eliminar una cita inspiradora.

      Paso 4: Crear controladores y configurar rutas

      En esta sección, primero creará controladores para gestionar toda la lógica de la aplicación y luego añadirá estos controladores a una ruta específica para que los usuarios puedan acceder a ella a través de una URL.

      Para comenzar, usando Adonis CLI creará un nuevo controlador de solicitud HTTP para gestionar todos los procesos de autenticación de su aplicación con el siguiente comando:

      • adonis make:controller Auth --type http

      Este comando creará un archivo AuthController.js y lo guardará en la carpeta app/Controllers/Http. Utilice el indicador --type para indicar que desea que este controlador sea HTTP.

      Visualizará un resultado similar al siguiente:

      Output

      ✔ create app/Controllers/Http/AuthController.js

      Luego, abra el archivo del controlador recién creado:

      • nano app/Controllers/Http/AuthController.js

      Actualícelo con el siguiente contenido:

      app/Controllers/Http/AuthController.js

      'use strict'
      const User = use('App/Models/User')
      class AuthController {
      
          loginView({ view }) {
              return view.render('auth.login')
          }
          registrationView({ view }) {
              return view.render('auth.register')
          }
      
          async postLogin({ request, auth, response}) {
              await auth.attempt(request.input('email'), request.input('password'))
              return response.route('index')
          }
      
          async postRegister({ request, session, response }) {
              const user = await User.create({
                  username: request.input('name'),
                  email: request.input('email'),
                  password: request.input('password')
              })
              session.flash({ successmessage: 'User have been created successfully'})
              return response.route('login.create');
          }
      
          async logout ({ auth, response }) {
              await auth.logout()
              return response.route("https://www.digitalocean.com/")
          }
      }
      module.exports = AuthController
      

      En este archivo, debe importar el modelo User y luego crear dos métodos denominados loginView() y registerView() para mostrar las páginas de inicio de sesión y registro respectivamente. Por último, se deben crear los siguientes métodos asíncronos:

      • postLogin(): este método obtendrá los valores de email y password publicados con ayuda del método request incorporado de AdonisJs, y luego validará este usuario con los detalles de la base de datos. Si dicho usuario existe en la base de datos e ingresó la credencial correcta, regresará a la página de inicio y se autenticará para crear una nueva cita. De lo contrario,aparecerá un mensaje que indicará que las credenciales ingresadas son incorrectas.
      • postRegister(): este método recibirá el valor de username, email y password para que un usuario cree una cuenta para dicho usuario en la base de datos. Se pasará a la sesión un mensaje que indicará que se creó correctamente dicho usuario y este accederá a la página de inicio de sesión para autenticarse y comenzar a crear una cita.
      • logout(): este método gestionará la función de inicio de sesión y redireccionará el usuario de vuelta a la página de inicio.

      Guarde el archivo y ciérrelo.

      Ahora que configuró el controlador para registrar y autenticar usuarios, proseguirá creando un controlador de solicitudes HTTP para administrar todas las operaciones relacionadas con citas.

      En el terminal, ejecute el siguiente comando para crear el QuoteController:

      • adonis make:controller Quote --type http --resource

      Usar el indicador --resource creará un controlador con métodos ingeniosos predefinidos para realizar operaciones CRUD (crear, leer, actualizar y borrar).

      Verá lo siguiente:

      Output

      ✔ create app/Controllers/Http/QuoteController.js

      Ubique este archivo en app/Controllers/Http/QuoteController.js:

      • nano app/Controllers/Http/QuoteController.js

      Actualícelo con el siguiente contenido:

      app/Controllers/Http/QuoteController.js

      'use strict'
      const Quote = use('App/Models/Quote')
      
      class QuoteController {
      
        async index ({ view }) {
          const quote = await Quote.all()
          return view.render('index', {
            quotes: quote.toJSON()
          })
        }
      
        async create ({ view }) {
          return view.render('quotes.create-quote')
        }
      
        async store ({ request,auth,session, response }) {
          const quote = await Quote.create({
            user_id: auth.user.id,
            username: auth.user.username,
            body: request.input('body')
          })
          session.flash({ 'successmessage': 'Quote has been created'})
          return response.redirect("https://www.digitalocean.com/")
        }
      
        async show ({ params, view }) {
          const quote = await Quote.find(params.id)
          return view.render('quotes.view-quote', {
            quote: quote.toJSON()
          })
        }
      
        async edit ({ params, view }) {
          const quote = await Quote.find(params.id)
          return view.render('quotes.edit-quote', {
            quote: quote.toJSON()
          })
        }
      
        async update ({ params, request, response, session }) {
          const quote = await Quote.find(params.id)
          quote.body = request.input('body')
          await quote.save()
          session.flash({'successmessage': 'Quote has been updated'})
          return response.redirect("https://www.digitalocean.com/")
        }
      
        async destroy ({ params, response, session }) {
          const quote = await Quote.find(params.id)
          await quote.delete()
          session.flash({'successmessage': 'Quote has been deleted'})
          return response.redirect("https://www.digitalocean.com/")
        }
      }
      module.exports = QuoteController
      

      En este controlador, importó el modelo Quote y actualizó los siguientes métodos que se crearon automáticamente mediante AdonisJs CLI:

      • index(): permite buscar todas las citas de la base de datos y mostrarlas en la página de inicio de la aplicación.
      • create(): permite representar una página para crear citas.
      • store(): permite persistir una cita recién creada en la base de datos y mostrar una respuesta adecuada.
      • show(): permite obtener el id de una cita en particular, obtenerlo de la base de datos y procesarlo en la página para editar citas.
      • edit(): permite obtener el detalle de una cita en particular de la base de datos y representarla para su edición
      • update(): permite procesar cualquier actualización de una cita y redirigir al usuario de vuelta a la página de inicio.
      • destroy(): permite eliminar una cita en particular y eliminarla por completo de la base de datos.

      Guarde el archivo y ciérrelo.

      Después de crear todos los controladores necesarios para esta aplicación, podrá configurar las rutas para que los usuarios puedan interactuar fácilmente con su aplicación. Para comenzar, diríjase al archivo start/routes.js

      Reemplace su contenido con lo siguiente:

      start/routes.js

      'use strict'
      const Route = use('Route')
      
      Route.get("https://www.digitalocean.com/",'QuoteController.index').as('index')
      Route.get('/register','AuthController.registrationView').as('register.create')
      Route.post('/register-store','AuthController.postRegister').as('register.store').validator('Register')
      Route.get('/login','AuthController.loginView').as('login.create')
      Route.post('/login-store','AuthController.postLogin').as('login.store')
      Route.get('/view-quote/:id','QuoteController.show').as('view.quote')
      
      Route.group(() => {
          Route.get('/create-quote','QuoteController.create').as('create.quote')
          Route.post('/store-quote','QuoteController.store').as('store.quote')
          Route.get('/edit-quote/:id','QuoteController.edit').as('edit.quote')
          Route.post('/update-quote/:id','QuoteController.update').as('update.quote')
          Route.get('/delete-quote/:id','QuoteController.destroy').as('delete.quote')
          Route.post('/logout','AuthController.logout').as('logout')
      }).middleware(['auth'])
      

      Aquí, debe definir el camino para cada ruta en su aplicación, especificar los verbos del HTTP para cada acción y vincular la ruta a un método particular en cada controlador. También debe nombrar cada una de estas rutas, ya que se referencian en los controladores y las vistas.

      Para garantizar que solo los usuarios autenticados puedan acceder a todas las rutas de citas, asigne un middleware de grupo con nombre. Por último, debe añadir un método validator a la ruta de register.store para validar la entrada del usuario.

      Guarde el archivo y ciérrelo.

      Creó sus controladores y configuró las rutas para su aplicación. Luego, cree el método de validación definido en este paso.

      Paso 5: Validar el resultado del usuario

      AdonisJs no tiene validadores incorporados por defecto. Por ello, instale y registre el validador para su aplicación manualmente.

      Ejecute el siguiente comando para instalarlo:

      Abra el siguiente archivo para registrar el proveedor del validador:

      Luego, registre el proveedor del validador agregándolo a la lista de proveedores, como se muestra a continuación:

      start/app.js

      ...
      const providers = [
         ...
         '@adonisjs/cors/providers/CorsProvider',
         '@adonisjs/shield/providers/ShieldProvider',
         '@adonisjs/session/providers/SessionProvider',
         '@adonisjs/auth/providers/AuthProvider',
         '@adonisjs/validator/providers/ValidatorProvider'
      ]
      

      Ahora que instaló y registró el proveedor del validador en su aplicación, cree un validador personalizado para validar la entrada del usuario durante el registro con el siguiente comando:

      • adonis make:validator Register

      Con esto se creará un archivo Register.js en el directorio App/validators. Abra el archivo con:

      • nano app/Validators/Register.js

      Añada el siguiente código al archivo:

      app/Validators/Register.js

      'use strict'
      class Register {
        get rules () {
          return {
            name:'required',
            email:'required|email|unique:users',
            password:'required|min:8'
          }
        }
      
        get messages(){
          return{
            'name.required':'Full name is required',
            'email.required':'email is required',
            'email.unique':'email already exists',
            'password.required':'password is required',
            'password.min':'password should be at least 8 characters'
          }
        }
      }
      module.exports = Register
      

      En su aplicación, debe definir reglas para campos específicos. Si las validaciones fallan en algún momento, el validador establecerá automáticamente el error como mensaje flash y el usuario regresará al formulario.

      Guarde y cierre el archivo una vez que termine de editarlo.

      Por último, para añadir estilo a su aplicación, abra el siguiente archivo:

      Reemplace su contenido por lo siguiente:

      /public/style.css

      @import url('https://fonts.googleapis.com/css?family=Montserrat:300');
      
      html, body {
        height: 100%;
        width: 100%;
      }
      
      body {
        font-family: 'Montserrat', sans-serif;
        font-weight: 300;
        background-image: url("/splash.png");
        background-color: #220052;
      }
      
      * {
        margin: 0;
        padding: 0;
      }
      
      a {
        color: inherit;
        text-decoration: underline;
      }
      
      p {
        margin: 0.83rem 0;
      }
      
      .quote-wrapper {
        margin-top: 20px;
      }
      
      .quote-wrapper a {
        text-decoration: none;
      }
      
      .quote-wrapper a:hover {
        color: #ffffff;
      }
      
      .empty-quote {
        color: #ffffff;
      }
      
      form {
        padding: 20px;
      }
      

      En este archivo, debe actualizar el estilo CSS de su aplicación en el archivo style.css.

      Instaló y registró un proveedor de validador para verificar la entrada de los usuarios durante el proceso de registro. También actualizó el contenido de su hoja de estilos para añadir más estilos a la aplicación. En el último paso, probará su aplicación.

      Paso 6: Presentar la aplicación

      En este paso, presentará su aplicación y creará un usuario y una contraseña para probar la autenticación. También agregará una cita a su aplicación y la verá en la página de inicio.

      Para probar su aplicación, inicie el servidor de desarrollo con el siguiente comando desde el directorio root de su aplicación:

      Esto iniciará la aplicación en el puerto definido dentro del archivo .env root, el cual es 3333. Diríjase a http://localhost:3333 desde su navegador.

      Página de inicio de la aplicación de citas

      La página de inicio estará vacía en este momento, ya que no creó citas. Haga clic en el botón Register.

      Página de registro

      Ingrese sus detalles y haga clic en el botón Submit para completar el proceso de registro. Accederá a la página de inicio de sesión. Ingrese su dirección de correo electrónico y su contraseña para la autenticación.

      Página de inicio de sesión

      Una vez que esté autenticado, haga clic en el botón Crear cita.

      Página de creación de citas

      Ingrese una cita y diríjase a la página View all para ver su cita.

      Página de visualización de todas las citas

      Probó su aplicación creando y autenticando un usuario y, luego, escribiendo una cita.

      Conclusión

      A lo largo de este tutorial, creó una aplicación web con AdonisJs. Configuró la aplicación usando el AdonisJs CLI y aprovechó el CLI para crear otros archivos importantes, como controladores, modelos y vistas.

      Puede crear aplicaciones web con este marco independientemente de su tamaño y complejidad. Puede descargar el código fuente de este proyecto aquí en GitHub. Para ver más características, también puede visitar la documentación oficial.

      Si desea ver algunos de nuestros otros tutoriales del marco de JavaScript, puede consultar lo siguiente:



      Source link