0% encontró este documento útil (0 votos)
5 vistas12 páginas

Manual Flask: Jinja2, BBDD y Despliegue

El documento es un manual completo sobre Flask que abarca desde la instalación hasta el despliegue, incluyendo temas como Jinja2, bases de datos, autenticación, testing y buenas prácticas. Se presenta en un formato de archivo Markdown, listo para ser convertido a PDF o Word, y contiene una estructura organizada en secciones detalladas. Este manual es una guía esencial para desarrolladores que buscan construir aplicaciones web con Flask.
Derechos de autor
© All Rights Reserved
Nos tomamos en serio los derechos de los contenidos. Si sospechas que se trata de tu contenido, reclámalo aquí.
Formatos disponibles
Descarga como PDF, TXT o lee en línea desde Scribd
0% encontró este documento útil (0 votos)
5 vistas12 páginas

Manual Flask: Jinja2, BBDD y Despliegue

El documento es un manual completo sobre Flask que abarca desde la instalación hasta el despliegue, incluyendo temas como Jinja2, bases de datos, autenticación, testing y buenas prácticas. Se presenta en un formato de archivo Markdown, listo para ser convertido a PDF o Word, y contiene una estructura organizada en secciones detalladas. Este manual es una guía esencial para desarrolladores que buscan construir aplicaciones web con Flask.
Derechos de autor
© All Rights Reserved
Nos tomamos en serio los derechos de los contenidos. Si sospechas que se trata de tu contenido, reclámalo aquí.
Formatos disponibles
Descarga como PDF, TXT o lee en línea desde Scribd

Manual completo de Flask (con Jinja2, BBDD, autenticación, blueprints, migraciones,

testing y despliegue)
Idioma: Español
Extensión: 1 solo archivo .md listo para convertir a PDF o importar a Word
Páginas estimadas al imprimir: 20-22

1. Introducción y stack
2. Instalación y “Hola Flask”
3. Estructura profesional de carpetas
4. Request/Response, contextos y errores
5. Jinja2 – sintaxis, filtros, herencia y macros
6. Formularios con Flask-WTF y validaciones
7. Base de datos con Flask-SQLAlchemy
8. Migraciones con Flask-Migrate
9. Relaciones (1-N, N-M) y consultas avanzadas
10. Autenticación manual (login, logout, sesiones)
11. Blueprints – modularizar la aplicación
12. Testing con pytest y cliente de pruebas
13. Logging y manejo de configuraciones
14. Flask-Login para usuarios “recuerdame”
15. Subida de archivos estáticos y media
16. API RESTful con Flask-RESTful (opción)
17. Seguridad (CSRF, bcrypt, HTTPS headers)
18. Tareas en segundo plano con Celery
19. Despliegue en Render + Gunicorn + PostgreSQL
20. Check-list de buenas prácticas

A continuación el contenido completo en bloque Markdown. Copia, pega en manual-


[Link] y exporta a PDF con Typora, VS Code, Pandoc, etc.
Markdown
Copy
Code Preview
# Manual de Flask (Jinja2, BBDD, Autenticación, Testing y Despliegue)

> Versión 1.0 – junio 2025 – Español

---

## 1. Introducción y stack
Flask es un micro-framework que proporciona las herramientas mínimas para
construir webapps en Python.
Stack que cubriremos:
- Flask 2.x
- Jinja2 (plantillas)
- SQLAlchemy vía Flask-SQLAlchemy
- Flask-Migrate (Alembic)
- Flask-WTF (formularios)
- Flask-Login (gestión de sesiones)
- pytest (testing)
- Celery (tareas asíncronas)
- Gunicorn + PostgreSQL + Render (despliegue)

---

## 2. Instalación y “Hola Flask”


