Fork me on GitHub

Bruno Rocha at 14:30 of 06/02/2014

What The Flask

6 passos para ser um Flask ninja!

Olá, nesta semana comecei a publicar a série What The Flask lá no PythonClub.

http://res.cloudinary.com/diu8g9l0s/image/upload/v1400201393/pythonclub/logo_275x130.png

para quem ainda não conhece, o PythonClub é um blog colaborativo feito pela comunidade e hospedado no github. Colabore você também!

Nesta série de 6 artigos/tutoriais pretendo abordar de maneira bem detalhada o desenvolvimento web com o framework Flask.

Depois de mais de um ano desenvolvendo projetos profissionais com o Flask e adquirindo experiência também no desenvolvimento do projeto open source Quokka CMS resolvi compartilhar algumas dicas para facilitar a vida de quem pretende começar a desenvolver para web com Python.

A série What The Flask será dividida nos seguintes capítulos.

  1. Hello Flask: Introdução ao desenvolvimento web com Flask
  2. Flask patterns: boas práticas na estrutura de aplicações Flask
  3. Plug & Use: extensões essenciais para iniciar seu projeto
  4. DRY: Criando aplicativos reusáveis com Blueprints
  5. from flask.ext import magic: Criando extensões para o Flask e para o Jinja2
  6. Run Flask Run: "deploiando" seu app nos principais web servers e na nuvem.

Hello Flask

Parte 1 - Introdução ao desenvolvimento web com Flask

Conhecendo o Flask

Quick and Dirty Tutorial: Desenvolvendo um aplicativo de notícias

Para acompanhar a série completa acesse o PythonClub

Comentários, dúvidas, sugestões e formulário a respeito do curso online e do livro de Flask também estão no lá no final do artigo no PythonClub. :)

read more

Bruno Rocha at 00:37 of 04/19/2014

Como framework o Flask não tem nenhuma funcionalidade de cache embutida, porém existe a cache API do werkzeug e uma excelente extensão para prover essas funcionalidades de cache para suas aplicacoes Flask, esta extensão foi criado pelo @thadeusb e é bastante fácil de implementar e utilizar.

instalando

Em sua env instale pelo PyPI (recommended)

pip install Flask-Cache  

Você também pode instalar diretamente através do github se quiser pegar a última versao de desenvolvimento.

pip install https://github.com/thadeusb/flask-cache/tarball/master

Configurando

Existe uma série de chaves de configuração que podem ser setadas, mas a mais importante é a que define o backend de cache CACHE_TYPE.

A chave CACHE_TYPE pode ser uma string resolvendo um caminho de importação de uma classe que implemente a api de cache do werkzeug, mas existem alguns alias que mapeam as implemetações default que estão no werkzeug.contrib.cache

Por padrão o CACHE_TYPE é NUll o que significa que o cache não terá efeito, então você precisa escolher um backend para o cache.

chave | Cache type

  • null | Sem Cache - NullCache
  • simple | Usará pickle em memória e é recomendado apenas em servidor single process durante o desenvolvimento
  • memcached | Requer pylibmc ou memcached e precisa das configs do memcached no settings
  • redis | Requer redis, werkzeug 0.7 e configurações do Redis no settings
  • filesystem | O mesmo que o simple, porém armazena o pickle em filesystem ao inves da memoria
  • gaememcached | Para o Google AppEngine
  • saslmemcached | Mesmo que o memcached mas para instalações em SASL - requer pylibmc

Todas as opções e outras chaves de config em http://pythonhosted.org/Flask-Cache/#configuring-flask-cache

App Flask simples

em um arquivo nomeado app.py

import time
from flask import Flask

app = Flask(__name__)

@app.route("/")
def view():
    return time.ctime()

if __name__ == "__main__":
    app.run(port=5000, debug=True, host='0.0.0.0')

Execute com python app.py e abra a url http://localhost:5000 no seu browser, aperte F5 (refresh) para ver a data e hora correntes.

Habilitando o cache para a view

agora vamos habilitar o cache para esta pequena app e evitar que a hora seja atualizada a cada request. (neste exemplo usamos a data atual mas imagine que isso poderia ser o carregamento de um grande conjunto de dados ou cálculos complexos)

em um arquivo nomeado cached_app.py

import time
from flask import Flask

# importe a extensao
from flask.ext.cache import Cache   

app = Flask(__name__)

# defina a configuração do cache (isso pode ser feito em um arquivo de settings)
app.config['CACHE_TYPE'] = 'simple'

# instancie o cache e atribua a sua aplicação
app.cache = Cache(app)   

@app.route("/")
@app.cache.cached(timeout=300)  # cache this view for 5 minutes
def cached_view():
    return time.ctime()

if __name__ == "__main__":
    app.run(port=5000, debug=True, host='0.0.0.0')

Execute com python cached_app.py e abra http://localhost:5000 em seu browser e aperte F5 (refresh) para ver a data e hora cacheadas durante 5 minutos.

O decorator @cache.cached utiliza o request.path para a view como chave de cache, se por algum motivo você precisar especificar uma chave diferente poderá passar o argumento key_prefix para o decorator. Neste caso, se o seu argumento incluir um placeholder "%s" ele será substituido pelo request.path.

O exemplo acima é a mais simples e regular app Flask com o uso de cache, mas se sua app é projetada usando app factories, blueprints, class based views ou views separadas em arquivos diferentes você precisará usar abordagens avançadas.

Cacheando funções

O mesmo decorator cached pode ser usado para cachear funções (que não são views), neste caso será preciso especificar um key_prefix, caso contrário o request.path será usado e então poderá cair em conflito quando existir mais de uma função cacheada.

Neste exemplo usaremos o módulo this para extrair citações do Zen do Python.

Em um arquivo nomeado cached_function_app.py

import time
import random

from this import s, d
from string import translate, maketrans

from flask.ext.cache import Cache
from flask import Flask

app = Flask(__name__)
app.config['CACHE_TYPE'] = 'simple'
app.cache = Cache(app)

