Regístrate para acceder a más de 15 cursos gratuitos de programación con un simulador

Formularios de modificación Python: Desarrollo web con Flask

Los formularios no solo sirven para buscar información; también permiten a los usuarios enviar y modificar datos ✍️, como al registrarse o actualizar su perfil. Estos casos son más complejos, pero con los conceptos adecuados, aprenderás a manejarlos fácilmente. ¡Vamos paso a paso!

¿Qué necesitas saber antes?

Cuando trabajas con formularios que envían datos, tienes que tener claros estos temas:

  • Qué etiquetas HTML se usan en los formularios
  • Cómo se envía una solicitud con datos usando HTTP
  • Cómo recibir y procesar esos datos en el servidor (en este caso, usando Flask)
  • Validar los datos y mostrar mensajes de error cuando algo esté mal

Enrutamiento para mostrar y procesar formularios

En aplicaciones web como las hechas con Flask, normalmente se usan dos rutas distintas para manejar un formulario:

  1. Una ruta que muestra el formulario vacío al usuario (tipo GET).
  2. Otra ruta que recibe los datos cuando el formulario se envía (tipo POST).

Ejemplo típico para registrar usuarios:

Método Ruta Función
GET /users/new Muestra el formulario para crear un nuevo usuario.
POST /users Recibe los datos del formulario y los procesa.

Esto se alinea bien con los principios REST, que son una base en muchos marcos modernos.


Paso 1: Crear la plantilla HTML del formulario

Primero necesitamos un archivo llamado new.html donde definamos los campos del formulario. Así podría verse:

<!-- templates/users/new.html -->
<form action="/users" method="post">
  <div>
    <label>
      Nombre
      <input type="text" name="name" value="{{ user.name }}">
    </label>
    {% if errors.get('name') %}
      <div>{{ errors['name'] }}</div>
    {% endif %}
  </div>
  <div>
    <label>
      Email
      <input type="email" required name="email" value="{{ user.email }}">
    </label>
    {% if errors.get('email') %}
      <div>{{ errors['email'] }}</div>
    {% endif %}
  </div>
  <div>
    <label>
      Contraseña
      <input type="password" required name="password" value="{{ user.password }}">
    </label>
    {% if errors.get('password') %}
      <div>{{ errors['password'] }}</div>
    {% endif %}
  </div>
  <div>
    <label>
      Confirmación de contraseña
      <input type="password" required name="passwordConfirmation" value="{{ user.passwordConfirmation }}">
    </label>
  </div>
  <div>
    <label>
      Ciudad
      <select name="city">
        <option value="">Selecciona</option>
        <option value="3" {{ 'selected' if user.city == '3' else '' }}>Bogotá</option>
        <option value="13" {{ 'selected' if user.city == '13' else '' }}>Medellín</option>
        <option value="399" {{ 'selected' if user.city == '399' else '' }}>Cali</option>
      </select>
    </label>
    {% if errors.get('city') %}
      <div>{{ errors['city'] }}</div>
    {% endif %}
  </div>
  <input type="submit" value="Registrarse">
</form>

Este formulario incluye campos comunes de registro: nombre, correo, contraseña, confirmación y una lista de ciudades.


Paso 2: Mostrar el formulario vacío

Crea una vista que muestre el formulario vacío cuando visitas /users/new:

@app.route('/users/new')
def users_new():
    user = {
        'name': '',
        'email': '',
        'password': '',
        'passwordConfirmation': '',
        'city': ''
    }
    errors = {}
    return render_template(
        'users/new.html',
        user=user,
        errors=errors
    )

Acá simplemente mandamos al template un diccionario vacío para los datos del usuario y para los errores, así evitamos problemas con los campos vacíos o no definidos.


Paso 3: Procesar el envío del formulario

Cuando el usuario envía los datos, llegan a través de un POST. Acá los recibimos y procesamos:

@app.post('/users')
def users_post():
    repo = UserRepository()
    user = request.form.to_dict()  # convierte los campos del formulario en un diccionario

    errors = validate(user)  # validamos los datos antes de guardarlos

    if errors:
        return render_template(
            'users/new.html',
            user=user,
            errors=errors
        ), 422  # 422 indica error del lado del cliente (datos inválidos)

    # Guardamos el usuario, por ejemplo, en base de datos
    repo.save(user)
    return redirect('/users', code=302)  # Redireccionamos a la página de usuarios

Validar los datos

La validación es fundamental. Nunca debes confiar en lo que envía el usuario directamente. Siempre revisa que los datos estén correctos y completos antes de guardarlos.

