Formulario de contacto en FLASK (WTForms) + ReCaptcha

formularios en flaskHola nuevamente a mis lectores en esta entrada aprenderemos a crear un formulario de contacto en FLASK continuando con nuestro proyecto.

Recordemos: aprendimos a crear un formulario sencillo en HTML y a utilizar el método post. Si bien nuestro formulario de contacto en flask podríamos realizarlo de la misma manera, en este caso, vamos a utilizar una librería llamada WTForms, pero igualmente vamos a estudiar ambas maneras. Primero veremos a fondo los formularios HTML y luego veremos como simplificarnos este proceso usando WTForms.

Veamos como ha quedado mi portfolio..

 

Para actualizarlos un poco he realizado algunas modificaciones en el, aunque puramente se ha tratado de HTML + CSS + JS. Es decir, la parte visual sobre todo.

 

 

 

 

 

 

 

 

Por supuesto aún hay mucho que completar, falta la parte de Blog, sub-dividir y completar Mis proyectos y Habilidades, entre otras cosas. Pero en este momento vamos a dedicarnos a aprender a crear un formulario de contacto en flask. Al final te dejaré el código completo de mi portfolio. Aunque es obvio que no deberías utilizar el mismo, sino más bien modificarlo y darle otros estilos, tu propia esencia.

formulario en flask mi portfolioA veces creo que no es buena idea publicarlo aquí y utilizar el mismo porque alguien podría copiarlo y utilizarlo; pero vamos, siempre me ha gustado compartir y quien lo haga realmente no llegará muy lejos si se nutre de «copiar y pegar» contenido de otras personas. Y aunque alguien consiguiera un empleo cometiendo este abuso, realmente creo que luego no sabría que hacer en su puesto de trabajo.

 

Para ello vamos a ver como estaba nuestra página «contacto.html«, recordemos que apenas colocamos un alert de Bootstrap y lo dejamos así:

{% extends "base/base.html" %}
{% block title %}Contacto{% endblock %}
{%block columna8 %}
<br>
<div class="alert alert-success" role="alert">
    <h4 class="alert-heading">Estaré encantado de contactarte!</h4>
    <p>Aquí verás un formulario de contacto ;)</p>
    <hr>
    <p class="mb-0">Saludos!</p>
  </div>
{%endblock %}
  {% block columna4 %}
<p>Columna</p>
  {% endblock %}

Y por cierto veamos nuestro «base.html» donde añadí un bloque que llamé «inicio» antes de los otros dos bloques «columna 8» y «columna 4«. Este bloque lo añadí para cuando quisiera utilizar todo el ancho de página sin columnas como es el caso del «index.html» que verás al final.

 

«base.html»

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>{% block title%} {%endblock%}</title>
    <!--BOOTSTRAP CSS --> 
    <link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.4.1/css/bootstrap.min.css" integrity="sha384-Vkoo8x4CGsO3+Hhxv8T/Q5PaXtkKtu6ug5TOeNV6gBiFeWPGFN9MuhOf23Q9Ifjh" crossorigin="anonymous">
    <!--MI CSS PERSONAL --> 
    <link rel="stylesheet" href="{{ url_for('static', filename='css/base.css' ) }}">
</head>
<body>
    <div id = "capa1">
    {% include "nav/nav-bar.html" %}
    {%block inicio%}
    {%endblock%}

        <div class="container">    <!-- Aquí estamos usando la clase "container" de Bootstrap --> 
            <div class="row">    <!-- Aquí estamos usando la clase "row" que significa "Fila", "o hilera"-->
            <div class="col-8"> <!-- Aquí nos esta creando una columna de tamaño 8--> 
                {% block columna8 %}
            
                
                  {% endblock %}
            </div>   
            <div class="col-4">
                {% block columna4 %}

                {% endblock %}



            </div>     <!-- Aquí nos esta creando una columna de tamaño 4--> 
          </div>                               <!--Cierra el row--> 
      </div>                                   <!-- Cierra el container-->
    </div>
<!-- jQuery first, then Popper.js, then Bootstrap JS --> 
<script src="https://code.jquery.com/jquery-3.4.1.slim.min.js" integrity="sha384-J6qa4849blE2+poT4WnyKhv5vZF5SrPo0iEjwBvKU7imGFAV0wwj1yYfoRSJoZ+n" crossorigin="anonymous"></script> 
<script src="https://cdn.jsdelivr.net/npm/popper.js@1.16.0/dist/umd/popper.min.js" integrity="sha384-Q6E9RHvbIyZFJoft+2mJbHaEWldlvI9IOYy5n3zV9zzTtmI3UksdQRVvoxMfooAo" crossorigin="anonymous"></script> 
<script src="https://stackpath.bootstrapcdn.com/bootstrap/4.4.1/js/bootstrap.min.js" integrity="sha384-wfSDF2E50Y2D1uUdj0O3uMBJnjuUD4Ih7YwaYd1iqfktj0Uod8GCExl3Og8ifwB6" crossorigin="anonymous"></script>   
<script src="{{ url_for('static', filename='js/tittle-efect.js' ) }}"></script> 
</body>
</html>

 

Despreocúpate si recién comienzas!
Si tu recién comienzas o llegas a este post y no has creado ningún bloque ni nada. Y te encuentras frente a una pantalla en blanco, no te preocupes, ya que simplemente bastará con utilizar la estructura de un archivo HTML normal para crear y procesar un formulario. No hagas mucho caso de los estilos, ya que simplemente se trata de un proyecto que yo estoy realizando. Luego haremos otros desde el inicio y volveremos a aprender parte por parte FLASK.

2 Formas de crear un formulario de contacto en FLASK

Lo cierto es que existen dos maneras, la primera es creando un tradicional formulario HTML y programando del otro lado nuestro controlador para procesarle manualmente. Y la otra es utilizando WTForms que es una librería que nos facilita un poco las cosas pero también nos limita en otras. Yo creo que ambas maneras son validas y dependen de la decisión y estilo de cada programador. Aquí aprenderemos las dos, por supuesto:

 

Creando un formulario de contacto clásico en HTML (FORMA 1)

Así que lo primero será modificar la parte visual creando los inputs clásicos de un formulario de contacto. Normalmente se le solicita al usuario:

  1. Su nombre.
  2. Su correo electrónico, para enviarle una respuesta.
  3. El mensaje que nos desea enviar.

Por lo tanto nos podemos ir directamente al sitio web de Bootstrap y buscar allí el apartado Formularios, recordemos que utilizamos Bootstrap en nuestro proyecto.

Allí veremos múltiples ejemplos de lo que podemos hacer con Bootstrap, en este caso voy a copiar el código del formulario horizontal, este precisamente: Horizontal Form

Y lo voy a colocar dentro del bloque «columna 8» que habíamos creado para organizar el espacio de nuestro template donde antes había un alert, quedando así:

«contacto.html»

{% extends "base/base.html" %}
{% block title %}Contacto{% endblock %}
{%block columna8 %}
<form>
  <div class="form-group row">
    <label for="inputEmail3" class="col-sm-2 col-form-label">Email</label>
    <div class="col-sm-10">
      <input type="email" class="form-control" id="inputEmail3" placeholder="Email">
    </div>
  </div>
  <div class="form-group row">
    <label for="inputPassword3" class="col-sm-2 col-form-label">Password</label>
    <div class="col-sm-10">
      <input type="password" class="form-control" id="inputPassword3" placeholder="Password">
    </div>
  </div>
  <fieldset class="form-group">
    <div class="row">
      <legend class="col-form-label col-sm-2 pt-0">Radios</legend>
      <div class="col-sm-10">
        <div class="form-check">
          <input class="form-check-input" type="radio" name="gridRadios" id="gridRadios1" value="option1" checked>
          <label class="form-check-label" for="gridRadios1">
            First radio
          </label>
        </div>
        <div class="form-check">
          <input class="form-check-input" type="radio" name="gridRadios" id="gridRadios2" value="option2">
          <label class="form-check-label" for="gridRadios2">
            Second radio
          </label>
        </div>
        <div class="form-check disabled">
          <input class="form-check-input" type="radio" name="gridRadios" id="gridRadios3" value="option3" disabled>
          <label class="form-check-label" for="gridRadios3">
            Third disabled radio
          </label>
        </div>
      </div>
    </div>
  </fieldset>
  <div class="form-group row">
    <div class="col-sm-2">Checkbox</div>
    <div class="col-sm-10">
      <div class="form-check">
        <input class="form-check-input" type="checkbox" id="gridCheck1">
        <label class="form-check-label" for="gridCheck1">
          Example checkbox
        </label>
      </div>
    </div>
  </div>
  <div class="form-group row">
    <div class="col-sm-10">
      <button type="submit" class="btn btn-primary">Sign in</button>
    </div>
  </div>
</form>
{%endblock %}
  {% block columna4 %}
<p>Columna</p>
  {% endblock %}

Pero vamos a borrar todos los campos, excepto el campo «Email» y el botón del final, vamos a ir añadiendo los nuestros propios, así que nos quedará algo así:

{% extends "base/base.html" %}
{% block title %}Contacto{% endblock %}
{%block columna8 %}
<form>
  <div class="form-group row">
    <label for="inputEmail3" class="col-sm-2 col-form-label">Email</label>
    <div class="col-sm-10">
      <input type="email" class="form-control" id="inputEmail3" placeholder="Email">
    </div>
  </div>
  <div class="form-group row">
    <!--Aquí estaba el campo password-->
  </div>
  <fieldset class="form-group">
    <div class="row">
     <!--Aquí los radio-->
    </div>
  </fieldset>
  <div class="form-group row">
   <!--Aquí los checkbox-->
  </div>
  <div class="form-group row">
    <div class="col-sm-10">
      <button type="submit" class="btn btn-primary">Sign in</button>
    </div>
  </div>
