Aplicación básica ToDo (Tareas por Hacer) en Flask con persistencia en PostgreSQL

El auge de Python es tremendo, sobre todo en mi actual empresa y luego de una breve conversación con el gran maestro JPS, decidí migrar de mi querido Ruby a Python. Debo aclarar que gracias a Ruby le volví a tomar el gusto al desarrollo y con Rails, su framework insignia, aprendí mucho de patrones de diseño, ORM, y de como funciona un Framework sin importar el lenguaje que lo soporte.

El patrón MVC es una manera o una forma de trabajar que permite diferenciar y separar lo que es el modelo de datos (los datos que van a tener la App que normalmente están guardados en BD), la vista (página HTML) y el controlador (donde se gestiona las peticiones de la app web).

Dicho lo anterior, la migración a Python fue una decisión arriesgada en un momento crítico de un proyecto igual de crítico. Por lo que si bien es cierto, Python tiene una curva de aprendizaje bastante suave, no tengo todo el tiempo necesario para aprender su sintaxis previo a la primera entrega del proyecto… Es por ello que apostaré a lo básico de todo lenguaje de programación, una pincelada de la sintaxis de Python y un poco más de Flask, el Framework escogido en esta oportunidad.

Flask es un microframework desarrollado por Armin Ronacher que te permite crear aplicaciones web en un abrir y cerrar de ojos, todo con una cantidad absurdamente pequeña de líneas de código. Veamos un Hola Mundo! en Flask…

from flask import Flask
app = Flask(__name__)

@app.route('/')
def hello_world():
    return 'Hola Mundo!'

if __name__ == '__main__':
    app.run()

Flask, a diferencia de otros frameworks para Python, no trae cientos de módulos para abordar las tareas más comunes en el desarrollo web, más bien se enfoca en proporcionar lo mínimo necesario para que puedas poner a funcionar una aplicación básica en cuestión de minutos. Es perfecto, por ejemplo, para el prototipado rápido de proyectos.

Incluye un servidor web de desarrollo para que puedas probar tus aplicaciones sin tener que instalar algo como Nginx o Apache. También trae un depurador y soporte integrado para pruebas unitarias. Tiene un excelente soporte para Unicode y es compatible 100% con WSGI 1.0.

Entre las cosas más hermosas que tiene este microframework están el esquema de rutas y la documentación. Con el decorador de rutas puedes hacer que tu aplicación responda a peticiones totalmente RESTful con URLs lindas y en la documentación encontrarás cualquier cosa que necesites saber para poner a andar tu aplicación explicado de manera simple y con ejemplos.

from flask import Flask, render_template, request, redirect, url_for
from flask_sqlalchemy import SQLAlchemy

app = Flask(__name__)
app.config['SQLALCHEMY_DATABASE_URI'] = 'postgresql://postgres:postgres@127.0.0.1/MiBD'
app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = False
db = SQLAlchemy(app)

class Task(db.Model):
    id = db.Column(db.Integer, primary_key=True)
    content = db.Column(db.String(200))
    done = db.Column(db.Boolean)

@app.route('/')
def home():
    tasks = Task.query.all()
    return render_template('index.html', tasks = tasks)

@app.route('/create-task', methods=['POST'])
def create():
    task = Task(content=request.form['content'], done=False)
    db.session.add(task)
    db.session.commit()
    return redirect(url_for('home'))

@app.route('/done/<id>')
def done(id):
    task = Task.query.filter_by(id=int(id)).first()
    task.done = not(task.done)
    db.session.commit()
    return redirect(url_for('home'))


@app.route('/delete/<id>')
def delete(id):
    task = Task.query.filter_by(id=int(id)).delete()
    db.session.commit()
    return redirect(url_for('home'))

if __name__ == '__main__':
    app.run(debug=True)
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Task App</title>
    <!--Bootstrap-->
    <link href="https://stackpath.bootstrapcdn.com/bootswatch/4.4.1/lux/bootstrap.min.css" rel="stylesheet" integrity="sha384-oOs/gFavzADqv3i5nCM+9CzXe3e5vXLXZ5LZ7PplpsWpTCufB7kqkTlC9FtZ5nJo" crossorigin="anonymous">
    <!--Fuentes-->
    <link href="https://fonts.googleapis.com/css2?family=Lobster&display=swap" rel="stylesheet"> 
    <!--CSS Principal-->
    <link rel="stylesheet" href="{{ url_for('static', filename='main.css') }}">    
</head>
<body>
    <main class="container p-4">
        <h1 class="display-4 text-center mt-4 title">Task App</h1>
    </main>
    <div class="row">
        <div class="col-md-4 offset-md-4 my-auto">
            <div class="card">
                <div class="card-header">
                   <form action="/create-task" method="POST" >
                        <div class="form-group">
                            <input type="text" name="content" placeholder="Task" class="form-control" autofocus>
                        </div>
                        <button type="submit" class="btn btn-primary btn-block">
                            Save
                        </button>
                   </form>
                </div>
                <div class="card-body">
                    <ul class="list-group">
                        {% for task in tasks %}
                            <li class="list-group-item">
                                <span class="{% if task.done %} done {% endif %}">{{task.content}}</span>
                                <a href="/done/{{task.id}}" class="btn btn-success btn-sm">Done</a>
                                <a href="/delete/{{task.id}}" class="btn btn-danger btn-sm">Delete</a>
                            </li>
                        {% endfor %}
                    </ul>
                </div>
            </div>
        </div>
    </div>
</body>
</html>
body {
    background: #E0EAFC;  /* fallback for old browsers */
    background: -webkit-linear-gradient(to right, #CFDEF3, #E0EAFC);  /* Chrome 10-25, Safari 5.1-6 */
    background: linear-gradient(to right, #CFDEF3, #E0EAFC); /* W3C, IE 10+/ Edge, Firefox 16+, Chrome 26+, Opera 12+, Safari 7+ */
    color: #000;
}

.title{
    font-family: 'Lobster', cursive;
}

.done {
    text-decoration: line-through;
    color: #cfcfcf;
}

Dejo el código acá a modo de lugar para consulta en el proyecto real ya que muchas características son validas para el objetivo final. Agradezco haber dado este primer paso y ahora vamos con todo por ese proyecto…!!

Happy Hacking!

Deja una respuesta

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