@app.cache.cached(timeout=10, key_prefix="current_time")
def get_current_time():
    return time.ctime()

def random_zen_quote():
    """Pega uma citação aleatória do Zen do Python""" 
    transtable = maketrans("".join(d.keys()), "".join(d.values()))
    return random.choice(translate(s, transtable).split("\n")[2:])

@app.route("/")
def zen():
    return """
    <ul>
        <li><strong>Com cache:</strong> {cached}</li>
        <li><strong>Sem cache:</strong> {not_cached}</li>
    </ul>
    """.format(
        cached=get_current_time(),
        not_cached=random_zen_quote()
    )

if __name__ == "__main__":
    app.run(debug=True, port=5000, host='0.0.0.0')

Execute com python cached_function_app.py acesse http://localhost:5000 apertando F5 (refresh) você verá que a data atual é cacheada mas a citação continua sendo atualizada, você pode alternar o cache para ver como fica.

def get_current_time():
    return time.ctime()

@app.cache.cached(timeout=10, key_prefix="zen_quote")
def random_zen_quote():
    transtable = maketrans("".join(d.keys()), "".join(d.values()))
    return random.choice(translate(s, transtable).split("\n")[2:])

@app.route("/")
def zen():
    return """
    <ul>
        <li><strong>Sem cache:</strong> {cached}</li>
        <li><strong>Com cache:</strong> {not_cached}</li>
    </ul>
    """.format(
        cached=get_current_time(),
        not_cached=random_zen_quote()
    )

NOTE: por ter usado o módulo this você verá algumas citações serem impressas em seu terminal mas isso não tem nenhum problema.

Cacheando views modulares

agora um exemplo com views separadas em um arquivo diferente para melhor organização

Em um pasta chamada app coloque 3 arquivos __init__.py, app.py e views.py

app/__init__.py um arquivo vazio

app/views.py

import time
import random
from this import s, d
from string import translate, maketrans

def get_current_time():
    return time.ctime()

def random_zen_quote():
    transtable = maketrans("".join(d.keys()), "".join(d.values()))
    return random.choice(translate(s, transtable).split("\n")[2:])

def zen_view():
    return """
    <h1>No cache por 10 segundos!</h1>
    <ul>
        <li>{time}</li>
        <li>{quote}</li>
    </ul>
    """.format(
        time=get_current_time(),
        quote=random_zen_quote()
    )

Como pode ver no exemplo acims definimos a view em um arquivo separado. Para evitar import circular não seria recomendado usar @app.route nem o @app.cache portanto esta view será agnostica com relação a app pois iremos registrar seu mapeamento de urls e seu cache no arquivo principal da app.

Este tipo de estrutura é necessária quando se tem muitas views e é preciso organizar em vários arquivos separados.

NOTE: Para uma melhor organização é recomendado o uso de blueprints que será explicado em seguida.

app/app.py

agora na app principal importaremos as views e explicitamente decoraremos com o cache e também registraremos a regra de url.

from flask import Flask
from flask.ext.cache import Cache
from views import zen_view

app = Flask(__name__)
app.config['CACHE_TYPE'] = 'simple'
app.cache = Cache(app)

# aplique o cache usando o velho estilo de uso dos decorators
cached_zen_view = app.cache.cached(timeout=10)(zen_view)

# registre o mapeamento de url
app.add_url_rule("/", view_func=cached_zen_view)

if __name__ == "__main__":
    app.run(debug=True, port=5000, host='0.0.0.0')

NOTE: Tambem é possivel colocar o cache em um arquivo separado como veremos em seguida.

Cacheando views em Blueprints

Como foi mencionado anteriormente, a melhor maneira de organizar uma app Flask é com o uso de Blueprints, que é uma maneira de criar ' meta-apps' que serão conectadas ao app principal no momento da inicialização, O problema aqui é que o Blueprint é feito pensando em criar módulos reutilizaveis por diferentes apps, portanto o controle do cache precisa ser delegado dinamicamente.

Para evitar import circular criaremos uma instancia de cache em um arquivo separado (considere usar application factory se estiver criando algo mais complexo)

Crie uma pasta chamada blueprint_app com os seguintes arquivos

cached_blueprint_app/
├── app.py
├── cache.py
├── blueprints
│   ├── __init__.py
│   └── zen_blueprint.py
└── __init__.py

No cache.py

from flask.ext.cache import Cache    
cache = Cache()

Criamos uma instancia burra do Cache, ela ainda não foi inicializada mas já pode ser usada, no futuro ela será inicializada junto com a app e estará disponivel quando a view for chamada. Para isso reimportaremos a mesma instancia de cache e chamaremos o metodo init_app.

Um blueprint básico blueprints/zen_blueprint.py

import time
import random
from this import s, d
from string import translate, maketrans
from flask import Blueprint
from cache import cache

zen = Blueprint('zen', __name__)

def get_current_time():
    return time.ctime()

def random_zen_quote():
    transtable = maketrans("".join(d.keys()), "".join(d.values()))
    return random.choice(translate(s, transtable).split("\n")[2:])

@zen.route("/")
@cache.cached(timeout=20)
def zen_view():
    return """
    <h1>Cacheado por 20 segundos!</h1>
    <ul>
        <li>{time}</li>
        <li>{quote}</li>
    </ul>
    """.format(
        time=get_current_time(),
        quote=random_zen_quote()
    )

NOTE: Em uma aplicação real é recomendado separar mais o blueprint em arquivos diferents para views, urls e helpers, para isso você poderá promover este blueprint para um pacote Python.

A app principal app.py

from flask import Flask

from blueprints.zen_blueprint import zen
from cache import cache

app = Flask(__name__)
app.config['CACHE_TYPE'] = 'simple'
cache.init_app(app)

app.register_blueprint(zen)

if __name__ == "__main__":
    app.run(debug=True, port=5000, host='0.0.0.0')