</form>
{%endblock %}
  {% block columna4 %}
<p>Columna</p>
  {% endblock %}

Esto se ve algo así:

formulario de contacto en Flask

Horrible!. ¿Verdad?.

Para ello voy a añadir un div con un estilo personal que tengo listo en mi CSS, podrás descargarlo al final.

Entonces nos va quedando algo así..

Formulario de contacto en FLASK 2

Fíjate que más allá de haber cambiado el botón para utilizar mi propio estilo, o habiendo quitado la clase de los label’s siempre dejamos la referencia que nos otorga Bootstrap, aunque la podemos cambiar claro después deberemos respetar estas en el controlador de nuestro proyecto. Me refiero a lo siguiente:

 

Etiquetas y propiedades de los elementos de un formulario HTML

Por ejemplo el botón de «Enviar» siempre debe tener la propiedad type = «submit»; de lo contrario nuestro controlador no sabría cual es el botón que procesa la información ingresada en el formulario. Así que vamos a aclarar ciertos puntos antes de continuar!

Un formulario normalmente cuenta con lo siguientes elementos:

La etiqueta FORM

  • Etiqueta «form»: La etiqueta form que abre el formulario es la más importante debido a que indica al navegador que lo que se encuentre dentro de estas pertenece a un formulario y por tanto debe ser interpretado como tal. Además en el caso de que trabajemos con cualquier lenguaje backend encargado de procesar el formulario debemos dentro de esta etiqueta indicar los siguientes atributos:
    • Atributo «Action»: Este atributo indica que acción se realizará al completar el formulario, es decir, al clikear el botón «submit» o «enviar». Si este no es especificado se asume que es la base del documento. Como en el ejemplo de solicitudes get y post en flask te mostré debemos indicar la url encargada de procesar este formulario para que FLASK busque la función en la ruta o url que especificamos.
    • El atributo «Method»: Este atributo indica mediante que método se accede a la acción que ejecuta el formulario, normalmente indicamos POST, debido a que se trata del método de procesamiento. Pero en realidad, para mostrarse el formulario primero se hace uso del método GET y luego al ser rellenado y procesado (submit) hace uso de POST para ejecutar la acción (action).
    • Atributo «Enctype»: Este atributo indica que tipo de encriptación se utilizará para encriptar los datos enviados. Nosotros aún no lo utilizamos, pero es importante ya ir conociéndolo para cuando hagamos algún formulario de Registro/Login más adelante.

Etiqueta INPUT:

  • Etiqueta «input»: Esta etiqueta indica una entrada de datos por parte del usuario. Obviamente este tipo de entrada puede variar en el tipo de datos. Pero lo importante es destacar los siguientes atributos:
    • El atributo «name»: Este atributo es el que considero más importante y en el que no podemos equivocarnos, debido a que este debe coincidir al momento de procesar el formulario del lado del controlador. Debes prestar especial atención a que el nombre que le colocas al campo de entrada de datos coincida al momento de procesar el formulario. Así tanto este atributo en cada campo como la url del «action» del form, no pueden ser diferentes en el controlador u obtendrás algún error, o por defecto no pasará «nada». El nombre puede ser cualquiera, no necesariamente debe indicar el tipo de dato, y debe evitar repetirse para evitar confusiones al procesarlos.
    • Atributo «type»: Este atributo indica el tipo de dato que se permite ingresar en el campo. Existen múltiples tipos de entrada de datos, a continuación los nombro todos pero no entro en detalle: (button, checkbox, color, date, datetime, email, file, hidden, month, number, password, radio, range, reset, search, submit, tel, text,  time, url, week). El tipo de dato que coloques en este atributo determina como se mostrará el campo de formulario. Por ejemplo si colocas [type= «password»] al escribir en el campo automáticamente se mostrarán los asteriscos ocultando los caracteres del password.
    • Otros atributos opcionales: Podemos colocar otros atributos opcionalmente para limitar por ejemplo el máximo de caracteres admitidos (MAXLENGTH, SIZE) o VALUE para indicar el valor por defecto del campo. Tampoco entraré en mucho detalle porque esto es más HTML básico que Python. Pero es importante remarcarlo!

Etiqueta SELECT

  • La etiqueta «select»: Esta etiqueta nos permite añadir un campo con opciones desplegables para que el usuario marque o se decida por alguna de las que nosotros ofrecemos. Por ejemplo:
    • <SELECT NAME="bebida">
      <OPTION selected>Agua
      <OPTION>Cerveza
      <OPTION VALUE=refresco>Refresco Gaseoso
      <OPTION>Vino
      <OPTION>Jugo
      </SELECT>

       

Y esto es todo lo que veremos por ahora, finalmente luego vamos a crear una famosa entrada de «cero a cien» y explicare más detallada y puntualmente cada campo y atributo.

LO MÁS IMPORTANTE EN LA VIDA

formularios en flask otra vez

 

Lo más importante es que comprendas que el atributo «action» del FORM debe coincidir con la url de la ruta en donde se procesa este formulario (CONTROLADOR). Y cada atributo «name» de cada INPUT debe coincidir también en la parte del código donde procesamos el formulario para obtener los datos de cada input (como te muestro en la imagen). Si colocamos otro nombre, no obtendremos los datos del campo que queremos hacerlo.

 

Mi formulario de contacto en FLASK + ReCaptcha

Finalmente me he decidido por incorporar en mi formulario únicamente 3 campos; El nombre, el correo y el mensaje. Nada de complicarle la vida a nuestro visitante / cliente. Pero eso sí vamos a incluir un ReCaptcha para evitar los molestos bot’s manda spam’s que andan dando vuelta por la red.

Primero fíjate como he modificado mi formulario y vamos a tratar de entender un poco el código!

formulario de contacto en flask

 

Aquí esta el código de «contacto.html»

{% extends "base/base.html" %}
{% block title %}Contacto{% endblock %}
{%block columna8 %}
<div class ="contenedor-neon-int">
  <div class = "title-neon" >
    <h3>Enviar mensaje a través del espacio</h3>
    <p class="mb-4"></p>
    <p class = "p-neon">Seguro tenemos mucho de que hablar! Puedes contactarme a través del siguiente formulario:</p>
    <br>

  </div>
<form class = "form-horizontal">
  
  <div class="form-group">
    
    <div class="col-sm-10 ml-auto mr-auto">
      <p>Tu Nombre:</p>
      <input type="name" name = "Nombre" class="form-control" placeholder="Nombre">
    </div>
  </div>
  <div class="form-group">
    <div class="col-sm-10 ml-auto mr-auto">
      <p>Tu Email:</p>
      <input type="email" name = "Email" class="form-control" placeholder="Email">
    </div>
  </div>
  <div class="form-group">
    <div class="col-sm-10 ml-auto mr-auto">
      <p>Tu mensaje:</p>
      <textarea name = "Mensaje" rows="5" cols="20" class="form-control" placeholder="Mensaje"></textarea>
    </div>
  </div>
    <div class="col-sm-10 ml-auto mr-auto">
      <br>
      <div align = "right">
      <a href="#"><button type="submit">
        <span></span>
        <span></span>
        <span></span>
        <span></span>
        ENVIAR MENSAJE!
      </button></a>
    </div>
    </div>
    <br>
  </div>
</form>
{%endblock %}
  {% block columna4 %}
<p>Columna</p>
  {% endblock %}

Prestemos atención a partir de la etiqueta «form» ya que el resto son divs con clases que he creado para estilizar mi portfolio con CSS. Si te fijas en la etiqueta form tiene una clase predeterminada de Bootstrap para crear formularios horizontales, porque bien podemos alinear todos los campos en vertical. ¿Pero, que le está faltando a este formulario?. Ya desde el inicio deberías notar que falta lo más importante!

El atributo «action» y el «method» por el cual se procesará el formulario!!. Así que vamos a ello:

En nuestro formulario agregamos:

<form action = "/contacto" method = "post" class = "form-horizontal">

 

Y con eso ya nos aseguraremos de procesar el formulario posteriormente en nuestra ruta «contacto» de nuestro controlador. Ahora siguiendo con el código HTML del Formulario, podemos distinguir 3 campos INPUT uno llamado «Nombre«, «Email» y «Mensaje«. Estos nombres definidos en la propiedad «name» y que son los que utilizaremos para identificarlos en nuestro controlador al procesarlos. Además añadí algunos atributos extra para hacerlo un poco más accesible y añadí 5 lineas al campo mensaje.

Pero creo que sería útil añadir un Captcha y para ello podemos usar el de Google.

 

Incluir ReCaptcha en formulario de contacto HTML en Flask sin librerías

Para empezar, bueno, no voy a explicar lo que es un Captcha ni para que sirve, creo que eso hasta los robots lo saben jiji.

formularios de contacto en flask con recaptcha

Vamos a registrarnos aquí:

https://www.google.com/recaptcha/admin/create

Como no tenemos ni un puñetero domino vamos a ingresar «localhost» o bien «127.0.0.1» completando el registro así:

En mi caso elegí la versión 2 de ReCaptcha. Y finalmente damos clic en «Enviar» y deberíamos obtener unas llaves como la imagen que sigue:

 

 

 

 

Estas llaves las debes de copiar en algún archivo de texto en tu pc, porque pronto las vamos a utilizar para validar que nuestro emisor no es un robotito.

 

 

Bien ahora hay dando vuelta por la red diversas formas de incluir y trabajar con ReCaptcha. Yo incluiré la más básica y que mejor me ha funcionado:

Para poder utilizar Google ReCaptcha debemos primero añadir en la parte de la VISTA, que actualmente estamos trabajando sobre «contacto.html» un div que incluya el código JS de ReCaptcha y también claro el control o elemento check clásico «No soy un robot» y esto lo hacemos incluyendo:

Dentro del HEAD de nuestro template HTML:
<script src="https://www.google.com/recaptcha/api.js"></script>

 

 

Aquí tenemos un pequeño problema

-Pero Mariano, ¿Cómo incluyo código dentro del HEAD en «contacto.html» si es un template que extiende de «base.html«?. Auxilio!!

-¿O lo incluimos en el base.html directamente?

pero claro-Claro pichón! El archivo «contacto.html» no tiene etiqueta HEAD, porque usa la del «base.html«. Pero, incluir este script en base.html a pesar de que funcionaria sería algo medio estúpido, ya que solo lo utilizaremos en el apartado de contacto. ¿Y para qué lo vamos a cargar en toda la web entonces?. Eso recargaría mucho el sitio, porque cada página que carguemos deberá buscar e incluir este script JS. Pero para todo en esta vida hay una solución, bueno menos para la muerte claro..

 

Si has entendido más o menos el funcionamiento de JINJA2 en flask habrás tenido la brillante idea de crear un bloque en «base.html» (PLANTILLA PADRE) para incluir cada script en «contacto.html» u otras PLANTILLAS HIJAS. Para ello simplemente en base.html dentro de la etiqueta HEAD añadimos:

{%block head%}
{%endblock%}

Y entonces ya podremos en las plantillas head incluir scripts según los necesitemos en cada una. Y no estaremos cargando innecesariamente nada más en nuestra web. Así queda «base.html«:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>{% block title%} {%endblock%}</title>
    <!--BOOTSTRAP CSS --> 
    <link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.4.1/css/bootstrap.min.css" integrity="sha384-Vkoo8x4CGsO3+Hhxv8T/Q5PaXtkKtu6ug5TOeNV6gBiFeWPGFN9MuhOf23Q9Ifjh" crossorigin="anonymous">
    <!--MI CSS PERSONAL --> 
    <link rel="stylesheet" href="{{ url_for('static', filename='css/base.css' ) }}">
    {%block head%}
    {%endblock%}
</head>
<body>
    <div id = "capa1">
    {% include "nav/nav-bar.html" %}
    {%block inicio%}
    {%endblock%}

        <div class="container">    <!-- Aquí estamos usando la clase "container" de Bootstrap --> 
            <div class="row">    <!-- Aquí estamos usando la clase "row" que significa "Fila", "o hilera"-->
            <div class="col-8"> <!-- Aquí nos esta creando una columna de tamaño 8--> 
                {% block columna8 %}
            
                  {% endblock %}
            </div>   
            <div class="col-4">
                {% block columna4 %}

                {% endblock %}



            </div>     <!-- Aquí nos esta creando una columna de tamaño 4--> 
          </div>                               <!--Cierra el row--> 
      </div>                                   <!-- Cierra el container-->
    </div>
<!-- jQuery first, then Popper.js, then Bootstrap JS --> 
<script src="https://code.jquery.com/jquery-3.4.1.slim.min.js" integrity="sha384-J6qa4849blE2+poT4WnyKhv5vZF5SrPo0iEjwBvKU7imGFAV0wwj1yYfoRSJoZ+n" crossorigin="anonymous"></script> 
<script src="https://cdn.jsdelivr.net/npm/popper.js@1.16.0/dist/umd/popper.min.js" integrity="sha384-Q6E9RHvbIyZFJoft+2mJbHaEWldlvI9IOYy5n3zV9zzTtmI3UksdQRVvoxMfooAo" crossorigin="anonymous"></script> 
<script src="https://stackpath.bootstrapcdn.com/bootstrap/4.4.1/js/bootstrap.min.js" integrity="sha384-wfSDF2E50Y2D1uUdj0O3uMBJnjuUD4Ih7YwaYd1iqfktj0Uod8GCExl3Og8ifwB6" crossorigin="anonymous"></script>   
<script src="{{ url_for('static', filename='js/tittle-efect.js' ) }}"></script> 
</body>
</html>

 

Ahora en nuestro «contacto.html» usamos este bloque añadiendo la llamada al script de Google ReCaptcha:

{% extends "base/base.html" %}
{%block head%}
<script src="https://www.google.com/recaptcha/api.js"></script>
{%endblock%}
{% block title %}Contacto{% endblock %}
{%block columna8 %}
<div class ="contenedor-neon-int">
  <div class = "title-neon" >
    <h3>Enviar mensaje a través del espacio</h3>
    <p class="mb-4"></p>
    <p class = "p-neon">Seguro tenemos mucho de que hablar! Puedes contactarme a través del siguiente formulario:</p>
    <br>

  </div>
<form action = "/contacto" method = "post" class = "form-horizontal">
  
  <div class="form-group">
    
    <div class="col-sm-10 ml-auto mr-auto">
      <p>Tu Nombre:</p>
      <input type="name" name = "Nombre" class="form-control" placeholder="Nombre">
    </div>
  </div>
  <div class="form-group">
    <div class="col-sm-10 ml-auto mr-auto">
      <p>Tu Email:</p>
      <input type="email" name = "Email" class="form-control" placeholder="Email">
    </div>
  </div>
  <div class="form-group">
    <div class="col-sm-10 ml-auto mr-auto">
      <p>Tu mensaje:</p>
      <textarea name = "Mensaje" rows="5" cols="20" class="form-control" placeholder="Mensaje"></textarea>
    </div>
  </div>
    <div class="col-sm-10 ml-auto mr-auto">
      <br>
      <div align = "right">
      <a href="#"><button type="submit">
        <span></span>
        <span></span>
        <span></span>
        <span></span>
        ENVIAR MENSAJE!
      </button></a>
    </div>
    </div>
    <br>
  </div>
</form>
{%endblock %}
  {% block columna4 %}
  {{ recaptcha }} 
  {% endblock %}

Ahora si te fijas en el navegador en tu página de contacto, haz clic derecho, ver código fuente de la página y verás como se incluyó el script. Recuerda que debe ir dentro del HEAD he!!:

formulario de contacto en flask - head

Ahora finalmente incluimos la caja de Check clásica del captcha en contacto.html dentro del form pero utilizando los bloques JINJA2 para incluir la llave que obtuvimos anteriormente. (Podríamos incluir la llave directamente, pero es mejor hacerlo de esta forma en el caso que expire o queramos cambiar de dominio, simplemente entonces deberíamos modificar el controlador y no la parte visual.)

<div class="g-recaptcha" align = "center" data-sitekey="{{ sitekey }}"></div>

 

El bloque «{{sitekey}}» representa una variable que luego tomará el valor que nosotros LE PASEMOS COMO ARGUMENTO PARA EL PARÁMETRO DEL RENDER DE ESTE TEMPLATE DENTRO DEL CONTROLADOR O RUTAS.

Entonces el código de nuestro formulario «contacto.html» queda:

{% extends "base/base.html" %}
{%block head%}
<script src="https://www.google.com/recaptcha/api.js"></script>
{%endblock%}
{% block title %}Contacto{% endblock %}
{%block columna8 %}
<div class ="contenedor-neon-int">
  <div class = "title-neon" >
    <h3>Enviar mensaje a través del espacio</h3>
    <p class="mb-4"></p>
    <p class = "p-neon">Seguro tenemos mucho de que hablar! Puedes contactarme a través del siguiente formulario:</p>
    <br>

  </div>
<form action = "/contacto" method = "post" class = "form-horizontal">
  
  <div class="form-group">
    
    <div class="col-sm-10 ml-auto mr-auto">
      <p>Tu Nombre:</p>
      <input type="name" name = "Nombre" class="form-control" placeholder="Nombre">
    </div>
  </div>
  <div class="form-group">
    <div class="col-sm-10 ml-auto mr-auto">
      <p>Tu Email:</p>
      <input type="email" name = "Email" class="form-control" placeholder="Email">
    </div>
  </div>
  <div class="form-group">
    <div class="col-sm-10 ml-auto mr-auto">
      <p>Tu mensaje:</p>
      <textarea name = "Mensaje" rows="5" cols="20" class="form-control" placeholder="Mensaje"></textarea>
    </div>
  </div>
  <div class="g-recaptcha" align = "center" data-sitekey="{{ sitekey }}"></div>
    <div class="col-sm-10 ml-auto mr-auto">
      <br>
      <div align = "right">
      <a href="#"><button type="submit">
        <span></span>
        <span></span>
        <span></span>
        <span></span>
        ENVIAR MENSAJE!
      </button></a>
    </div>
    </div>
    <br>
  </div>
</form>
{%endblock %}
  {% block columna4 %} 
  {% endblock %}

Aún así no se mostrará todavía.. Porque debemos ahora trabajar en el procesamiento de ese Captcha y las configuraciones pertinentes!!

 

contacto en flask

Ahora lo que sigue es programar el controlador de nuestro formulario, para ello nos vamos al archivo «run.py» y modificamos la ruta «/contacto» para admitir el método POST.

Como ves a la derecha bastará con añadir «POST» a la lista de argumentos del parámetro Methods de nuestra ruta «contacto«.

Se me había olvidado que debíamos crear o cambiar a una rama «Controlador» en git para luego fusionar a la rama Desarrollo. Pero tu ya debes saber hacerlo!

 

 

Y lo siguiente sería comenzar a trabajar en el procesamiento del formulario + el captcha!

 

Procesando un formulario de contacto HTML + Google ReCaptcha

