Primeras líneas con #Flask

Posted by in Frameworks, Python

Cómo ya hemos comentado anteriormente, Flask es un microframework de #Python muy potente para programación Web, y es el que he elegido para trabajar. Podía haber elegido otro, la verdad, pero en la práctica la filosofía de un framework respecto a otro son detalles, y supongo que migrar a otro no será muy complicado. El caso es … que empezamos!

El «Hola mundo» en Flask

Quizás este ejemplo es el clásico, y que puedes encontrar en muchos sitios, por ejemplo aquí: https://realpython.com/blog/python/introduction-to-flask-part-1-setting-up-a-static-site/. Desgranamos el contenido, en lugar de que te ponga yo aquí el mismo código una y otra vez:

Para empezar, cada proyecto debe iniciarse en un directorio. La recomendación es usar virtualenv para crear un entorno «exclusivo» para trabajar, sin extender demasiado tu instalación original de Python. En mi caso, he optado por no hacerlo, pero sí que se recomienda.

Una vez tenemos directorio de trabajo, se crearán dos subdirectorios: templates y static. En el primero se almacenarán las plantillas en HTML5, mientras que en el segundo se almacenan otros ficheros estáticos, como CSS, Javascript, …

En general, este debe ser el esqueleto básico para proyectos pequeños, que podrán ser ampliados.

Otra buena referencia para iniciarse con Flask es este tutorial de tutsplus.

Pero sin duda, la mejor recomendación que te puedo dar es la documentación oficial, que tienes aquí: http://flask.pocoo.org/docs/0.10/.

Un segundo proyecto

El caso es que en este segundo proyecto, vamos a ver cómo llamar a varias páginas, y cómo hacer obligatorio estar registrado en una web para ver contenido interno, mediante el uso de decoradores (decorators). Puedes leer más de los decoradores aquí.

Empezamos. Vamos a definir en primer lugar, las páginas HTML, lo que nos permitirá conocer cómo se usan las plantillas, que se ubican en el directorio templates. Así, definimos base.html que será la base con la que iremos generando la plantilla. Aquí quiero destacar el uso de url_for que es una función para generar URLs. El uso de esta función requiere modificar un poco la forma de vincular con los archivos CSS, Javascript, …. e incluso para llamar a otras rutas, pero es tremendamente efectivo!

Aquí he utilizado las dos formas, aunque puedes elegir la que más te guste!

El código base.html es:

<!DOCTYPE html>
<html>
<head>
<title>Jugando con Flask - Manejando datos.es</title>
<meta name="viewport" content="width=device-width, initial-scale=1.0">
    <link href="static/css/bootstrap.min.css" rel="stylesheet" media="screen">
    <link id="pure" href="{{ url_for('static', filename='css/purecss-min.css') }}" rel="stylesheet">
    <link id="pure" href="{{ url_for('static', filename='css/manejandodatos_purecss.css') }}" rel="stylesheet">
</head>
<body>
    <header>
        <h1>Probando Flask desde Manejando datos</h1>
    </header>
<div class="container">
    <!-- child template -->
    {% block content %}{% endblock %}

    <!-- errors -->
    {% if error %}
    <p class="error"><strong>Error:</strong> {{ error }}</p>
    {% endif %}

    <!-- messages -->
    {% for message in get_flashed_messages() %}
    {{ message }}
    {% endfor %}
</div>
    <footer>
        <h6>
            Creado desde <a target="_blank" href="http://www.manejandodatos.es">Manejando datos</a>
        </h6>
    </footer>
</body>
</html>

Entre los CSS utilizados, están Twitter Bootstrap y PureCSS, de los que ya hemos hablado aquí.

Ahora toca crear index.html, que será una ampliación de la plantilla base.html.

{% extends "base.html" %}
{% block content %}
<h1>Welcome to Flask!</h1>
<br>

<h3>Posts:</h3>
{% for post in posts %}
<h1> {{ post.title }}</h1>
 {{ post.description }} <br>

<br>
{% endfor %}
{% endblock %}

Además, creamos una página saludo.html:

{% extends "base.html" %}
{% block content %}
<h2>Saludo desde Flask!</h1>
<br>
<p>volver a pagina <a href="/">principal</a>.</p>
{% endblock %}

Y una tercera página privada.html, cuyo contenido es:

{% extends "base.html" %}
{% block content %}
<h2>Página privada!</h1>
<br>
<p>Volver a pagina <a href="/">principal</a>
o ir a <a href="/saludo" class="pure-button">saludo</a>.</p>

Pulse para <a href="/logout" class="pure-button">salir</a>!!
{% endblock %}

Vamos ahora con run.py, y que será el encargado de trabajar con Flask, hacer las llamadas a las plantillas, etc, etc, …:


from flask import Flask, render_template, url_for, redirect

app = Flask(__name__)
methods = ['GET', 'POST']
GET, POST = methods

@app.route('/')
def home():
    return render_template('index.html')
    
@app.route('/saludo')
def saludo():
    return render_template('saludo.html')

@app.route('/privada')
def privada():
    return render_template('privada.html')

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

En esta primera versión, sólo nos centramos en mostrar 3 posibles páginas, y cambiar de una a otra. Cómo podeis ver, el código de Python apenas tiene dificultades, y utiliza render_template para mostrar las plantillas.

Subiendo el nivel de dificultad

Ahora, vamos con una segunda versión de run2.py, donde para ver la página privada, será necesario estár logueado. Así que creamos una plantilla para permitir dicho acceso:


from flask import Flask, render_template, url_for, redirect, flash, session, request

from functools import wraps

app = Flask(__name__)
app.secret_key = "flask_manejandodatos.es"
methods = ['GET', 'POST']
GET, POST = methods

def requiere_login(f):
    @wraps(f)
    def wrap(*args, **kkargs):
        if 'logged_in' in session:
            return f(*args, **kkargs)
        else:
            flash('Necesitas hacer LogIn antes!')
            return redirect(url_for('login'))
    return wrap
    
@app.route('/')
def home():
    return render_template('index.html')
    
@app.route('/saludo')
def saludo():
    return render_template('saludo.html')

@app.route('/privada')
@requiere_login
def privada():
    return render_template('privada.html')

@app.route('/login', methods=methods)
def login():
    error = None
    if request.method == POST:
        if request.form['username'] != 'admin' or request.form['password'] != 'admin':
            error = 'Error en las credenciales'
        else:
            session['logged_in'] = True
            flash('Estas dentro')
            return redirect(url_for('home'))
    return render_template('login.html', error=error)

@app.route('/logout')
def logout():
    session.pop('logged_in', None)
    flash('Estas fuera')
    print "Logout"
    return redirect(url_for('home'))
    
if __name__ == '__main__':
    app.run(debug=True)

Al igual que las otras plantillas anteriores, también será extensión de base.html.

En esta segunda versión, crearemos el decorador y modificamos la llamada a privada. Además, tenemos que controlar el formulario de registro (aquí está hecho muy simple, pero lo normal es conectar con una base de datos para validar!).

Otra cosa fundamental es que nos requiere incluir una clave privada en la app, y el objeto session es el que controla si estas dentro o no.

He subido el código a GitHub para que lo pruebes, las dos versiones.

Espero que sea de vuestro interés!