Repare que criamos a instancia burra do cache no arquivo cache.py e usamos esta instancia para decorar as views do blueprint, então o cache foi inicializado no app.py utilizando o método init_app. Isso e possivel por conta do ciclo de inicialização do Flask e também pelo fato da extensão Flask-Cache ter sido criada pensando nesses casos. Se um dia pretender escrever sua própria extensão para o Flask aconselho dar uma olhada no código fonte da Flask-Cache.

Execute com python cached_blueprint_app/app.py acesse http://localhost:5000 e a view será cacheada por 20 segundos

Cacheando MethodViews

Vamos usar o mesmo exemplo cached_blueprint_app transformado a view zen_view em uma MethodView

Altere o zen_blueprint.py para:

import time
import random
from this import s, d
from string import translate, maketrans
from flask import Blueprint
from flask.views import MethodView
from cache import cache

zen = Blueprint('zen', __name__)

class ZenView(MethodView):

    @cache.cached(30)
    def get(self):
        return """
        <h1>Cacheado por 30 segundos!</h1>
        <ul>
            <li>{time}</li>
            <li>{quote}</li>
        </ul>
        """.format(
            time=self.get_current_time(),
            quote=self.random_zen_quote()
        )

    @staticmethod
    def get_current_time():
        return time.ctime()

    @staticmethod
    def random_zen_quote():
        transtable = maketrans("".join(d.keys()), "".join(d.values()))
        return random.choice(translate(s, transtable).split("\n")[2:])


zen.add_url_rule("/", view_func=ZenView.as_view('zen'))

MethodViews mapeiam os métodos HTTP como GET, POST e DELETE para métodos da classe como get, post, delete etc, então neste caso criamos um método chamado get e decoramos com o @cache.cached.

NOTE: Você geralmente não pode usar decorators em métodos individuais de MethodViews, porém o Flask-Cache foi implementado de forma que permite este uso.

alternativamente você pode querer cachear todos os métodos de uma mesma view, para isso basta cachear o dispatch_request ou melhor ainda cachear a view completa usando um decorator explicito.

Cacheando o dispatcher
class ZenView(MethodView):
    @cache.cached(timeout=30)
    def dispatch_request(self):
        return super(ZenView, self).dispatch_request()

    ...
Cacheando a view com decorator explicito (recomendado)
zen = Blueprint('zen', __name__)

class ZenView(MethodView):
    ...

cached_zen_view = cache.cached(timeout=50)(ZenView.as_view('zen'))
zen.add_url_rule("/", view_func=cached_zen_view)

Cacheando blocos de template

O Flask-Cache vem com uma template tag que permite cachear blocos de template, vamos mudar a ZenView e faze-la renderizar um template com Jinja2

No zen_blueprint.py

import time
import random
from this import s, d
from string import translate, maketrans
from flask import Blueprint, render_template
from flask.views import MethodView

zen = Blueprint('zen', __name__)

class ZenView(MethodView):

    def get(self):
        return render_template(
            'zen.html',
            get_random_quote=self.random_zen_quote
        )

    @staticmethod
    def get_current_time():
        return time.ctime()

    @staticmethod
    def random_zen_quote():
        transtable = maketrans("".join(d.keys()), "".join(d.values()))
        return random.choice(translate(s, transtable).split("\n")[2:])

zen.add_url_rule("/", view_func=ZenView.as_view('zen'))

Agora teremos que criar o template cached_blueprint_app/templates/zen.html

<h3> Zen of Python Aleatório </h3>
<strong>{{get_random_quote()}}</strong>

Execute com python cached_blueprint_app/app.py e abra http://localhost:5000 você verá a citação ser atualizada a cada request, vamos evitar isso cacheando durante 30 segundos.

Change the zen.html template

{% cache 30 %}
<h3> Zen of Python Aleatório </h3>
<strong>{{get_random_quote()}}</strong>
{% endcache %}

Salve o template e de o refresh varias vezes e você verá que a citação estará cacheada por 30 segundos.

Cacheando funções com argumentos variaveis usando o decorator memoize

as vezes as views ou funções recebem argumentos através do mapeamento de urls ou diretamente passados, você pode querer cachear utilizando esses argumentos como chave do cache para ter caches diferentes para cada chamada. No Flask-Cache tem o decorator memoize para isso.

NOTE: Para funções que não recebem argumentos cached e memoize terão o mesmo efeito

Com uma app simples memoize_app.py

import time
from flask.ext.cache import Cache
from flask import Flask

app = Flask(__name__)
app.config['CACHE_TYPE'] = 'simple'
app.cache = Cache(app)

@app.cache.memoize(timeout=5)
def get_current_time_and_name(name):
    return "%s - %s" % (name, time.ctime())

@app.route("/<name>")
def view(name):
    return get_current_time_and_name(name)

if __name__ == "__main__":
    app.run(debug=True, port=5000, host='0.0.0.0')

Rode com python memoize_app.py abra http://localhost:5000/seunome e repare que a chamada é cacheada separadamente para cada argumento passado.

Cacheando objetos diretamente

As vezes não podemos usar decorators e precisamos cachear objetos diretamente.

Dentro de uma view ou blueprint é possivel usar o current_app

from flask import current_app

def some_function():
    cached = current_app.cache.get('a_key')
    if cached:
        return cached
    result = do_some_stuff()
    current_app.cache.set('a_key', result, timeout=300)
    return result

Ou se estiver usando uma instancia separada de cache (recomendado)

from cache import cache

def function():
    cached = cache.get('a_key')
    if cached:
        return cached
    result = do_some_stuff()
    cache.set('a_key', result, timeout=300)
    return result

Limpando o cache

Você pode criar um script para limpar o cache ou uma função para ser chamada quando preciso.

from flask.ext.cache import Cache    
from yourapp import app
cache = Cache()

def main():
    cache.init_app(app)

    with app.app_context():
        cache.clear()

if __name__ == '__main__':
    main()

AVISO: Em alguns backends a limpeza completa do cache não é suportada. Se não estiver usando key_prefix em alguns backends como redis irá fazer com que todo o database seja apagado.

Existe uma série de exemplos e uma API bem documentada no site do Flask-Cache http://pythonhosted.org/Flask-Cache/ também é possivel criar um novo backend de cache seguindo este exemplo.