get y post formulario flaskEn nuestro controlador «run.py» en la ruta contacto, tal como ya lo hicimos en otro post debemos incluir:

  1. Una sentencia de condición o condicional que determine si se trata de un método POST o un GET. Y en cada una de ellas realizar diferentes acciones.
  2. En el caso de GET debemos procesar la solicitud mostrando el formulario, y por supuesto al renderizar la página debemos incluir esos «datos» de la llave de ReCaptcha que obtuvimos de google!. Para GET solo se trata de cargar todo correctamente.
  3. En el caso de que la solicitud sea POST quiere decir que ya se ha procesado una solicitud GET antes (obviamente, porque nuestro cliente cargó el formulario), el usuario completó el formulario y nos está solicitando enviar los datos que colocó en cada campo input. Así que debemos extraerlos del formulario y procesarlos correctamente a cada uno para luego confirmarle mediante algún mensaje que ha sido un éxito o en caso contrario informar un error:
Comparar la respuesta del Captcha con el servidor de Google

Lo primero que tenemos que hacer es crear una función que compruebe y compare la respuesta del Captcha de nuestro visitante con Google ReCaptcha. Esta función la llamaremos «comprobar_humano». Y requeriremos importar las siguientes librerías:

  • requests : Librería que nos permite realizar solicitudes HTTP (GET O POST) a sitios webs o servidores externos.
  • json: Librería que incorpora compatibilidad con JavaScript Object Notation (Base de datos no relacionales)

 

Así que las importamos junto con las demás que importamos antes:

from flask import Flask, render_template, request
import requests, json

 

Yo tuve un problema al importar requests
En mi caso he tenido un problema al importar requests. Si es tu caso recuerda que primero debes instalarlo en tu entorno virtual usando «pip«:

#INSTALAR CON ENTORNO VIRTUAL ACTIVO!

pip install requests

pip install json

#Seguido a ello lo podemos importar de la siguiente manera:

import requests, json

#En caso de que Visual Studio Code marque un error similar al siguiente:

#Import «requests» could not be resolved from source Pylance (reportMissingModuleSource)

#Se trata de un error de VSCode y el PATH o Interprete que estas utilizando, para ello simplemente:

CTRL + SHIFT + P

Y escribe «seleccionar interprete». Allí selecciona el interprete de Python3 correcto.

 

Y nuestra función la ubicaremos al final de nuestro archivo «run.py«, no dentro de la ruta:

def comprobar_humano(respuesta_del_captcha):
    secret = "tu-clave-secreta-recaptcha"
    payload = {'response':respuesta_del_captcha, 'secret':secret}
    response = requests.post("https://www.google.com/recaptcha/api/siteverify", payload)
    response_text = json.loads(response.text)
    return response_text['success']

Esta función lo que hace es enviar una solicitud POST desde el servidor en que se ejecute nuestra aplicación, en este caso tu ordenador claro, al sitio web de Google ReCaptcha con lo que nuestro visitante completo en el Captcha del sitio como argumento para el parámetro «respuesta_del_captcha» y Google le retornará un success en caso de que coincida tanto la clave privada que nosotros obtuvimos al registrarnos como el captcha que completó nuestro visitante.

Así que ahora simplemente podemos continuar por crear un condicional en nuestra ruta «contacto» que en caso de obtener un «success» del servidor de google simplemente permita enviar los datos que nuestro visitante completo en el  formulario. Pero si no coinciden debemos informarle el error para que complete un nuevo Captcha.

Así queda «run.py» por ahora:

#Importar
from flask import Flask, render_template, request
import requests, json
#Crear app mediante instancia

app = Flask(__name__)

#Crear rutas con sus correspondientes funciones
#INICIO
@app.route('/', methods=['GET'])
def index():
    return render_template('/index.html')
#MI BLOG
@app.route('/blog', methods=['GET'])
def blog():
    return render_template('/blog.html')

#MIS PROYECTOS
@app.route('/mis-proyectos', methods=['GET'])
def mostrarproyectos():
    return render_template('mis-proyectos.html')

#MIS HABILIDADES
@app.route('/habilidades', methods=['GET'])
def habilidades():
    return render_template('mis-habilidades.html')
#CONTACTO
@app.route('/contacto', methods=['GET', 'POST'])
def contacto():
    return render_template('/contacto.html')


def comprobar_humano(respuesta_del_captcha):
    secret = "tu-clave-secreta-recaptcha"
    payload = {'response':respuesta_del_captcha, 'secret':secret}
    response = requests.post("https://www.google.com/recaptcha/api/siteverify", payload)
    response_text = json.loads(response.text)
    return response_text['success']
#Ejecutar nuestra app cuando ejecutemos este archivo run.py
if __name__ == '__main__':
    app.run(debug=True)

Ahora recuerda colocar tu clave secreta en la variable secret. Finalmente debemos como paso siguiente procesar la solicitud en caso de éxito. Para ello vamos a crear un condicional que compruebe si estamos ante una solicitud POST o GET en nuestra ruta de «Contacto»:

#CONTACTO
@app.route("/contacto", methods=["GET", "POST"])
def contacto():
    sitekey = "clave-del-sitio-google-recaptcha"

    if request.method == "POST":
        name = request.form['Nombre']
        email = request.form['Email']
        Mensaje = request.form['Mensaje']
        respuesta_del_captcha = request.form['g-recaptcha-response']
        
       if comprobar_humano(respuesta_del_captcha):
           #Si devuelve True
            status = "Exito."
            print (status)
        else:
           #Si devuelve False
            status = "Error, vuelve a comprobar que no eres un robot."
            print (status)

    return render_template("contacto.html", sitekey=sitekey)

 

explicando codigo flaskVamos a explicar un poco el código de nuestra ruta contacto. Primeramente estamos admitiendo solicitudes tanto GET como POST. E indicamos en la función contacto primero la variable «sitekey» que se corresponde a la «clave de sitio que nos otorgó Google ReCaptcha» y finalmente continuamos con un condicional que comprueba si se trata de una solicitud POST para obtener los datos del formulario. Así obtiene los datos de los campos input del formulario HTML utilizando la librería request, incluido el captcha. Y finalmente comprueba la respuesta de la función «comprobar_humano», si esta es True la variable «status» se asigna un valor, y si es False, otro. La última línea es aquella que escapa al condicional que comprueba si era un método POST, y por lo tanto se ejecuta si se trata de un método GET.

El funcionamiento básico de esta ruta dicta que ni bien cargamos la url «/contacto» se trata de una solicitud GET por lo tanto carga la plantilla HTML y le asigna el valor de la clave de Google ReCaptcha a la variable «sitekey«, y finalmente al completar el formulario y dar a «enviar» o «submitt» se vuelve a cargar siendo una solicitud POST y por ello ya entra dentro de el condicional correspondiente a realizar el resto del proceso.

Funciona, de puta madre!. Pero aún no se envía ningún mensaje, pues no hemos trabajado aún esa parte. Sin embargo ya tenemos lista la parte del captcha!

Código completo del archivo controlador «run.py«:

#Importar
from flask import Flask, render_template, request
import requests, json #Solución import requests from venv
#Crear app mediante instancia


app = Flask(__name__)

#Crear rutas con sus correspondientes funciones
#INICIO
@app.route('/', methods=['GET'])
def index():
    return render_template('/index.html')
#MI BLOG
@app.route('/blog', methods=['GET'])
def blog():
    return render_template('/blog.html')

#MIS PROYECTOS
@app.route('/mis-proyectos', methods=['GET'])
def mostrarproyectos():
    return render_template('mis-proyectos.html')

#MIS HABILIDADES
@app.route('/habilidades', methods=['GET'])
def habilidades():
    return render_template('mis-habilidades.html')

#CONTACTO
@app.route("/contacto", methods=["GET", "POST"])
def contacto():
    sitekey = "clave-del-sitio-google-recaptcha"

    if request.method == "POST":
        name = request.form['Nombre']
        email = request.form['Email']
        Mensaje = request.form['Mensaje']
        respuesta_del_captcha = request.form['g-recaptcha-response']
        
        if comprobar_humano(respuesta_del_captcha):
           #Si devuelve True
            status = "Exito."
            print (status)
        else:
           #Si devuelve False
            status = "Error, vuelve a comprobar que no eres un robot."
            print (status)

    return render_template("contacto.html", sitekey=sitekey)


def comprobar_humano(respuesta_del_captcha):
    secret = "tu-clave-secreta-recaptcha"
    payload = {'response':respuesta_del_captcha, 'secret':secret}
    response = requests.post("https://www.google.com/recaptcha/api/siteverify", payload)
    response_text = json.loads(response.text)
    return response_text['success']


#Ejecutar nuestra app cuando ejecutemos este archivo run.py
if __name__ == '__main__':
    app.run(debug=True)

Recuerdas debes reemplazar «nuestra-clave-secreta-del-sitio-otorgada-por-Google-ReCaptcha» por tu clave de sitio al registrarlo en ReCaptcha. Y «clave-secreta-otorgada-por-Google-ReCaptcha» por la clave secreta igualmente obtenida al momento del registro. Y con esto tenemos un formulario de contacto en flask casi listo, solo no estaría faltando la parte «funcional» del mismo. Haz la prueba a simular enviar un mensaje y en tu terminal deberías tener lo siguiente:

exitooooo-

 

Código final del formulario y nuestro controlador:

 

Código de «run.py«:

#Importar
from flask import Flask, render_template, request
import requests, json #Solución import requests from venv
#Crear app mediante instancia


app = Flask(__name__)

#Crear rutas con sus correspondientes funciones
#INICIO
@app.route('/', methods=['GET'])
def index():
    return render_template('/index.html')
#MI BLOG
@app.route('/blog', methods=['GET'])
def blog():
    return render_template('/blog.html')

#MIS PROYECTOS
@app.route('/mis-proyectos', methods=['GET'])
def mostrarproyectos():
    return render_template('mis-proyectos.html')

