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! :)

Hey no te vayas sin dejarme un comentario!

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

Aprende a programar de manera profesional!

¬ŅTodav√≠a sin obtener una retribuci√≥n justa de tu aprendizaje?

Curso de Python Udemy

Convierteté en un verdadero Jedy de Python y comienza a desarrollar aplicaciones de forma profesional obteniendo un certificado de Udemy!. Pasate por la sección de Cursos Premium y realiza los mejores cursos con certificación válida a nivel internacional por apenas unos dolares!