read more

Bruno Rocha at 18:12 of 04/17/2014

As a micro framework Flask does not have built-in cache functionality, however, there is werkzeug cache API and an excellent extension to provide its caching functionality to your Flask apps, that extension was created by @thadeusb and is very easy to implement and use.

installing

In your env install it via PyPI (recommended)

pip install Flask-Cache  

You can also install it directly from source code if you need recent changes or bugfixes

pip install https://github.com/thadeusb/flask-cache/tarball/master

Configuring

There is a set of configuration keys you can put in your app settings, but the most important is the cache backend defined by the CACHE_TYPE key.

The cache type resolves to an import string which needs to be an object implementing the werkzeug cache api, but there is some aliases to the werkzeug.contrib.cache implementations

By default the CACHE_TYPE is Null which means that your app will have no cache, so you need to choose of the options below:

  • null | No Cache - NullCache
  • simple | Will use in memory pickle and is recommended only for single process development server
  • memcached | Requires pylibmc or memcached and requires memcached configuration in settings
  • redis | Requires redis, werkzeug 0.7 and redis configuration in settings
  • filesystem | The same as simple but stores the pickle in a cache_dir
  • gaememcached | For Google AppEngine
  • saslmemcached | The same as memcached but for SASL - pylibmc required

Full options and config variables are in http://pythonhosted.org/Flask-Cache/#configuring-flask-cache

A Simple Flask app

a file named app.py

import time
from flask import Flask

app = Flask(__name__)

@app.route("/")
def view():
    return time.ctime()

if __name__ == "__main__":
    app.run(port=5000, debug=True, host='0.0.0.0')

Run the above with python app.py and open http://localhost:5000 in your browser and hit F5 (refresh) to see the current date and time.

Enabling Flask-Cache for views

Now we want to enable caching on that small application to avoid the refresh of the current time (for this example we are using current time as return but imagine that it could be a large dataset or huge calculations)

a file named cached_app.py

import time
from flask import Flask

# import the flask extension
from flask.ext.cache import Cache   

app = Flask(__name__)

# define the cache config keys, remember that it can be done in a settings file
app.config['CACHE_TYPE'] = 'simple'

# register the cache instance and binds it on to your app 
app.cache = Cache(app)   

@app.route("/")
@app.cache.cached(timeout=300)  # cache this view for 5 minutes
def cached_view():
    return time.ctime()

if __name__ == "__main__":
    app.run(port=5000, debug=True, host='0.0.0.0')

Run the above with python cached_app.py and open http://localhost:5000 in your browser and hit F5 (refresh) to see that the current date and time is now cached for 5 minutes.

@cache.cached decorator takes the request.path for that view and use this as cache key, if for any reason you need a different key you can pass a key_prefix argument to the decorator. In this case if you pass a key_prefix containing the %s placeholder it will be replaced by the current request.path

The above is the simplest and regular example of Flask app and the use of cache, but, if your app is designed using application factories, blueprints, class based views or views located in different modules you will need to use advanced approach.

Caching regular functions

The same cached decorator can be used to cache regular functions, but in this case you will need to specify the key_prefix argument, otherwise it will use the request.path which can lead to conflicts if you have many cached functions.

For this example we are going to use the this module and extract a random quote from the Zen of Python.

A file named cached_function_app.py

import time
import random

from this import s, d
from string import translate, maketrans

from flask.ext.cache import Cache
from flask import Flask

app = Flask(__name__)
app.config['CACHE_TYPE'] = 'simple'
app.cache = Cache(app)

@app.cache.cached(timeout=10, key_prefix="current_time")
def get_current_time():
    return time.ctime()

def random_zen_quote():
    """Pick a random quote from the Zen of Python""" 
    transtable = maketrans("".join(d.keys()), "".join(d.values()))
    return random.choice(translate(s, transtable).split("\n")[2:])

@app.route("/")
def zen():
    return """
    <ul>
        <li><strong>It is cached:</strong> {cached}</li>
        <li><strong>It is not cached:</strong> {not_cached}</li>
    </ul>
    """.format(
        cached=get_current_time(),
        not_cached=random_zen_quote()
    )

if __name__ == "__main__":
    app.run(debug=True, port=5000, host='0.0.0.0')

Now running python cached_function_app.py and opening http://localhost:5000 when hitting F5 to refresh you will see the current time cached for 5 minutes and the random quote updated, you can switch the cache just to see the efect.

def get_current_time():
    return time.ctime()

@app.cache.cached(timeout=10, key_prefix="zen_quote")
def random_zen_quote():
    transtable = maketrans("".join(d.keys()), "".join(d.values()))
    return random.choice(translate(s, transtable).split("\n")[2:])

@app.route("/")
def zen():
    return """
    <ul>
        <li><strong>It is not cached:</strong> {cached}</li>
        <li><strong>It is cached:</strong> {not_cached}</li>
    </ul>
    """.format(
        cached=get_current_time(),
        not_cached=random_zen_quote()
    )

NOTE: Because we are importing the this module for the example, you will see the Zen quotes in your flask terminal, but there is no problem with this.

Caching modular views

Now an example when you have your app splitted in two or more files for better organization

in a folder called app put 3 files__init__.py, app.py and views.py

app/__init__.py is an empty file

app/views.py

import time
import random
from this import s, d
from string import translate, maketrans

def get_current_time():
    return time.ctime()

def random_zen_quote():
    transtable = maketrans("".join(d.keys()), "".join(d.values()))
    return random.choice(translate(s, transtable).split("\n")[2:])

def zen_view():
    return """
    <h1>Cached for 10 seconds!</h1>
    <ul>
        <li>{time}</li>
        <li>{quote}</li>
    </ul>
    """.format(
        time=get_current_time(),
        quote=random_zen_quote()
    )

as you can see the above file defined view functions, as it it a separated file, to avoid circular imports we are not recommended to use @app.route neither @app.cache so this views will be app agnostic and we are going to register its url rules and caching in the main app file.