Aquí un ejemplo simple de función de validación:

def validate(user):
    errors = {}
    if not user.get('name'):
        errors['name'] = "El nombre es obligatorio"
    if '@' not in user.get('email', ''):
        errors['email'] = "El correo no es válido"
    if user.get('password') != user.get('passwordConfirmation'):
        errors['password'] = "Las contraseñas no coinciden"
    if not user.get('city'):
        errors['city'] = "Debes seleccionar una ciudad"
    return errors

Esta función devuelve un diccionario con los errores encontrados. Si hay errores, al usuario se le vuelve a mostrar el formulario con los mensajes correspondientes.


¿Qué pasa si hay errores?

Cuando validamos los datos y encontramos errores, mostramos el formulario de nuevo con los datos que el usuario había escrito y los errores correspondientes. No se hace redirección, porque queremos que el usuario vea la misma página con los mensajes de error:

return render_template(
    'users/new.html',
    user=user,
    errors=errors
), 422  # status de error
422 es un código de estado HTTP que significa "datos inválidos", usado para cuando el cliente envía datos incorrectos.

¿Y si hay muchos formularios parecidos?

Hacer esto a mano en cada formulario puede volverse tedioso. Por eso existen herramientas que simplifican la creación y validación de formularios.

Alternativa: usar WTForms

WTForms es una extensión para Flask que te permite:

  • Definir formularios como clases en Python
  • Validar campos fácilmente
  • Generar HTML automáticamente
  • Manejar la seguridad del formulario (como protección contra ataques CSRF)

Ejemplo básico de cómo podría verse un formulario con WTForms:

from flask_wtf import FlaskForm
from wtforms import StringField, PasswordField, SelectField
from wtforms.validators import InputRequired, Email, EqualTo

class UserForm(FlaskForm):
    name = StringField('Nombre', validators=[InputRequired()])
    email = StringField('Email', validators=[InputRequired(), Email()])
    password = PasswordField('Contraseña', validators=[InputRequired()])
    passwordConfirmation = PasswordField('Confirmar contraseña', validators=[
        InputRequired(),
        EqualTo('password', message="Las contraseñas deben coincidir")
    ])
    city = SelectField('Ciudad', choices=[
        ('3', 'Bogotá'),
        ('13', 'Medellín'),
        ('399', 'Cali')
    ])

Con esto, el formulario en HTML puede generarse automáticamente, validaciones incluidas.


Resumen

  • Un formulario de entrada necesita dos rutas: una para mostrarlo (GET) y otra para procesarlo (POST).
  • Los datos enviados por el usuario deben validarse siempre antes de guardarlos.
  • Si hay errores de validación, se vuelve a mostrar el formulario con los mensajes correspondientes.
  • Se usa un redireccionamiento (302) luego de guardar los datos exitosamente.
  • Es buena práctica devolver un código 422 cuando el usuario envía datos inválidos.
  • Para evitar repetir código y facilitar el trabajo, puedes usar herramientas como WTForms en Flask.

Si ya llegaste hasta aquí, ya tienes una buena base para trabajar con formularios en Flask como lo hacen en aplicaciones reales.


Trabajo independiente

  1. Crea una plantilla y agrega un manejador que muestre un formulario para crear un usuario con los campos: name y email. El id debe generarse dentro de la aplicación.
  2. Agrega en el manejador el guardado de los datos ingresados. Para almacenar al usuario utiliza un archivo. Los datos pueden codificarse en json usando la función dumps() del módulo json y decodificarse con loads().
  3. Después de agregar los datos al archivo, debe hacerse una redirección a la dirección /users.

image_processing.png

Aplicación de referencia

Para acceder completo a curso necesitas un plan básico

El plan básico te dará acceso completo a todos los cursos, ejercicios y lecciones de Códica, proyectos y acceso de por vida a la teoría de las lecciones completadas. La suscripción se puede cancelar en cualquier momento.

Obtener acceso
130
cursos
1000
ejercicios
2000+
horas de teoría
3200
test

Obtén acceso

Cursos de programación para principiantes y desarrolladores experimentados. Comienza tu aprendizaje de forma gratuita

  • 130 cursos, 2000+ horas de teoría
  • 1000 ejercicios prácticos en el navegador
  • 360 000 estudiantes
Al enviar el formulario, aceptas el «Política de privacidad» y los términos de la «Oferta», y también aceptas los «Términos y condiciones de uso»

Nuestros graduados trabajan en empresas como:

Bookmate
Health Samurai
Dualboot
ABBYY