#MIS HABILIDADES
@app.route('/habilidades', methods=['GET'])
def habilidades():
    return render_template('mis-habilidades.html')

#CONTACTO
@app.route("/contacto", methods=["GET", "POST"])
def contacto():
    sitekey = "clave-del-sitio-google-recaptcha"

    if request.method == "POST":
        name = request.form['Nombre']
        email = request.form['Email']
        Mensaje = request.form['Mensaje']
        respuesta_del_captcha = request.form['g-recaptcha-response']
        
        if comprobar_humano(respuesta_del_captcha):
           #Si devuelve True
            status = "Exito."
            print (status)
        else:
           #Si devuelve False
            status = "Error, vuelve a comprobar que no eres un robot."
            print (status)

    return render_template("contacto.html", sitekey=sitekey)


def comprobar_humano(respuesta_del_captcha):
    secret = "tu-clave-secreta-recaptcha"
    payload = {'response':respuesta_del_captcha, 'secret':secret}
    response = requests.post("https://www.google.com/recaptcha/api/siteverify", payload)
    response_text = json.loads(response.text)
    return response_text['success']


#Ejecutar nuestra app cuando ejecutemos este archivo run.py
if __name__ == '__main__':
    app.run(debug=True)

 

Código de «contacto.html«:

{% extends "base/base.html" %}
{%block head%}
<script src="https://www.google.com/recaptcha/api.js"></script>
{%endblock%}
{% block title %}Contacto{% endblock %}
{%block columna8 %}
<div class ="contenedor-neon-int">
  <div class = "title-neon" >
    <h3>Enviar mensaje a través del espacio</h3>
    <p class="mb-4"></p>
    <p class = "p-neon">Seguro tenemos mucho de que hablar! Puedes contactarme a través del siguiente formulario:</p>
    <br>

  </div>
  <form action = "/contacto" method = "post" class = "form-horizontal">
  
  <div class="form-group">
    
    <div class="col-sm-10 ml-auto mr-auto">
      <p>Tu Nombre:</p>
      <input type="name" name = "Nombre" class="form-control" placeholder="Nombre">
    </div>
  </div>
  <div class="form-group">
    <div class="col-sm-10 ml-auto mr-auto">
      <p>Tu Email:</p>
      <input type="email" name = "Email" class="form-control" placeholder="Email">
    </div>
  </div>
  <div class="form-group">
    <div class="col-sm-10 ml-auto mr-auto">
      <p>Tu mensaje:</p>
      <textarea name = "Mensaje" rows="5" cols="20" class="form-control" placeholder="Mensaje"></textarea>
    </div>
  </div>
  <div class="g-recaptcha" align = "center" data-sitekey="{{sitekey}}"></div>
    <div class="col-sm-10 ml-auto mr-auto">
      <br>
      <div align = "right">
      <a href="#"><button type="submit">
        <span></span>
        <span></span>
        <span></span>
        <span></span>
        ENVIAR MENSAJE!
      </button></a>
    </div>
    </div>
    <br>
  </div>
</form>
{%endblock %}
  {% block columna4 %}
  {% endblock %}

 

 

Vamos a aprender la segunda forma de crear formularios usando una librería.

 

 


Formulario de contacto en flask usando WTFORMS + ReCaptcha (FORMA 2)

Te había dicho anteriormente que hay dos formas de crear formularios funcionales para utilizar con flask. La primera utilizamos HTML para crear un formulario de contacto en flask. En esta segunda vamos a crear el mismo formulario pero utilizando una librería desarrollada para facilitarnos un poco el trabajo y la organización de nuestro proyecto. Para esto no voy a borrar mi formulario HTML, sino que vamos a duplicarlo en otro archivo llamado «contacto2.html» y añadiendo su correspondiente ruta en «run.py» y al final vamos a crear la parte funcional de tal formulario, digamos la función de enviar el mensaje que escriba el visitante a nuestra casilla de correo.

Primero lo primero, vamos a activar nuestro entorno virtual si aún no está activado. Y vamos a instalar WTFORMS además de conocer bien las características que nos ofrece:

pip3 install Flask-WTF

o bien:

python3 -m pip install Flask-WTF

Ventajas de utilizar WTForms

Las ventajas más resaltadas a la hora de utilizar esta librería para nuestros formularios son:

  • No nos tenemos que preocupar por validar los datos antes de procesarlos. Es decir normalmente la librería se encargará de este trabajo.
  • No necesitamos utilizar etiquetas HTML.
  • Nos brinda mayor seguridad ante la falsificación de requests utilizando CSRF ( XSRF, enlace hostil, ataque de un click, cabalgamiento de sesión, y ataque automático)
  • Además es muy sencillo de utilizar ya que se basa en el uso de clases y nos permite organizar nuestro formulario en un archivo aparte que normalmente llamamos «forms.py».

Esta librería es muy utilizada para crear formularios de registro y login, la verdad la he utilizado y me ha facilitado mucho el trabajo gracias a los parámetros mediante los cuales nos permite modificar requerimientos fácilmente.

Vamos a ello, una vez instalada vamos a crear otra ruta en nuestro archivo controlador «run.py« llamada «contacto2» y también vamos a crear un nuevo archivo al que llamaremos «forms.py» donde allí vamos a trabajar todos los formularios de nuestro sitio por separado de nuestro controlador, pero incluyéndolo dentro de él. Así:

 

Paso 1: Crear forms.py e importar la clase FlaskForm

  • Crear el archivo «forms.py» en el directorio principal de nuestro proyecto, es decir, junto a «run.py»
  • Dentro de él importar la clase FlaskForm así:

crear formulario de contacto en flask

 

 

En caso de problema al importar librerías en Visual Studio

err-min

En caso de errores de este tipo en el que no se encuentra la librería o modulo que importamos debemos revisar de estar utilizando el INTERPRETE de PYTHON3 de nuestro ENTORNO VIRTUAL y no de nuestro SOFTWARE PARTICULAR… Fíjate presiona Ctrl + SHIFT + P:

seleccionar interprete de python

Y ahora sí, acabamos de importar nuestra clase FlaskForm sin ningún error.

 

Paso 2: Importar los elementos a utilizar

Ahora bien también debemos importar de ella los elementos del formulario que vamos a utilizar, recordemos que en nuestro caso es solo un formulario de contacto en flask con 3 campos y un Captcha (que también vamos a incluir luego). Así que manos a la obra, trata de recordar los nombres de las clases y objetos. Nuestro archivo «forms.py» queda así:

#Importar
from flask_wtf import FlaskForm
#Aquí importamos, campodetexto, validadoresdedatos, y el boton submit
from wtforms import StringField, validators, SubmitField
#Aquí de los validadores importamos el dato obligatorio
from wtforms.validators import DataRequired

 

Puedes leer la documentación de WTForms
Una práctica importante de todo desarrollador de altura es leer la documentación de las librerías que utiliza o conoce para asegurarse de que la esta aprovechando al máximo y la está utilizando de forma correcta. Además claro de que la documentación es la que nos enseña como utilizarla. Si quieres leer la documentación de WTForms para saber todo lo que puedes hacer con esta librería aquí te la dejo: https://wtforms.readthedocs.io/en/2.3.x/

 

Paso 3: Crear la clase de nuestro formulario heredando de FlaskForm

Y con ello ya podemos empezar a crear nuestro formulario básico. Lo primero es crear una clase y darle nombre heredando de la clase FlaskForm y dentro de esta clase definimos los atributos de nuestro formulario, en nuestro caso los campos así:

#Importar
from flask_wtf import FlaskForm
#Aquí importamos, campodetexto, validadoresdedatos, y el boton submit
from wtforms import StringField, validators, SubmitField
#Aquí de los validadores importamos el datoobligatorio 
from wtforms.validators import DataRequired

class miformulario(FlaskForm):
    name = StringField(label='Nombre', validators=[DataRequired()])
    email = StringField(label='Email', validators=[DataRequired()])
    message = StringField(label='Mensaje')
    submit = SubmitField(label="Enviar")

Fíjate con atención, dentro de la clase colocamos el nombre del campo, por ejemplo «name» y luego el tipo de dato que acepta según esta librería que utilizamos «StringField» (campo de texto), tanto el nombre, email y mensaje son campos de texto, excepto el botón enviar que es un Submit. Y dentro de cada atributo colocamos los argumentos para los parametros admitidos, en este caso los validators donde especificamos que es un dato obligatorio con «DataRequired«, aquí también podemos añadir otros atributos como la longitud máxima o mínima de caracteres admitidos en el campo «max_length«,etc. Lee la documentación!!

Y bien, ahora continuemos importando este formulario que se encuentra dentro de «forms.py» a «run.py» para procesarlo en nuestra ruta que vamos a crear nueva llamada «contacto2«.

En el archivo «run.py«:

from forms import miformulario

#Porque el archivo se llama "forms.py" y mi formulario dentro se llama "miformulario" derivado
#de la clase FlaskForm

Y creo la ruta «contacto2» debajo de la anterior «contacto«, en este caso añadí un simple pass por ahora, luego voy a editar:

#CONTACTO2 - FLASKFORMS WTFORMS
@app.route("/contacto2", methods=["GET", "POST"])
def contacto2():
    pass

Y nuestro «run.py» queda así:

#Importar
from flask import Flask, render_template, request
import requests, json #Solución import requests from venv
from forms import miformulario


#Crear app medante instancia


app = Flask(__name__)

#Crear rutas con sus correspondientes funciones
#INICIO
@app.route('/', methods=['GET'])
def index():
    return render_template('/index.html')
#MI BLOG
@app.route('/blog', methods=['GET'])
def blog():
    return render_template('/blog.html')