That kind of structure is needed when your app has too many views and want a better organization.

NOTE: For better organization the mostly recommended pattern is Blueprints which I will explain further.

app/app.py

Now in the main app we need to import our views, explicitly decorate for caching and also register its urls.

from flask import Flask
from flask.ext.cache import Cache
from views import zen_view

app = Flask(__name__)
app.config['CACHE_TYPE'] = 'simple'
app.cache = Cache(app)

# explicitly apply the cache in the old-style decoration way
cached_zen_view = app.cache.cached(timeout=10)(zen_view)

# explicitly register the cached view url mapping
app.add_url_rule("/", view_func=cached_zen_view)

if __name__ == "__main__":
    app.run(debug=True, port=5000, host='0.0.0.0')

NOTE: You can also separate the cache instance in a different file for lazy initialization as we are going to see in the next example

Caching Blueprint views

As mentioned before, the best pattern to follow in Flask applications is the Blueprint pattern which is a way to create separated 'meta-apps' that will be connected to your main application in the time of initialization, the problem here is that Blueprints are meant to be reusable by many different applications, so the delegation of cache control should be dynamized.

In order to avoid circular imports you will want to create your cache instance separate from your application instance (you may want to consider switching to the app factory module if you are building something more complex).

Create a folder called blueprint_app with the following structure

cached_blueprint_app/
├── app.py
├── cache.py
├── blueprints
│   ├── __init__.py
│   └── zen_blueprint.py
└── __init__.py

The cache.py

from flask.ext.cache import Cache    
cache = Cache()

we can create a dummy lazy cache instance, that will be initialized in the future when the view will be called. For that in the app we are going to reimport the same cache instance and call init_app method.

The basic blueprints/zen_blueprint.py

import time
import random
from this import s, d
from string import translate, maketrans
from flask import Blueprint
from cache import cache

zen = Blueprint('zen', __name__)

def get_current_time():
    return time.ctime()

def random_zen_quote():
    transtable = maketrans("".join(d.keys()), "".join(d.values()))
    return random.choice(translate(s, transtable).split("\n")[2:])

@zen.route("/")
@cache.cached(timeout=20)
def zen_view():
    return """
    <h1>Cached for 20 seconds!</h1>
    <ul>
        <li>{time}</li>
        <li>{quote}</li>
    </ul>
    """.format(
        time=get_current_time(),
        quote=random_zen_quote()
    )

NOTE: In a real application you will want to modularize it separating the views, helpers etc and promoting your blueprint to a Python package.

The main app.py

from flask import Flask

from blueprints.zen_blueprint import zen
from cache import cache

app = Flask(__name__)
app.config['CACHE_TYPE'] = 'simple'
cache.init_app(app)

app.register_blueprint(zen)

if __name__ == "__main__":
    app.run(debug=True, port=5000, host='0.0.0.0')

Notice that we created a dummy instance of cache in cache.py and then used that instance to decorate the blueprints views, then the cache was initialized in app.py with init_app method. That is possible because of the Flask initialization cycle and the excellent implementation in Flask-Cache extension that takes care of this case, if you plan to write yor own Flask extension take a look at the Flask-Cache source code.

Run the application by calling python cached_blueprint_app/app.py and open http://localhost:5000 to see the blueprint view cached for 20 seconds.

Caching MethodView

Lets use the same cached_blueprint_app example but turning the zen_view in to a MethodView

Change your zen_blueprint.py to:

import time
import random
from this import s, d
from string import translate, maketrans
from flask import Blueprint
from flask.views import MethodView
from cache import cache

zen = Blueprint('zen', __name__)

class ZenView(MethodView):

    @cache.cached(30)
    def get(self):
        return """
        <h1>Cached for 30 seconds!</h1>
        <ul>
            <li>{time}</li>
            <li>{quote}</li>
        </ul>
        """.format(
            time=self.get_current_time(),
            quote=self.random_zen_quote()
        )

    @staticmethod
    def get_current_time():
        return time.ctime()

    @staticmethod
    def random_zen_quote():
        transtable = maketrans("".join(d.keys()), "".join(d.values()))
        return random.choice(translate(s, transtable).split("\n")[2:])


zen.add_url_rule("/", view_func=ZenView.as_view('zen'))

Method views maps HTTP method names as GET, POST, DELETE to the view methos as get, post, delete etc, So all we needed to do is to create a method called get and decorate it with @cache.cached decorator.

NOTE: Due to the implicit self from the caller’s perspective you cannot use regular view decorators on the individual methods of the view however, Flask-Cache is one exception because its implementation allow the use of cached decorator in individual methods. Keep this in mind.

Alternativelly you may want to cache all the methods in a view, for that you can cache the dispatch_request method or even better you can decorate the whole view.

Caching the dispatcher
class ZenView(MethodView):
    @cache.cached(timeout=30)
    def dispatch_request(self):
        return super(ZenView, self).dispatch_request()

    ...
Caching the whole view (recommended)
zen = Blueprint('zen', __name__)

class ZenView(MethodView):
    ...

cached_zen_view = cache.cached(timeout=50)(ZenView.as_view('zen'))
zen.add_url_rule("/", view_func=cached_zen_view)

Caching template blocks

Flask cache comes with a template tag able to cache template blocks, lets change our ZenView to use a Jinja2 template

in zen_blueprint.py

import time
import random
from this import s, d
from string import translate, maketrans
from flask import Blueprint, render_template
from flask.views import MethodView

zen = Blueprint('zen', __name__)

class ZenView(MethodView):

    def get(self):
        return render_template(
            'zen.html',
            get_random_quote=self.random_zen_quote
        )

    @staticmethod
    def get_current_time():
        return time.ctime()

    @staticmethod
    def random_zen_quote():
        transtable = maketrans("".join(d.keys()), "".join(d.values()))
        return random.choice(translate(s, transtable).split("\n")[2:])

zen.add_url_rule("/", view_func=ZenView.as_view('zen'))

Now we need to create a template file in cached_blueprint_app/templates/zen.html

