@vero4ka_ru
Django Bogotá Meetup
¡Poyejali! (en ruso: Поехали!; se traduce como «¡Vámonos!») fue la frase que dijo Gagarin en el momento del despegue de su nave, Vostok 1
from flask import Flask
app = Flask(__name__)
@app.route("/")
def hello():
return "Hello World!"
if __name__ == "__main__":
app.run()
$ pip install Flask
app.py:
$ python app.py
* Running on http://127.0.0.1:5000/ (Press CTRL+C to quit)
vista
URL
from flask import Flask
def create_app():
app = Flask(__name__,
static_url_path='/static')
app.config.from_object('conf.config')
return app
$ pip install Flask-Script
manage.py:
$ ./manage.py runserver -h HOST -p PORT
$ ./manage.py shell
In [1]:
run.py:
from flask.ext.script import Manager
from run import create_app
app = create_app()
manager = Manager(app)
@app.route("/")
def hello():
return "Hello World!"
if __name__ == '__main__':
manager.run()
import sys
DEBUG = False
try:
if 'test' in sys.argv:
from test_config import *
else:
from local_config import *
except ImportError, e:
pass
config.py:
├── conf
│ ├── config.py
│ ├── __init__.py
│ └── local_config.py
├── manage.py
└── run.py
Configuration Handling: flask.pocoo.org/docs/0.11/config/
app.config.from_object('conf.config')
run.py:
cargar la configuración
desde el objeto
├── conf
│ ├── config.py
│ ├── __init__.py
│ └── local_config.py
├── app1
│ ├── templates
│ └── views.py
├── app2
│ ├── templates
│ └── views.py
├── manage.py
└── run.py
├── conf
│ ├── config.py
│ ├── __init__.py
│ └── local_config.py
├── manage.py
└── run.py
from flask import Blueprint
users = Blueprint(
'users',
__name__,
template_folder='templates')
@users.route("/")
def hello():
return "Hello Worlds!"
Crear un blueprint. users/views.py:
├── conf
│ ├── config.py
│ ├── __init__.py
│ └── local_config.py
├── users
│ ├── templates
│ └── views.py
├── manage.py
└── run.py
def create_app():
app = Flask(__name__, static_url_path='/static')
# ...
from users.views import users
app.register_blueprint(users, url_prefix='/users')
Registrar el blueprint. run.py:
http://localhost:5000/users/
@users.route('/user/<name>')
def user_profile(name):
return '<h1>Hello, {}!</h1>'.format(name)
def users_list():
pass
users.add_url_rule('/', 'users_list', users_list)
@users.route('/user/<int:pk>')
def user_detail(pk):
return '<h1>Hello, user #{}!</h1>'.format(pk)
@users.route('/user/<int:pk>/create', methods=('POST',))
def user_create(pk):
pass
Django:
reverse('user_detail', args=(user_pk,))
Flask:
url_for('users.user_detail', pk=user_pk)
Para los archivos estáticos:
Nombre de la vista
Métodos permitidos
url_for('static', filename='images/profile.png')
Nombre del blueprint
from flask import request
@users.route('/user/<int:pk>')
def user_detail(pk):
print request
print request.method
print request.args
print request.headers
return '<h1>Hello, user #{}!</h1>'.format(pk)
http://localhost:5000/users/user/1?q=foo
GET
ImmutableMultiDict([('q', u'foo')])
Request
@users.route('/user/<int:pk>')
@login_required
def user_detail(pk):
return '<h1>Hello, user #{}!</h1>'.format(pk)
Decoradores
from flask import render_template
@users.route('/user/<int:pk>')
def user_detail(pk):
return render_template('users/detail.html', user_pk=pk)
<html>
<body>
<h1>Hello, user #{{ user_id }}!</h1>
</body>
</html>
Variable de contexto
users/views.py:
users/templates/users/detail.html:
Variables:
{{ foo.bar }}
{{ foo['bar'] }}
Condicionales:
{% if user.address %}
<p>{{ user.address }}</p>
{% endif %}
Bucles:
{% for user in users %}
<li>{{ user.name }}</li>
{% else %}
<li>No hay usuarios</li>
{% endfor %}
Bloques:
{% block title %}Usuarios{% endblock %}
Extender una plantilla base:
{% extends "layouts/main.html" %}
Comentario:
{# Texto #}
Incluir otra plantilla:
{% with rating=user.rating %}
{% include 'includes/_rating.html' %}
{% endwith %}
Asignar valor a una variable local:
{% set permissions = user.get_permission() %}
{{ permissions }}
Filtros:
{{ user.address|default('N/A') }}
Variables estándares de contexto:
config - Objeto de configuración (flask.config) {{ config.DEBUG }} request - Objeto de la petición actual (flask.request) {{ request.path }} session - Objeto de sesión (flask.session) g - Variables globales
Pasar variables a todas las plantillas:
def create_app():
app = Flask(__name__, static_url_path='/static')
app.config.from_object('conf.config')
@app.context_processor
def constants_processor():
return {
'say_hello': 'Hola',
}
return app
run.py:
$ pip install SQLAlchemy
$ pip install Flask-SQLAlchemy
from flask_sqlalchemy import SQLAlchemy
db = SQLAlchemy()
def create_app():
app = Flask(__name__, static_url_path='/static')
app.config.from_object('conf.config')
db.init_app(app)
return app
run.py:
SQLALCHEMY_DATABASE_URI = 'sqlite:////tmp/test.db'
config.py:
Common Filter Operators: docs.sqlalchemy.org/en/latest/orm/tutorial.html#common-filter-operators
from run import db
class User(db.Model):
__tablename__ = 'users'
id = db.Column(db.Integer, primary_key=True)
username = db.Column(db.String(80), unique=True)
email = db.Column(db.String(120), unique=True)
city_id = db.Column(db.ForeignKey(u'cities.id'), nullable=False, index=True)
created_at = db.Column(db.DateTime, nullable=False, default=db.func.now())
updated_at = db.Column(db.DateTime, nullable=False, default=db.func.now(),
onupdate=db.func.now())
def __repr__(self):
return '<Usuario %r>' % self.username
class City(db.Model):
__tablename__ = 'cities'
id = db.Column(db.Integer, primary_key=True)
name = db.Column(db.String(255))
users = db.relationship('User', backref='city', lazy='dynamic')
users/models.py:
Select, Insert, Delete: flask-sqlalchemy.pocoo.org/2.1/queries/
Crear user = User(email='john@example.com', username='') db.session.add(user) db.session.commit() Obtener todos usuarios users = User.query.all() # [<Usuario john'>, <Usuario u'admin'>] Obtener el primer usuario john = User.query.first() # <Usuario 'john'> Filtrar usuarios y ordenar User.query.filter_by(username='john').all() User.query.filter( User.created_at >= (datetime.datetime.utcnow() - datetime.timedelta(days=3) ).order_by(User.created_at.desc()).limit(10).all() Borrar db.session.delete(user) db.session.commit()
import wtforms
from flask_wtf import Form
class UserForm(Form):
email = wtforms.StringField(
validators=[
wtforms.validators.Email(),
wtforms.validators.DataRequired(),
],
)
username = wtforms.StringField(
validators=[wtforms.validators.DataRequired()],
)
submit = wtforms.SubmitField('Save')
users/forms.py:
$ pip install Flask-WTF
$ pip install Flask-Bootstrap
StringField
TextAreaField
BooleanField
DateField
DateTimeField
DecimalField
FloatField
IntegerField
SelectField
SelectMultipleField
FileField
HiddenField
PasswordField
SubmitField
Campos:
{% extends "layouts/main_layout.html" %}
{% import "bootstrap/wtf.html" as wtf %}
{% block content %}
{{ wtf.quick_form(form) }}
{% endblock %}
users/templates/users/form.html:
{{ wtf.form_field(form.email) }}
{{ wtf.form_field(form.submit) }}
Referirse a un campo específico:
~ crispy-forms en Django
@users.route('/update/<int:pk>/', methods=('GET', 'POST'))
def user_update(pk):
user = User.query.filter_by(id=pk).first_or_404()
form = UserForm(user, request.form)
if request.method == 'POST' and form.validate():
form.populate_obj(user)
db.session.add(user)
db.session.commit()
flash('Usuario fue editado exitosamente', 'success')
return redirect(url_for('users.user_detail', pk=user.id))
return render_template(
'users/form.html',
form=form,
)
users/views.py:
Procesar el formulario en la vista:
Obtener formulario
del request
form.validate() # True/False
Validación:
from flask_login import LoginManager
login_manager = LoginManager()
login_manager.login_view = 'users.login'
def create_app():
app = Flask(__name__, static_url_path='/static')
app.config.from_object('conf.config')
app.permanent_session_lifetime = datetime.timedelta(days=365)
login_manager.init_app(app)
users/run.py:
$ pip install Flask-Login
$ pip install Flask-OAuth
from flask_login import UserMixin
from run import db
class User(db.Model, UserMixin):
__tablename__ = 'users'
id = Column(Integer, primary_key=True)
# ...
users/models.py:
@main.route('/login')
def login():
callback = url_for('main.authorized', _external=True)
return google.authorize(callback=callback)
users/views.py:
from flask_login import current_user
# Extraer los textos para traducción
# (se corre sólo una vez al principio)
$ pybabel extract -F babel.cfg -o messages.pot .
# Generar un catálogo para español
$ pybabel init -i messages.pot -d translations -l es
# Se crea el directorio translations/es
# Por dentro hay otro directorio llamado LC_MESSAGES que tiene
# un archivo messages.po.
# Después de traducir los textos y guardarlos en messages.po,
# compilamos el archivo y publicamos los textos:
$ pybabel compile -d translations
# Para actualizar las traducciones a diario:
$ pybabel extract -F babel.cfg -o messages.pot .
$ pybabel update -i messages.pot -d translations
$ pip install Flask-Babel
En una vista:
from flask_babel import gettext as _
_('Invalid authentication token')
En una plantilla:
<button class="btn btn-default" title="{{ _('Help') }}">
<i class="fa fa-question"></i>
</button>
En Django:
from django.contrib import messages
messages.add_message(request, messages.INFO, 'Hello world.')
En Flask:
from flask import flash
flash('Hello world.', 'success')
{% for category, message in get_flashed_messages(with_categories=true) %}
<div class="alert alert-{{ category|replace('message', 'info') }}">
<button type="button" class="close" data-dismiss="alert">×</button>
{{ message }}
</div>
{% endfor %}
from flask_cache import Cache
cache = Cache()
def create_app():
app = Flask(__name__, static_url_path='/static')
app.config.from_object('conf.config')
cache.init_app(app)
users/run.py:
$ pip install Flask-Cache
En una vista:
from run import cache
@users.route('/list')
@cache.cached(timeout=60*60, key_prefix='user_list')
def user_list():
pass
En una plantilla:
{% cache 60*60, 'dashboard_menu_user' + current_user.id|string %}
{% include 'layouts/_menu.html' %}
{% endcache %}
# Framework
Flask==0.11
# Libs
celery==3.1.20
coverage==4.0.3
factory-boy==2.6.1
SQLAlchemy==1.0.12
SQLAlchemy-Utils==0.31.6
# Flask libs
Flask-And-Redis==0.6
Flask-Babel==0.9 # Múltiples idiomas y zonas horarias
Flask-Bootstrap==3.3.5.7 # ~ django-crispy-forms para Bootstrap
Flask-Cache==0.13.1
Flask-DebugToolbar==0.10.0 # En Django: django-debug-toolbar
Flask-fillin==0.2 # Diligenciar formularios en pruebas
Flask-Login==0.3.2
Flask-Migrate==1.8.0
Flask-Moment==0.5.1 # Integración con moment.js
Flask-OAuth==0.12 # ~ python-social-auth
Flask-Script==2.0.5 # manage.py, shell
Flask-SQLAlchemy==2.1
Flask-WTF==0.12 # Formularios con protección CSRF
WTForms-Alchemy==0.15.0 # Para crear formularios basados en modelos
WTForms-Components==0.10.0 # Campos adicionales para los formularios de Flask