#MIS PROYECTOS
@app.route('/mis-proyectos', methods=['GET'])
def mostrarproyectos():
    return render_template('mis-proyectos.html')

#MIS HABILIDADES
@app.route('/habilidades', methods=['GET'])
def habilidades():
    return render_template('mis-habilidades.html')

#CONTACTO
@app.route("/contacto", methods=["GET", "POST"])
def contacto():
    sitekey = "clave-del-sitio-google-recaptcha"

    if request.method == "POST":
        name = request.form['name']
        email = request.form['email']
        Mensaje = request.form['Mensaje']
        respuesta_google = request.form['g-recaptcha-response']
        
        if comprobar_humano(respuesta_google):
           #Si devuelve True
            status = "Exito."
        else:
           #Si devuelve False
            status = "Error, vuelve a comprobar que no eres un robot."
        
        print(status)

    return render_template("contacto.html", sitekey=sitekey)

#CONTACTO2 - FLASKFORMS WTFORMS
@app.route("/contacto2", methods=["GET", "POST"])
def contacto2():
    pass


def comprobar_humano(respuesta_del_captcha):
    secret = "tu-clave-secreta-recaptcha"
    payload = {'respuesta':respuesta_del_captcha, 'clave_secreta':secret}
    response = requests.post("https://www.google.com/recaptcha/api/siteverify", payload)
    response_text = json.loads(response.text)
    return response_text['success']


#Ejecutar nuestra app cuando ejecutemos este archivo run.py
if __name__ == '__main__':
    app.run(debug=True)

Fíjate que aceptamos GET y POST también en esta ruta. Ahora vamos a llamar nuestro formulario y añadirlo como argumento del return para que se rende-rice al cargar la url:

@app.route("/contacto2", methods=["GET", "POST"])
def contacto2():
    miform = miformulario()
    return render_template("contacto2.html", form=miform)

De esta manera estamos definiendo la ruta contacto2 y dentro de la función creamos una instancia de la clase «miformulario» que deriva de FlaskForm que es la que creamos en el archivo «forms.py» y lo importamos a «run.py«. ¿Recuerdas?. Y luego lo estamos pasando como argumento indicando que al cargar el template «contacto2.html» enviamos la variable «form» que contiene el formulario 😉

Ahora vamos ver como procesaríamos este formulario:

Para procesar el formulario lo hacemos así:

#CONTACTO2 - FLASKFORMS WTFORMS
@app.route("/contacto2", methods=["GET", "POST"])
def contacto2():
    miform = miformulario()
    if miform.validate_on_submit():
       print(f"Name:{miform.name.data},Email:{miform.email.data},message:{miform.message.data}"))
    else:
        print("Algún dato es invalido")
    return render_template("contacto2.html", form=miform)
No necesitamos comprobar el tipo de solicitud
En este caso no necesitamos comprobar si se trata de una solicitud GET o POST debido a que simplemente el condicional comprueba «En caso de ser validos los datos al momento de hacer submit» procesar lo que esta dentro del bloque, en caso contrario simplemente dar un aviso de error. Pero cuando se produce una solicitud GET simplemente va a saltearse el condicional e ir al final, al «return» y cargar el sitio con «form» como argumento.

En este caso simplemente colocamos un print, es decir, que luego veremos más adelante como enviar un email y desarrollar la parte «funcional» verdaderamente del formulario… En fin.

Paso 4: El token CSRF

Al momento de utilizar esta librería nos brinda la opción de crear y utilizar una clave secreta para mejorar la seguridad de nuestro formulario y evitar ciertos agujeros de seguridad en nuestro formulario. Para ello debemos crear una clave privada en nuestra ruta y pasarla luego como argumento al momento de cargar nuestro sitio, recordemos que nos falta aún la parte visual.. Creamos una clave (puede ser cualquiera que se te ocurra):

Esta clave debe estar dentro del archivo «run.py» y debe preferentemente estar debajo de la instancia de la app:

#Importar
from flask import Flask, render_template, request
import requests, json #Solución import requests from venv
from forms import miformulario


#Crear app medante instancia


app = Flask(__name__)
app.secret_key = "pythones.netelmejorblog"
#Crear rutas con sus correspondientes funciones
#INICIO
@app.route('/', methods=['GET'])
def index():
    return render_template('/index.html')
#MI BLOG
@app.route('/blog', methods=['GET'])
def blog():
    return render_template('/blog.html')

#MIS PROYECTOS
@app.route('/mis-proyectos', methods=['GET'])
def mostrarproyectos():
    return render_template('mis-proyectos.html')

#MIS HABILIDADES
@app.route('/habilidades', methods=['GET'])
def habilidades():
    return render_template('mis-habilidades.html')

#CONTACTO
@app.route("/contacto", methods=["GET", "POST"])
def contacto():
    sitekey = "clave-del-sitio-google-recaptcha"

    if request.method == "POST":
        name = request.form['Nombre']
        email = request.form['Email']
        Mensaje = request.form['Mensaje']
        respuesta_del_captcha = request.form['g-recaptcha-response']
        
        if comprobar_humano(respuesta_del_captcha):
           #Si devuelve True
            status = "Exito."
            print (status)
        else:
           #Si devuelve False
            status = "Error, vuelve a comprobar que no eres un robot."
            print (status)

    return render_template("contacto.html", sitekey=sitekey)

#CONTACTO2 - FLASKFORMS WTFORMS
@app.route("/contacto2", methods=["GET", "POST"])
def contacto2():
    miform = miformulario()
    if miform.validate_on_submit():
        print(f"Name:{miform.name.data},Email:{miform.email.data},message:{miform.message.data}")
    else:
        print("Algún dato es invalido")
    return render_template("contacto2.html", form=miform)


def comprobar_humano(respuesta_del_captcha):
    secret = "tu-clave-secreta-recaptcha"
    payload = {'response':respuesta_del_captcha, 'secret':secret}
    response = requests.post("https://www.google.com/recaptcha/api/siteverify", payload)
    response_text = json.loads(response.text)
    return response_text['success']


#Ejecutar nuestra app cuando ejecutemos este archivo run.py
if __name__ == '__main__':
    app.run(debug=True)

 

Paso 5: Añadiendo Bootstrap a WTForms

Primero si queremos incluir estilos en los formularios de WTForms o FlaskForms la forma más sencilla es instalando flask_bootstrap. Aunque podemos hacerlo de una manera más rustica y no tan complicada para evitar instalar una librería más que en realidad ya estamos utilizando (bootstrap) prefiero enseñártelo de esta manera:

pip install flask_bootstrap

Y luego en nuestro archivo «run.py» debemos decorar nuestra app con bootstrap:

#Importar
from flask import Flask, render_template, request
import requests, json #Solución import requests from venv
from forms import miformulario
from flask_bootstrap import Bootstrap #importamos Bootstrap para el formulario

#Crear app medante instancia


app = Flask(__name__)
app.secret_key = "pythones.netelmejorblog"
Bootstrap(app) #Decoramos nuestra app con bootstrap
#Crear rutas con sus correspondientes funciones
#INICIO
@app.route('/', methods=['GET'])
def index():
    return render_template('/index.html')
#MI BLOG
@app.route('/blog', methods=['GET'])
def blog():
    return render_template('/blog.html')

#MIS PROYECTOS
@app.route('/mis-proyectos', methods=['GET'])
def mostrarproyectos():
    return render_template('mis-proyectos.html')

#MIS HABILIDADES
@app.route('/habilidades', methods=['GET'])
def habilidades():
    return render_template('mis-habilidades.html')

#CONTACTO
@app.route("/contacto", methods=["GET", "POST"])
def contacto():
    sitekey = "clave-del-sitio-google-recaptcha"

    if request.method == "POST":
        name = request.form['Nombre']
        email = request.form['Email']
        Mensaje = request.form['Mensaje']
        respuesta_del_captcha = request.form['g-recaptcha-response']
        
        if comprobar_humano(respuesta_del_captcha):
           #Si devuelve True
            status = "Exito."
            print (status)
        else:
           #Si devuelve False
            status = "Error, vuelve a comprobar que no eres un robot."
            print (status)

    return render_template("contacto.html", sitekey=sitekey)

#CONTACTO2 - FLASKFORMS WTFORMS
@app.route("/contacto2", methods=["GET", "POST"])
def contacto2():
    miform = miformulario()
    if miform.validate_on_submit():
        print(f"Name:{miform.name.data},Email:{miform.email.data},message:{miform.message.data}")
    else:
        print("Algún dato es invalido")
    return render_template("contacto2.html", form=miform)


def comprobar_humano(respuesta_del_captcha):
    secret = "tu-clave-secreta-recaptcha"
    payload = {'response':respuesta_del_captcha, 'secret':secret}
    response = requests.post("https://www.google.com/recaptcha/api/siteverify", payload)
    response_text = json.loads(response.text)
    return response_text['success']


#Ejecutar nuestra app cuando ejecutemos este archivo run.py
if __name__ == '__main__':
    app.run(debug=True)

 

Ahora vamos a la parte visual, creamos nuestro template dentro de la carpeta «/templates» llamado «contacto2.html» y dentro de el yo incluí algunos estilos para que quede similar al primer formulario:

{% extends "base/base.html" %}
{%block head%}
<script src="https://www.google.com/recaptcha/api.js"></script>
{%endblock%}
{% block title %}Contacto{% endblock %}
{%block columna8 %}
<div class ="contenedor-neon-int">
  <div class = "title-neon" >
    <h3>Enviar mensaje a través del espacio</h3>
    <p class="mb-4"></p>
    <p class = "p-neon">Seguro tenemos mucho de que hablar! Puedes contactarme a través del siguiente formulario:</p>
    <br>

  </div>