<h3> Random Zen of Python </h3>
<strong>{{get_random_quote()}}</strong>

Running the application with python cached_blueprint_app/app.py and opening http://localhost:5000 you will see a random quote refreshed every time you push F5, lets cache it for 30 second.

Change the zen.html template

{% cache 30 %}
<h3> Random Zen of Python </h3>
<strong>{{get_random_quote()}}</strong>
{% endcache %}

Now save the file and refresh to see the content cached for 30 seconds.

Caching functions and views with variant arguments using memoize decorator

Sometimes yout views and functions receives arguments which can come from url mapping or directly to the function call, yiou may want to cache the view or funtion and use the arguments as keys to cache its different results, Flask-Cache has a different decorator for doing that.

NOTE: With functions that do not receive arguments, cached() and memoize() are effectively the same.

Now with a simple application memoize_app.py

import time
from flask.ext.cache import Cache
from flask import Flask

app = Flask(__name__)
app.config['CACHE_TYPE'] = 'simple'
app.cache = Cache(app)

@app.cache.memoize(timeout=5)
def get_current_time_and_name(name):
    return "%s - %s" % (name, time.ctime())

@app.route("/<name>")
def view(name):
    return get_current_time_and_name(name)

if __name__ == "__main__":
    app.run(debug=True, port=5000, host='0.0.0.0')

Now run python memoize_app.py and open http://localhost:5000/yourname and note that the function will be cached for each different name you pass as argument in the url.

Caching arbitrary objects

There are some times when decorators cannot be used and you need to explicitly set or get some thing on the cache.

Inside a view or a blueprint you can use current_app

from flask import current_app

def some_function():
    cached = current_app.cache.get('a_key')
    if cached:
        return cached
    result = do_some_stuff()
    current_app.cache.set('a_key', result, timeout=300)
    return result

Or if using a separete cache instance you can do this directly

from cache import cache

def function():
    cached = cache.get('a_key')
    if cached:
        return cached
    result = do_some_stuff()
    cache.set('a_key', result, timeout=300)
    return result

Clearing the cache

You can create a script to clear the cache, or a function to use it when needed

from flask.ext.cache import Cache    
from yourapp import app
cache = Cache()

def main():
    cache.init_app(app)

    with app.app_context():
        cache.clear()

if __name__ == '__main__':
    main()

WARNING: Some backend implementation do not support completely clearing the case. Also, if you’re not using key prefix, some implementation (e.g. Redis) will flush the whole database. Make sure you’re not storing any other data in your caching database.

There is a lot of examples and well documented API in flask-Cache website http://pythonhosted.org/Flask-Cache/ you can also create your own cache backend following the examples in the Flask-Cache docs.

read more

Bruno Rocha at 20:36 of 11/28/2013

Last week I was writing a talk to give at Google Developers Bus and I needed to show how to integrate Flask and Google Maps API, as I did not found any extension to Google Maps I decided to create one.

One of the best things in Flask is the way it is extended by Extensions and Blueprints, by the way, Blueprints is one of the best idea I've seem in Python web frameworks, once you start working with Blueprints you want to use it everywhere (but unfortunatelly not every framework has an ellegant way to be extended)

I will start showing how the extension works and then I will explain how to build it from scratch.

Flask Google Maps

Template filter and a template global to create Google Maps from latitude and longitude

Installing

pip install flask-googlemaps

Loading in your app

from flask import Flask
from flask.ext.googlemaps import GoogleMaps
app = Flask(__name__)
GoogleMaps(app)

Using in templates

<div>
{{ googlemap('identifier', **params)}}
</div>