```bash
python -m venv venv
source venv/bin/activate # Windows: venv\Scripts\activate
pip install Flask python-dotenv
[Link]
Python
Copy
from flask import Flask
app = Flask(__name__)

@[Link]("/")
def index():
return "<h1>Hola Flask</h1>"
bash
Copy
flask --app app run --debug

3. Estructura profesional de carpetas


Copy
proyecto/

├── app/
│ ├── __init__.py
│ ├── [Link]
│ ├── [Link]
│ ├── routes/
│ │ ├── __init__.py
│ │ ├── [Link]
│ │ └── [Link]
│ ├── templates/
│ │ └── [Link]
│ └── static/
│ └── css/[Link]
├── migrations/
├── tests/
│ └── test_app.py
├── [Link]
├── [Link]
└── [Link]
[Link]
Python
Copy
import os
basedir = [Link]([Link](__file__))
class Config:
SECRET_KEY = [Link]('SECRET_KEY') or 'clave-temporal'
SQLALCHEMY_DATABASE_URI = [Link]('DATABASE_URL') or \
'sqlite:///' + [Link](basedir, '[Link]')
SQLALCHEMY_TRACK_MODIFICATIONS = False
app/__init__.py
Python
Copy
from flask import Flask
from flask_sqlalchemy import SQLAlchemy
from flask_migrate import Migrate
from config import Config

db = SQLAlchemy()
migrate = Migrate()

def create_app(config_class=Config):
app = Flask(__name__)
[Link].from_object(config_class)

db.init_app(app)
migrate.init_app(app, db)

from [Link] import bp as main_bp


app.register_blueprint(main_bp)

return app

4. Request/Response, contextos y errores


Python
Copy
from flask import request, jsonify, abort, render_template

@[Link]('/search')
def search():
q = [Link]('q', '')
if not q:
abort(400, description="Falta parámetro q")
return jsonify({"query": q, "results": []})

@[Link](404)
def not_found(e):
return render_template('[Link]'), 404

5. Jinja2 – sintaxis, filtros, herencia y macros


templates/[Link]
HTML
Preview
Copy
<!doctype html>
<html lang="es">
<head>
<meta charset="utf-8">
<title>{% block title %}FlaskApp{% endblock %}</title>
<link rel="stylesheet" href="{{ url_for('static',
filename='css/[Link]') }}">
</head>
<body>
<nav><a href="{{ url_for('[Link]') }}">Inicio</a></nav>
<main>{% block content %}{% endblock %}</main>
</body>
</html>
templates/[Link]
HTML
Preview
Copy
{% extends '[Link]' %}
{% block title %}Inicio{% endblock %}
{% block content %}
<h1>Bienvenido {{ username|default("invitado") }}</h1>
<p>Fecha: {{ moment().format('LLL') }}</p>
{% endblock %}
Filtros útiles: |upper, |truncate(80), |floatformat(2)
Macros (reutilizable):
HTML
Preview
Copy
{% macro input(name, value='', type='text') %}
<input type="{{ type }}" name="{{ name }}" value="{{ value|e }}"
class="form-control">
{% endmacro %}
{{ input('email', type='email') }}

6. Formularios con Flask-WTF y validaciones


bash
Copy
pip install flask-wtf
[Link]
Python
Copy
from flask_wtf import FlaskForm
from wtforms import StringField, PasswordField, SubmitField
from [Link] import DataRequired, Email, Length

class LoginForm(FlaskForm):
email = StringField('Email', validators=[DataRequired(), Email()])
password = PasswordField('Contraseña', validators=[DataRequired(),
Length(min=6)])
submit = SubmitField('Entrar')
Vista:
Python
Copy
from flask import render_template, redirect, url_for, flash
from [Link] import LoginForm

@[Link]('/login', methods=['GET', 'POST'])


def login():
form = LoginForm()
if form.validate_on_submit():
flash(f'Recibido {[Link]}', 'success')
return redirect(url_for('[Link]'))
return render_template('[Link]', form=form)
Plantilla:
HTML
Preview
Copy
{% extends '[Link]' %}
{% block content %}
<form method="post" novalidate>
{{ form.hidden_tag() }}
<div class="mb-3">
{{ [Link] }}
{{ [Link](class='form-control') }}
{% for e in [Link] %}
<small class="text-danger">{{ e }}</small>
{% endfor %}
</div>
<div class="mb-3">
{{ [Link] }}
{{ [Link](class='form-control') }}
</div>
{{ [Link](class='btn btn-primary') }}
</form>
{% endblock %}

7. Base de datos con Flask-SQLAlchemy


[Link]
Python
Copy
from datetime import datetime
from app import db

class User([Link]):
id = [Link]([Link], primary_key=True)
username = [Link]([Link](64), index=True, unique=True)
email = [Link]([Link](120), index=True, unique=True)
password_hash = [Link]([Link](128))
posts = [Link]('Post', backref='author', lazy='dynamic')

class Post([Link]):
id = [Link]([Link], primary_key=True)
title = [Link]([Link](140))
body = [Link]([Link])
timestamp = [Link]([Link], index=True,
default=[Link])
user_id = [Link]([Link], [Link]('[Link]'))
Shell interactivo:
bash
Copy
flask shell
>>> from [Link] import User
>>> u = User(username='admin', email='admin@[Link]')
>>> [Link](u)
>>> [Link]()

8. Migraciones con Flask-Migrate


bash
Copy
pip install flask-migrate
bash
Copy
flask db init # crea carpeta migrations
flask db migrate -m "Initial migration"
flask db upgrade

9. Relaciones (1-N, N-M) y consultas avanzadas


Ejemplo N-M: tags en posts
Python
Copy
tags = [Link]('post_tags',
[Link]('tag_id', [Link], [Link]('[Link]'),
primary_key=True),
[Link]('post_id', [Link], [Link]('[Link]'),
primary_key=True)
)

class Tag([Link]):
id = [Link]([Link], primary_key=True)
name = [Link]([Link](30))

class Post([Link]):
...
tags = [Link]('Tag', secondary=tags, lazy='subquery',
backref=[Link]('posts', lazy=True))
Consultas:
Python
Copy
# posts que contienen tag "python"
[Link]([Link]([Link] == 'python')).all()

# paginación
pagination = [Link].order_by([Link]()).paginate(
page=1, per_page=5, error_out=False)

10. Autenticación manual (login, logout, sesiones)


Python
Copy
from [Link] import generate_password_hash, check_password_hash
from flask import session

def login_user(email, password):


user = [Link].filter_by(email=email).first()
if user and check_password_hash(user.password_hash, password):
session['uid'] = [Link]
return True
return False

def logout_user():
[Link]('uid', None)

@[Link]('/logout')
def logout():
logout_user()
return redirect(url_for('[Link]'))

11. Blueprints – modularizar la aplicación


routes/[Link]
Python
Copy
from flask import Blueprint, render_template, redirect, url_for, flash
from [Link] import LoginForm
from [Link] import User
from app import db

bp = Blueprint('auth', __name__, url_prefix='/auth')

@[Link]('/login', methods=['GET', 'POST'])


def login():
form = LoginForm()
if form.validate_on_submit():
...
return render_template('auth/[Link]', form=form)
Registrar en fábrica:
Python
Copy
from [Link] import bp as auth_bp
app.register_blueprint(auth_bp)

12. Testing con pytest y cliente de pruebas


bash
Copy
pip install pytest pytest-flask
tests/[Link]
Python
Copy
import pytest
from app import create_app, db

@[Link]
def app():
app = create_app()
[Link]['TESTING'] = True
[Link]['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///:memory:'
with app.app_context():
db.create_all()
yield app
db.drop_all()

@[Link]
def client(app):
return app.test_client()
tests/test_routes.py
Python
Copy
def test_index(client):
resp = [Link]('/')
assert resp.status_code == 200
assert b'Hola Flask' in [Link]
Ejecutar:
bash
Copy
pytest -v

13. Logging y manejo de configuraciones


Python
Copy
import logging
from [Link] import RotatingFileHandler

if not [Link]:
file_handler = RotatingFileHandler('logs/[Link]', maxBytes=10240,
backupCount=10)
file_handler.setFormatter([Link](
'%(asctime)s %(levelname)s: %(message)s [in
%(pathname)s:%(lineno)d]'))
file_handler.setLevel([Link])
[Link](file_handler)

14. Flask-Login para usuarios "recuérdame"


bash
Copy
pip install flask-login
app/__init__.py
Python
Copy
from flask_login import LoginManager
login_manager = LoginManager()
login_manager.login_view = '[Link]'
login_manager.init_app(app)
[Link]
Python
Copy
from flask_login import UserMixin

class User(UserMixin, [Link]):


...
__init__.py
Python
Copy
@login_manager.user_loader
def load_user(user_id):
return [Link](int(user_id))
Decorar rutas:
Python
Copy
from flask_login import login_required, current_user

@[Link]('/dashboard')
@login_required
def dashboard():
return render_template('[Link]', user=current_user)

15. Subida de archivos estáticos y media


Python
Copy
import os
from [Link] import secure_filename

UPLOAD_FOLDER = [Link](basedir, 'uploads')


ALLOWED_EXTENSIONS = {'png', 'jpg', 'jpeg', 'gif'}

[Link]['UPLOAD_FOLDER'] = UPLOAD_FOLDER

def allowed_file(filename):
return '.' in filename and [Link]('.', 1)[1].lower() in
ALLOWED_EXTENSIONS

@[Link]('/upload', methods=['POST'])
def upload_file():
if 'file' not in [Link]:
flash('No se seleccionó archivo')
return redirect([Link])
file = [Link]['file']
if file and allowed_file([Link]):
filename = secure_filename([Link])
[Link]([Link]([Link]['UPLOAD_FOLDER'], filename))
return redirect(url_for('uploaded_file', filename=filename))
Servir en desarrollo:
Python
Copy
from flask import send_from_directory

@[Link]('/uploads/<filename>')
def uploaded_file(filename):
return send_from_directory([Link]['UPLOAD_FOLDER'], filename)

16. API RESTful con Flask-RESTful (opcional)


bash
Copy
pip install flask-restful
Python
Copy
from flask_restful import Api, Resource, reqparse, fields, marshal_with

api = Api(app)

resource_fields = {
'id': [Link],
'title': [Link],
'body': [Link]
}

class PostResource(Resource):
@marshal_with(resource_fields)
def get(self, post_id):
post = [Link].get_or_404(post_id)
return post

api.add_resource(PostResource, '/api/posts/<int:post_id>')
17. Seguridad (CSRF, bcrypt, headers)
 Flask-WTF ya incluye protección CSRF (activa con SECRET_KEY).
 Hash de contraseñas:

Python
Copy
from [Link] import generate_password_hash, check_password_hash
user.password_hash = generate_password_hash(password)
check_password_hash(user.password_hash, password)

 Headers de seguridad:

Python
Copy
@app.after_request
def security_headers(resp):
[Link]['Strict-Transport-Security'] = 'max-age=31536000;
includeSubDomains'
[Link]['X-Content-Type-Options'] = 'nosniff'
[Link]['X-Frame-Options'] = 'DENY'
return resp

18. Tareas en segundo plano con Celery


bash
Copy
pip install celery redis
[Link]
Python
Copy
from celery import Celery

celery = Celery('app', broker='redis://localhost:6379/0')

@[Link]
def send_async_email(to, subject):
# código de envío
pass
Llamar:
Python
Copy
send_async_email.delay('user@[Link]', 'Bienvenido')
Ejecutar worker:
bash
Copy
celery -A [Link] worker --loglevel=info
19. Despliegue en Render + Gunicorn + PostgreSQL
[Link]
Copy
Flask
Flask-SQLAlchemy
Flask-Migrate
Flask-WTF
Flask-Login
psycopg2-binary
gunicorn
[Link]
Python
Copy
from app import create_app
app = create_app()
Procfile
web: gunicorn wsgi:app
Variables de entorno en Render:
Copy
DATABASE_URL=postgresql://user:pass@host:5432/dbname
SECRET_KEY=una-clave-muy-secreta
Push a GitHub y conectar repo en Render → despliegue automático.

20. Check-list de buenas prácticas


✔ Usa entornos virtuales y variables de entorno (.env)
✔ No hardcodees SECRET_KEY ni credenciales
✔ Separa configuraciones (dev, test, prod)
✔ Usa blueprints para escalar
✔ Testea rutas y modelos con pytest
✔ Haz backups de la BBDD
✔ Configura logging y monitoreo
✔ Protege headers y usa HTTPS
✔ Mantén dependencias actualizadas (pip-tools, Dependabot)
✔ Documenta tu API (Swagger/OpenAPI con flasgger o flask-restx)

También podría gustarte