<!--Aquí estaba el formulario HTML anteriormente-->
{%endblock %}
  {% block columna4 %}
  {% endblock %}

Básicamente copie, pegue y elimine el formulario HTML y ahora dentro colocaremos el que creamos con WTForms o FLASKFORMS, pero primero importamos dentro del HTML el Token de seguridad CSFR y nuestro BOOTSTRAP así :

{% extends "base/base.html" %}
{% import "bootstrap/wtf.html" as wtf %}

{%block head%}
<script src="https://www.google.com/recaptcha/api.js"></script>
{%endblock%}
{% block title %}Contacto{% endblock %}
{%block columna8 %}
<div class ="contenedor-neon-int">
  <div class = "title-neon" >
    <h3>Enviar mensaje a través del espacio</h3>
    <p class="mb-4"></p>
    <p class = "p-neon">Seguro tenemos mucho de que hablar! Puedes contactarme a través del siguiente formulario:</p>
    <br>

  </div>
<!--Aquí estaba el formulario HTML anteriormente-->
{{ form.csrf_token() }}
{{ wtf.quick_form(form) }}
{%endblock %}
  {% block columna4 %}
  {% endblock %}

Y este será el resultado al cargar nuestra url «127.0.0.1:5000/contacto2»

formulario de contacto en flask-

Y así de simple tenemos un formulario funcional, aunque claro, los estilos… Bue…

Habrá que acomodarlo, pero si lo probamos funciona, al completar los campos y presionar «enviar» en nuestro interprete vemos lo siguiente:

formulario test

Ahora vamos a acomodar un poco los estilos, y añadir nuestro css personalizado al botón, etc. Para ello podemos en vez de usar el Quick_Form de WTForms, podemos crear un formulario HTML e incluir campo por campo de nuestro formulario WTForm así:

{% extends "base/base.html" %}
{% import "bootstrap/wtf.html" as wtf %}

{%block head%}
<script src="https://www.google.com/recaptcha/api.js"></script>
{%endblock%}
{% block title %}Contacto{% endblock %}
{%block columna8 %}
<div class ="contenedor-neon-int">
  <div class = "title-neon" >
    <h3>Enviar mensaje a través del espacio</h3>
    <p class="mb-4"></p>
    <p class = "p-neon">Seguro tenemos mucho de que hablar! Puedes contactarme a través del siguiente formulario:</p>
    <br>

  </div>
<!--Aquí estaba el formulario HTML anteriormente-->
    <div class="row">
      <div class="col-sm-10 ml-auto mr-auto">
       <form action = "/contacto2" method = "post" class = "form-horizontal">
          {{ form.csrf_token() }}
        {{ wtf.form_field(form.name, class='form-control', 
           placeholder='Nombre') }}
        {{ wtf.form_field(form.email, class='form-control', 
           placeholder='Email') }}
        {{ wtf.form_field(form.message, class='form-control', 
           placeholder='Tu mensaje') }}
           <div align = "right">
            <a href="#"><button type="submit">
              <span></span>
              <span></span>
              <span></span>
              <span></span>
              Enviar!
            </button></a>
          </div></form>

      </div></div>

{%endblock %}
  {% block columna4 %}
  {% endblock %}

Y obtenemos este resultado:

formulario WTForms

Mejor no?

Un lujo!!

Último paso: Comprobando Email  y añadiendo ReCaptcha a WTForms

Para que nuestro formulario creado con WTForms este completo nos estaría faltando:

  • Comprobar la validez del email ingresado por el visitante.
  • Añadir ReCaptcha
  • Procesar estos datos para enviar un mensaje.

En el caso del primer item, como en el formulario HTML lo hicimos manualmente utilizando la etiqueta «input type = «email», en el caso de WTForms debemos comprobarlo en el lado del controlador:

comprobar email
Primer formulario de contacto en flask usando HTML. Comprobación de correo mediante HTML.

 

Así que para ello podemos recurrir a la librería «Email Validator«. Para ello instalamos:

pip install email_validator

E importamos dentro de «forms.py» y en wtforms.validators incluimos Email:

#Importar
from flask_wtf import FlaskForm
#Aquí importamos, campodetexto, validadoresdedatos, y el boton submit
from wtforms import StringField, validators, SubmitField
#Aquí de los validadores importamos el datoobligatorio y el email
from wtforms.validators import DataRequired, Email
import email_validator

class miformulario(FlaskForm):
    name = StringField(label='Nombre', validators=[DataRequired()])
    email = StringField(label='Email', validators=[DataRequired()])
    message = StringField(label='Mensaje')
    submit = SubmitField(label="Enviar")

Y en nuestro campo de email añadimos como argumento True para el parámetro Email_Granulator, que comprueba que se trate de un email lo más parecido a uno real posible. Es decir que se incluya el carácter «@». Quedando nuestro «forms.py» así:

#Importar
from flask_wtf import FlaskForm
#Aquí importamos, campodetexto, validadoresdedatos, y el boton submit
from wtforms import StringField, validators, SubmitField
#Aquí de los validadores importamos el datoobligatorio y el email
from wtforms.validators import DataRequired, Email
import email_validator

class miformulario(FlaskForm):
    name = StringField(label='Nombre', validators=[DataRequired()])
    email = StringField(label='Email', validators=[DataRequired(), Email(granular_message=True)])
    message = StringField(label='Mensaje')
    submit = SubmitField(label="Enviar")

Ahora al momento de ingresar cualquier cosa, en vez de un email nos dará la siguiente alerta:

comprobar email2

Y ya solo nos estaría faltando agregar ReCaptcha a nuestro formulario de contacto en Flask para que este completo:

flaskrecaptchaRecordemos que ya teníamos clave del sitio y contraseña para incluir Google ReCaptcha en nuestro formulario, con WTForms lo hacemos de la siguiente manera:

  • Instalamos la librería Flask-reCaptcha en nuestro entorno virtual:
pip install Flask-reCaptcha

 

Ahora bastará con importar esta librería y añadir a nuestro archivo de configuración lo siguiente, pero como no tenemos uno aún y no hemos profundizado mucho ese tema lo añadimos en el «run.py» así:

from flask_recaptcha import ReCaptcha # Importar ReCaptcha

#Debajo de la declaracíon de la app:
app.config['RECAPTCHA_SITE_KEY'] = 'clave-del-sitio-recaptcha'
app.config['RECAPTCHA_SECRET_KEY'] = 'clave-secreta-de-recaptcha'
recaptcha = ReCaptcha(app)

Eso si, coloca tus claves, nuestro «run.py» queda así:

#Importar
from flask import Flask, render_template, request
import requests, json #Solución import requests from venv
from forms import miformulario
from flask_bootstrap import Bootstrap #importamos Bootstrap para el formulario
from flask_recaptcha import ReCaptcha # Importar ReCaptcha

#Crear app medante instancia
app = Flask(__name__)
#Configuraciones
app.secret_key = "pythones.netelmejorblog"
Bootstrap(app) #Decoramos nuestra app con bootstrap
#Recaptcha
app.config['RECAPTCHA_SITE_KEY'] = 'clave-del-sitio-recaptcha' 
app.config['RECAPTCHA_SECRET_KEY'] = 'clave-secreta-de-recaptcha' 
recaptcha = ReCaptcha(app)


#Crear rutas con sus correspondientes funciones
#INICIO
@app.route('/', methods=['GET'])
def index():
    return render_template('/index.html')
#MI BLOG
@app.route('/blog', methods=['GET'])
def blog():
    return render_template('/blog.html')
#MIS PROYECTOS
@app.route('/mis-proyectos', methods=['GET'])
def mostrarproyectos():
    return render_template('mis-proyectos.html')
#MIS HABILIDADES
@app.route('/habilidades', methods=['GET'])
def habilidades():
    return render_template('mis-habilidades.html')
#CONTACTO
@app.route("/contacto", methods=["GET", "POST"])
def contacto():
    sitekey = "clave-del-sitio-google-recaptcha"

    if request.method == "POST":
        name = request.form['Nombre']
        email = request.form['Email']
        Mensaje = request.form['Mensaje']
        respuesta_del_captcha = request.form['g-recaptcha-response']
        
        if comprobar_humano(respuesta_del_captcha):
           #Si devuelve True
            status = "Exito."
            print (status)
        else:
           #Si devuelve False
            status = "Error, vuelve a comprobar que no eres un robot."
            print (status)

    return render_template("contacto.html", sitekey=sitekey)


#CONTACTO2 - FLASKFORMS WTFORMS
@app.route("/contacto2", methods=["GET", "POST"])
def contacto2():
    miform = miformulario()
    if miform.validate_on_submit():
        print(f"Name:{miform.name.data},Email:{miform.email.data},message:{miform.message.data}")
    else:
        print("Algún dato es invalido")
    return render_template("contacto2.html", form=miform)


def comprobar_humano(respuesta_del_captcha):
    secret = "tu-clave-secreta-recaptcha"
    payload = {'response':respuesta_del_captcha, 'secret':secret}
    response = requests.post("https://www.google.com/recaptcha/api/siteverify", payload)
    response_text = json.loads(response.text)
    return response_text['success']

#Ejecutar nuestra app cuando ejecutemos este archivo run.py
if __name__ == '__main__':
    app.run(debug=True)

Ahora solo nos faltaría agregar el Captcha a la parte visual, a nuestra plantilla «contacto2.html» de la siguiente manera:

{% extends "base/base.html" %}
{% import "bootstrap/wtf.html" as wtf %}