<div>
{{googlemap("map_name", lat=-0.12, lng=-0.45, markers=[(lat, lng), (lat, lng)]}} 
</div>

Parameters:

- identifier: The name tat will be used to identify your map (you can have multiple maps in one page)
- lat:  latitude to center the map
- lng: Longitude to center the map
- markers:  a list of tuples, each tuple is a (lat, lng) marker (a pointer in the map)
- zoom: percentage of the zoom
- maptype: Google map type, TERRAIN or ROADMAP. defaults to ROADMAP
- varname: The JS varname to bind the map, defaults to "map"
- style: css style to be appended to the < div >
- cls: css class to the map < div >

TODO: In near future it will be possible to pass an address as argument

Example

The template

<body>
<h1>Flask Google Maps Example</h1>

<h2> Google Dev Bus - Rua Quatá, 255 </h2>
{% with %}

    {% set location=(-23.599097,-46.675903) %}
    {% set style="width:500px;height:500px;"%}

    {{
    googlemap(
        "simple-map",
        location.0, location.1,
        markers=[location,],
        style=style
        )
    }}

{% endwith %}
</body>

The output

map

Github

Screenshots and docs on Github.

https://github.com/rochacbruno/Flask-GoogleMaps

How to create a Flask Extension

Flask is extendable by two patterns Extension and Blueprint

An Extension is something like a complete plugin, a distribution containing models, views, templates, static files, template globals and filters etc and usually an Extension is built of Blueprints, which is an app prototype, it can define the way an app will be when registered, exposing resources, and url rules, also the Blueprint has the capability to access the current running application to contribute with things like config values, template filters, etc.

In the Flask docs there is a great explanation on Extensions and Blueprints

Anathomy of an extension

An extension is just a Python package following the naming convention Flask_anything, with packages naming like that Flask will automatically find them in the Python PATH via the ext proxy. So instead of from flask_anything import something you can do from flask.ext.anything import something in that way the code will be very clear and explicit and you know you are dealing with a Flask extension.

Flask-Anything

root folder
|__ flask_anything/
    |__ templates/
    |__ static/
    |__ __init__.py
    |__ __some_module.py
    |__ *
|__ setup.py

That is it! you can write really anything you want and it will be available throught from flask.ext.anything.some_module import FooBar

What is inside?

Usually extensions expose a main Class which will be registered in your app, there is no rule, but there is some conventions, an example:

# flask.ext.anything.some_module.py

from flask import Blueprint

class Anything(object):
    def __init__(self, app=None, **kwargs):
        for k, v in kwargs.items():
            setattr(self, k, v)

        if app:
            self.init_app(app)

    def init_app(self, app):
        # here you get the app object and can do anything you want
        app.add_template_filter("pass a template filter function here")
        app.add_template_global("pass a template global here")

        app.add_url_rule("/anything/<arg>", view_func=self.something)

        #or even better you can register a Blueprint
        module = self.create_blueprint("module")
        another_module = self.create_another_blueprint("another_module")

        # then you can register many blueprints in the app
        app.register_blueprint(module)
        app.register_blueprint(another_module)


    def create_blueprint(self, blueprint_name):
        module =  Blueprint(blueprint_name, __name__, template_folder="a_path_to_relative_folder")
        module.add_app_template_filter(...)
        module.add-app_template_global(...)
        module.add_url_rule("/something/<argument>", view_func=self.some_view)
        return module

    def some_view(self, argument):
        context = {'argument': argument}
        return self.render(self.get_template_name, context)

    def render(self, *args, **kwargs):
        return render_template(*args, **kwargs)

    ...

By the community convention your extension main class should receive its configurations in __init__ method and should have a lazy way to init defined as a method called init_app, also is a good practice to create methods for things like create_blueprint, register_blueprint, get_url_rules and also a render_template method inside your Blueprint is usefull. That is because others could extend your class and overwrite them, in example to use Flask-Themes it is usefull to overwrite the render_template method.

Using your extension/Blueprint

# your_app.py

from flask import Flask
from flask.ext.anything.some_module import Anything

app = Flask(__name__)

# option 1
Anything(app)

# option 2
anything = Anything()
anything.init_app(app)

With the above instantiation/init_app your app will be manipulated by the extension and the views, urls, template filters etc will be available.

There is more conventions to follow as state, namespace, resource access etc, but you can find all the information on Flask docs.

If you have some idea to improve Flask-GoogleMaps, please comment!

read more

Bruno Rocha at 01:00 of 11/18/2013

I started the development of Quokka CMS in 6th July, and it started as an experiment to learn more about Flask patterns and MongoDB. From the learning experiment I ended creating an extendable CMS.

The new logo created by @guswidrig

Before talking about the project

I have to thank some friends who helped a lot during this development process, @jniltinho who developed the landing page, the demo website, deployment scripts and maintain the demo site, @guswidrig for the help with Quokka-Themes and the development of the new logo, @mrjoes who did not colaborated directly to Quokka core, but made significant enhancements to Flask-Admin which is the base for Quokka CMS, @ellisonleao fixed some compatibility bugs, @avelino for letting me stole some ideas from Opps CMS, @rturk and others who reported issues.

What's new in Quokka CMS?

I'll try to resume the important points of changelog since the first anouncement in July

  • Quokka got a new logo
  • Got a landing page
  • Documentation started to be writen in github wiki (yes we are not going to use sphinx)
  • Theming support (front-end and admin has support for multiple themes)
  • Can use a different theme for each channel or content
  • Reorganization of data models
  • Decoupled admin
  • New theme for front-end default theme based in Bootstrap 3
  • New admin theme based in Cosmo theme from bootstrap/bootswatch 2
  • New media management module (Work in Progress)
  • Decoupled comments and created a new internal "disqus like" comment system
  • Easy to setup disqus, facebook, intense debate etc
  • Google webmaster tools and analytics default settings
  • Better SEO settings in base templates
  • Support for multiple content formats (dafaults to HTML or Markdown)
  • Support for tinymce or lepture markdown editor
  • Pagination support (that was easy with mongoengine)
  • Customized MongoEngine lsit field to get filtering tools
  • Channel can be a redirect point
  • Channel can render a flat page or another content instead of channel lists
  • Dynamic template resolver, multiple template types for channels (list, grid, portal, blog)
  • Decoupled settings (now it is recommended to use settings based in environment variables)
  • "manage.py populate" to load sample data
  • Cookiecutter template to create quokka modules
  • Basic recommendation based in tags
  • Tags listings
  • ATOM/Feed for channels

Well that's the important implemented things and now the CMS is really "usable", but there is already some important things to do before the release of a beta version.

Roadmap to beta

There is a lot of basic things to be developed and I am counting on your help!

  • Search (needs full text based in MongoDB features)
  • REST api
  • Module and Themes Package Index python manage.py install theme|module module_name
  • Module management center in admin (allow installation of new modules via admin)
  • Theme management in admin (allow installation of new themes via admin)
  • Media management (need better widgets to manage related media in Content admin)
  • In place edit for posts
  • Basic admin index dashboard (it is already possible to customize the dashboard, it needs quick-post forms and other widgets)
  • Social auth
  • A lot of issues in github

Quokka modules

I already developed some Quokka modules ready for use:

  • Quokka-cart is a generic shopping cart
    It is a simple decopled shopping cart to be plugged in any application.

  • Quokka-classes is a classroom management system
    Currently it is only a showcase and course selling, but the idea is to create a complete LMS.

Sites using Quokka

  • YACOWS Academy is using Quokka and Quokka cart
  • BrunoRocha.org (this blog)

Some screenshots

See fullscreen image gallery

Click on the border gutter above to switch images in the gallery

How to join the project

You can help in different ways, testing, reporting issues, fixing some issues, developing new themes and modules or becoming a core developer, just fork it and send the pull requests.

Quokka the happiest ~~animal~~ CMS in the world

read more

Bruno Rocha at 11:14 of 07/19/2013

Quokka project - Flask and MongoDB powered CMS

logo

Fork on Github

Twitter @quokkaproject

Quokka is a flexible content management platform powered by Python, Flask and MongoDB.

Quokka provides a "full-stack" Flask application plus a bunch of selected extensions to provide all the needed CMS admin features and a flexible-easy way to extend the platform with quokka-modules built following the Flask Blueprints pattern.

some notes:

Quokka is a work in progress, it is in early stage of development but already works for the basic which is user authentication/authorization, article posting ans extending with blueprints.

This is not my first CMS project, I am also one of the developers of another CMS made in Django - Opps on github, Opps is a big project ready for production and running on large audience website, so if you need a CMS for production high traffic websites consider using Opps CMS by now.

So I started Quokka as an experiment to implement all the CMS features in a non-relational database, that's why I choose Flask and MongoDB. I think this project will get really serious because I am realizing that MongoDB fits perfectly for dynamic CMS. I am really open for suggestions and contributions.

Quokka - The happiest animal in the world and maybe the happiest CMS in the internets. :)