{%block head%}
<script src="https://www.google.com/recaptcha/api.js"></script>
{%endblock%}
{% block title %}Contacto{% endblock %}
{%block columna8 %}
<div class ="contenedor-neon-int">
  <div class = "title-neon" >
    <h3>Enviar mensaje a través del espacio</h3>
    <p class="mb-4"></p>
    <p class = "p-neon">Seguro tenemos mucho de que hablar! Puedes contactarme a través del siguiente formulario:</p>
    <br>

  </div>
<!--Aquí estaba el formulario HTML anteriormente-->
    <div class="row">
      <div class="col-sm-10 ml-auto mr-auto">
        <form action = "/contacto2" method = "post" class = "form-horizontal">
          {{ form.csrf_token() }}
        {{ wtf.form_field(form.name, class='form-control', 
           placeholder='Nombre') }}
        {{ wtf.form_field(form.email, class='form-control', 
           placeholder='Email') }}
        {{ wtf.form_field(form.message, class='form-control', 
           placeholder='Tu mensaje') }}
           {{ recaptcha }}
           <div align = "right">
            <a href="#"><button type="submit">
              <span></span>
              <span></span>
              <span></span>
              <span></span>
              Enviar!
            </button></a>
          </div></form>

      </div></div>

{%endblock %}
  {% block columna4 %}
  {% endblock %}

 

Y recordemos que eso no es todo, debemos procesar este captcha, para que no deje enviar el mensaje si no se ha procesado correctamente, para ello recurrimos a la función «recaptcha.verify()«. Recordar leer la documentación de cada librería que utilizamos!

A esta función la podemos agregar al mismo condicional que ya teníamos con un operador «and» en nuestro «run.py«:

#Importar
from flask import Flask, render_template, request
import requests, json #Solución import requests from venv
from forms import miformulario
from flask_bootstrap import Bootstrap #importamos Bootstrap para el formulario
from flask_recaptcha import ReCaptcha # Importar ReCaptcha

#Crear app medante instancia
app = Flask(__name__)
#Configuraciones
app.secret_key = "pythones.netelmejorblog"
Bootstrap(app) #Decoramos nuestra app con bootstrap
#Recaptcha
app.config['RECAPTCHA_SITE_KEY'] = 'clave-del-sitio-recaptcha' 
app.config['RECAPTCHA_SECRET_KEY'] = 'clave-secreta-de-recaptcha' 
recaptcha = ReCaptcha(app)


#Crear rutas con sus correspondientes funciones
#INICIO
@app.route('/', methods=['GET'])
def index():
    return render_template('/index.html')
#MI BLOG
@app.route('/blog', methods=['GET'])
def blog():
    return render_template('/blog.html')
#MIS PROYECTOS
@app.route('/mis-proyectos', methods=['GET'])
def mostrarproyectos():
    return render_template('mis-proyectos.html')
#MIS HABILIDADES
@app.route('/habilidades', methods=['GET'])
def habilidades():
    return render_template('mis-habilidades.html')
#CONTACTO
@app.route("/contacto", methods=["GET", "POST"])
def contacto():
    sitekey = "clave-del-sitio-google-recaptcha"

    if request.method == "POST":
        name = request.form['Nombre']
        email = request.form['Email']
        Mensaje = request.form['Mensaje']
        respuesta_del_captcha = request.form['g-recaptcha-response']
        
        if comprobar_humano(respuesta_del_captcha):
           #Si devuelve True
            status = "Exito."
            print (status)
        else:
           #Si devuelve False
            status = "Error, vuelve a comprobar que no eres un robot."
            print (status)

    return render_template("contacto.html", sitekey=sitekey)


#CONTACTO2 - FLASKFORMS WTFORMS
@app.route("/contacto2", methods=["GET", "POST"])
def contacto2():
    miform = miformulario()
    if miform.validate_on_submit() and recaptcha.verify():
        print(f"Name:{miform.name.data},Email:{miform.email.data},message:{miform.message.data}")
    else:
        print("Algún dato es invalido")
    return render_template("contacto2.html", form=miform)


def comprobar_humano(respuesta_del_captcha):
    secret = "tu-clave-secreta-recaptcha"
    payload = {'response':respuesta_del_captcha, 'secret':secret}
    response = requests.post("https://www.google.com/recaptcha/api/siteverify", payload)
    response_text = json.loads(response.text)
    return response_text['success']

#Ejecutar nuestra app cuando ejecutemos este archivo run.py
if __name__ == '__main__':
    app.run(debug=True)

Fíjate en la linea 61, si se cumplen ambas condiciones, es decir, que los datos del formulario son válidos y la función del ReCaptcha Verify devuelve True, entonces se procesa el formulario. De lo contrario lanzará un error que veremos en la terminal de nuestra app. Fíjate que chulo ha quedado el formulario!

crear formulario de contacto en Flask

Y podemos comprobar que funciona correctamente:

éxito

 

Código final de nuestro formulario y controlador:

Código del archivo «run.py«:

#Importar
from flask import Flask, render_template, request
import requests, json #Solución import requests from venv
from forms import miformulario
from flask_bootstrap import Bootstrap #importamos Bootstrap para el formulario
from flask_recaptcha import ReCaptcha # Importar ReCaptcha

#Crear app medante instancia
app = Flask(__name__)
#Configuraciones
app.secret_key = "pythones.netelmejorblog"
Bootstrap(app) #Decoramos nuestra app con bootstrap
#Recaptcha
app.config['RECAPTCHA_SITE_KEY'] = 'clave-del-sitio-google-recaptcha' 
app.config['RECAPTCHA_SECRET_KEY'] = 'tu-clave-secreta-recaptcha' 
recaptcha = ReCaptcha(app)


#Crear rutas con sus correspondientes funciones
#INICIO
@app.route('/', methods=['GET'])
def index():
    return render_template('/index.html')
#MI BLOG
@app.route('/blog', methods=['GET'])
def blog():
    return render_template('/blog.html')
#MIS PROYECTOS
@app.route('/mis-proyectos', methods=['GET'])
def mostrarproyectos():
    return render_template('mis-proyectos.html')
#MIS HABILIDADES
@app.route('/habilidades', methods=['GET'])
def habilidades():
    return render_template('mis-habilidades.html')
#CONTACTO
@app.route("/contacto", methods=["GET", "POST"])
def contacto():
    sitekey = "clave-del-sitio-google-recaptcha"

    if request.method == "POST":
        name = request.form['Nombre']
        email = request.form['Email']
        Mensaje = request.form['Mensaje']
        respuesta_del_captcha = request.form['g-recaptcha-response']
        
        if comprobar_humano(respuesta_del_captcha):
           #Si devuelve True
            status = "Exito."
            print (status)
        else:
           #Si devuelve False
            status = "Error, vuelve a comprobar que no eres un robot."
            print (status)

    return render_template("contacto.html", sitekey=sitekey)


#CONTACTO2 - FLASKFORMS WTFORMS
@app.route("/contacto2", methods=["GET", "POST"])
def contacto2():
    miform = miformulario()
    if miform.validate_on_submit() and recaptcha.verify():
        print(f"Name:{miform.name.data},Email:{miform.email.data},message:{miform.message.data}")
    else:
        print("Algún dato es invalido")
    return render_template("contacto2.html", form=miform)


def comprobar_humano(respuesta_del_captcha):
    secret = "tu-clave-secreta-recaptcha"
    payload = {'response':respuesta_del_captcha, 'secret':secret}
    response = requests.post("https://www.google.com/recaptcha/api/siteverify", payload)
    response_text = json.loads(response.text)
    return response_text['success']

#Ejecutar nuestra app cuando ejecutemos este archivo run.py
if __name__ == '__main__':
    app.run(debug=True)

 

Código del archivo html template «contacto2.html«:

{% extends "base/base.html" %}
{% import "bootstrap/wtf.html" as wtf %}

{%block head%}
<script src="https://www.google.com/recaptcha/api.js"></script>
{%endblock%}
{% block title %}Contacto{% endblock %}
{%block columna8 %}
<div class ="contenedor-neon-int">
  <div class = "title-neon" >
    <h3>Enviar mensaje a través del espacio</h3>
    <p class="mb-4"></p>
    <p class = "p-neon">Seguro tenemos mucho de que hablar! Puedes contactarme a través del siguiente formulario:</p>
    <br>

  </div>
<!--Aquí estaba el formulario HTML anteriormente-->
    <div class="row">
      <div class="col-sm-10 ml-auto mr-auto">
        <form action = "/contacto2" method = "post" class = "form-horizontal">
          {{ form.csrf_token() }}
        {{ wtf.form_field(form.name, class='form-control', 
           placeholder='Nombre') }}
        {{ wtf.form_field(form.email, class='form-control', 
           placeholder='Email') }}
        {{ wtf.form_field(form.message, class='form-control', 
           placeholder='Tu mensaje') }}
           {{ recaptcha }}
           <div align = "right">
            <a href="#"><button type="submit">
              <span></span>
              <span></span>
              <span></span>
              <span></span>
              Enviar!
            </button></a>
          </div></form>

      </div></div>

{%endblock %}
  {% block columna4 %}
  {% endblock %}

 

Esto ha sido CASI TODO!

final crear un formulario de contacto en flaskBien hasta aquí llegamos en esta entrada!. Aprendimos más o menos como se crean los formularios de contacto en FLASK. En la siguiente entrada me gustaría realizar una especie de resumen comenzando desde cero a explicar nuevamente como funciona flask. Pero todo en un solo post, de cero a 100! Ya que estos post podríamos considerarlos de tipo informativo ya que no todos manejan HTML, CSS y JS para comprender al 100% el funcionamiento de flask. Un abrazo!

 

Compartir es agradecer! :)

Deja un comentario

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *

Este sitio usa Akismet para reducir el spam. Aprende cómo se procesan los datos de tus comentarios.