Installing modules

There is no need to install or include your modules in config files or change quokka code to load the module.

Just drop your module package in quokka/modules restart your server and done!

Quokka admin also provides a web interface for admin-users to install and ENABLE/DISABLE modules.

Admin interface

Admin interface uses a customized version of Flask-SuperAdmin

admin_overview

Please join us and contribute! Fork on Github

we need tests, docs, Flask, Mongo and front-end development help.

python flask mongo

read more

Bruno Rocha at 09:07 of 08/18/2012

I was google addicted, I used gmail drafts as bookmarks and todo tasks, I used google calendar for any little appointment and I always consider it too much complicated to manage simple scheduler there.

So pointing the problems I had centralizing everything in Google account, I tried a lot of web apps.

Finally I think I've find the perfect set of web tools to rely on


.net

Blogging and snippeting

I am not sure the word "snippeting" really exists, if not, I just invented. Programmers are always snippeting somewhere and there are a lot of tools over the web for doing this. gist, pastebin, paster and others. In 2009 I came through Snipt.net, at that time this already was an excellent tool and also I knew it was made with Python. Sometime ago @nick opened the source code and created a brand new snipt.net.

This is the platform tht powers this blog you are reading, also this is a snippet repository. It has a very good API and a web editor codemirror which supports almost every language in the world (even Brainfuck) and also as every good app wih an API it already has google chrome plugin for instant snippeting and also a plugin for the amazing Sublime text 2 - You can blog and create snippets from your prefered code editor.

  • Blogging
    • Create a snippet, choose markdown as syntax, mark as public/blog post and done!
  • Snippeting
    • from web using codemirror, from Chrome selecting a text, right click send to snipt or using the snipt.net addon, from Sublime text, select a text and send to snipt, also you can sync your sublime snipts with snipt.net. (note: the sublime plugin is under development)
things I really like in snipt.net
  • It is fast and has a nice UX
  • Chrome addon for blogging/snippeting
  • Sublime Text plugin (which I am trying to improve)
  • The "snipt" based configuration for blog
  • Amazing support

Ok, shut up and take my money! well, snipt.net is free, go there and create your account, Although, @nick is creating a lot of new features and improvements and to support his work you can sign-up for a PRO account (only $4 month or $40 year) - You will have a new PRO theme for blog soon, a very nice support and you will be able to use your own domain.

Trust me, you can put your $40/year on this Snipt.net/PRO

things I would like too see on snipt soon
  • Android/iOS apps
  • Create snippets via e-mail
  • Better Sublime-Text integration
  • A Python command line client, maybe integrated with iPython
  • Better blog options (snipt based configs) for theme, header, footer, widgets etc.
  • Better search
  • RSS feeds for specific tags
  • Flat pages

You can support @nick to develop all those great things, go there and purchase your PRO account!


The Pinterest for Bookmarks!

I also tried to use many tools for bookmarking, it always turns in to a hell. Delicious and its bad UX, ZooTool and its extremally fat UX, also the Chrome sync and firefox bookmarks did not help me.

So I found KIPPT. This is the Pinterest for bookmarks, you can use the addon on your browser, you can use the API or you can keep items directly on web interface.

The web interface is perfect! clean, easy to use and understand and it is a Pinterest like feed of links you want to keep, Also it has social features, you can share the links, you can follow people and their link feeds, you can comment and like links keeped by your friends. You can create shared collections so you allowed friends can manage links on that collection. It is fully integrated with your twitter favorites, github starred repositories and another APIs. It has Android and iOs apps.

Found an interesting link? Go the and Kippt

The only thing I am missing is the ability to read my mind. it would be better if I can think Keep this link on my Read Later list and so it automagically keeps it, But it is under development :)


Calendar finally is easy!

Do you really use Google Calendar? Do you always remember about appointments created there? For me Google Calandar is too much. So I want a place where I can just drop an email with the subject tomorrow 9am meeting, or I can send an IM using my Gtalk 15/07/2012 10pm watch the final episose of my favourite program or you can do it in a clean web interface.

So that tool is Coolendar - the best calendar i've ever used. Create your plans easily and receive reminders right on your e-mail or Gtalk. It is also a very nice TODO list just sends an IM or email to coolendar with today Finish the reports #todo So it will be tagged as a TODO plan. Yes it has a Chrome and firefox addon and also a Android and iOs apps.

Obviously it has Sync for Google Calendar and iCal.

Things I miss on coolendar

  • API, API, API
  • Command Line client
  • Shared calendars

Yes, i will put Python Anywhere on the category of web apps, because it is more than a simple host platform. It is a powerful and complete environment for developers.

Everyone who loves Python want to use Python Anywhere, and the best approach for this task is obviously called Python Anywhere what made PA so wonderful?

  • Cloud based consoles, not only for Python but also for Bash and MySQL
  • You can share live console session with others (Yes, code together, you can even do an online DOJO)
  • It offers Python (2.6, 2.7, 3.2)
  • A bunch of batteries included on Python instalation
  • SSH access
  • web consoles
  • web browser and editor for files
  • Dropbox integration on your /home folder
  • Out of the box, one click install and deploy for web2py, Django and Flask
  • WSGI and access to wsgi.py, so you can use any framework
  • It is a good web host
  • Built in Cron-like task scheduler
  • It has free acoount and very nice price for payed accouts
  • API, API, API - A very good API
  • Sublime text plugin (you can edit your cloud files and start/stop your web apps from Sublime Text)

go there create a free account and consider purchase a payed plan, it worth a try!

PythonAnywhere

read more