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:59 of 01/02/2014

Python Meme de ano Novo

(não achei melhor tradução para o título)

Inspirado pelos posts de Alex Clark e Daniel Greenfield.

Qual o framework, aplicação ou biblioteca mais legal que você descobriu neste ano?

  • xmltodict
    Eu escrevi um post a respeito, e esta é definitivamente a melhor maneira de ler e escrever arquivos XML. Eu ainda não sei nada a respeito de performance mas já utilizei em produção não tive nennum problema. é o requests para XML.
  • Kivy + QPython
    Eu já conhecia o Kivy mas ainda não havia utilizado em nenhum projeto, neste ano tive a oportunidade de iniciar um projeto o Kivy já se tornou minha primeira opção para projetos desktop/mobile, poderia dizer que o Kivy é o novo "Tkinter" e desejo que se torne "padrão" para desktop/mobile em Python. O Qpython é uma distribuição Python para Android e já vem com suporte a Kivy e Pygame, além disso roda também o Django e o Flask numa boa!
  • MongoDB Aggregation Framework
    Pipelines são d+!!! se eu puder, nunca mais quero usar SQL para criar relatórios financeiros.
  • MongoEngine
    Eu ja usava muito o PyMongo desde 2011, então comecei a trabalhar muito com o Django ORM (que eu não gosto), mas a sintaxe dele se tornou natural e foi bom encontrar a mesma sintaxe para o Mongo.

Qual nova técnica de programação você aprendeu neste ano?

  • Descriptors
    Eu já sabia o básico dos descriptors pois escrevi vários exemplos para o cursodepython.com.br, porém só agora em 2013 que tive a oportunidade de usar em produção no Quokka CMS. Aprendi muito sobre descriptios e sobre Python
  • SSE
    Não é uma técnica e sim uma tecnologia/padrão. Nunca tinha usado e aprendi como implementar o backend e o frontend e tambem passei por vários problemas em produção.

Qual projeto Open Source que você mais contribuiu neste ano? e o que você fez?

  • Quokka CMS
    Iniciei o projeto em Julho de 2013 e 3 meses depois ja tinha mais de 500 starts no github (graças ao hacker news) e vários contribuidores. Agora já coloquei 3 sites em produção usando Quokka e seus módulos cart, classes e fundraising. Quokka foi bastante inspirado em outro projeto Open Source que trabalhei em 2013.
  • Opps CMS
    Eu comecei o ano de 2013 com um novo emprego na YACOWS e com o novo emprego um projeto muito bacana chamado OPPS (OPen Publishing System). Nós desenvolvemos o Opps em 3 meses e criamos 3 grandes portais de alto trafégo com muitos plugins legais para liveblog, feeds, analytics etc. Eu criei vários desses apps para o Opps (Enquetes, Promoções, Goalserve api de esportes, Feeds)
  • Python Pagseguro Pag Seguro é o gateway de pagamentos mais utilizado no Brasil e eu criei uma lib para integrar Python com a API v2 do pagseguro
  • Flask GoogleMaps
    Criei uma extensão para embutir Google Maps em templates Jinja do Flask.

Quais sites, blogs ou listas de discussão que você mais leu neste ano?

Quais são as principais 3 coisas que você deseja aprender no próximo ano?

  • Uma nova linguagem. Talvez Go ou Rust (Ainda não decidi) e também desejo melhorar meus conhecimentos de Lua e quem sabe escrever algum projeto web com Lua.
  • Python 3.4 Aync Framework
  • RST (Preciso muito começar a escrever docs no sphinx)

Qual e o software, app ou biblioteca que você deseja que alguém crie no próximo ano?

  • Um micro framework para web com Lua (já conheço o kepler mas gostaria de algo mais parecido com Flask ou Bottle)
  • Um ORM para o OrientDB com suporte a Flask e Flask-Admin
  • Uma ferramenta gráfica para o MongoDB Aggregation Framework (quem sabe o Aqua Data Studio não implementa?)

Planos para 2014

  • Trabalhar menos e dormir bem
  • PythonHub
    pythonhub.com é um projeto de rede social para Pythonistas que já comecei a desenvolver em 2013, neste ano o projeto decola!
  • Mais Quokka CMS e melhorias em seus módulos
    Quokka deve se tornar um "CMS Framework" e já te issues abertas sobre isso no github, tambem precisa de melhor documentação e guias de deploy.
  • Escreverei mais no blog (sobre Python e Mongo)
  • Terminarei meu novo livro (já iniciei os rascunhos de um novo livro de desenvolvimento web com Python e Flask, é um guia para iniciantes e escrito em português!)
  • Novas turmas no cursodepython.com.br, muita coisa pronta para as novas turmas online de web2py, Flask e Mongo e o novo sistema de ensino.
  • Fechar minha empresa (não aguento mais pagar tanto imposto)
  • Ir ao MongoDB World Conference
  • Ir a Rottnest Island (Australia) conhecer um Quokka de verdade

E você?

quer participar? copie as peguntas faça uma postagem e tuite com a hashtag #2014pythonmeme.

  • Qual o framework, aplicação ou biblioteca mais legal que você descobriu neste ano?
  • Qual nova técnica de programação você aprendeu neste ano?
  • Qual projeto Open Source que você mais contribuiu neste ano? e o que você fez?
  • Quais sites, blogs ou listas de discussão que você mais leu neste ano?
  • Quais são as principais 3 coisas que você deseja aprender no próximo ano?
  • Qual e o software, app ou biblioteca que você deseja que alguém crie no próximo ano?
  • Planos para 2014

read more

Bruno Rocha at 19:58 of 01/02/2014

New Year's Python Meme

Inspired by Alex Clark and Daniel Greenfield.

What’s the coolest Python application, framework or library you discovered this year?

  • xmltodict
    I made a post about it, and it is definitely the best way to read and write XML files. I do not know about the benchmarks yet, it is working well in production, I can call it the requests for xml.
  • Kivy + QPython
    I already knew Kivy but never worked with it, now I am working on a project and Kivy is now my first choice on desktop/mobile apps. I think Kivy is the new "Tkinter". I really hope Kivy to be the "standard" Python choice for mobile and Desktop applications. QPython is a Python distribution for Android, already support kivy and pygame, also you can run Django and Flask on that.
  • MongoDB Aggregation Framework
    Pipelines are Awesome!!! If possible I never want to create financial reports with SQL again.
  • MongoEngine
    I already worked with PyMongo in 2011, then I worked a lot with Django ORM (which I don't like), but the syntax become natural and it was very nice to discover the same syntax to work with MongoDB.

What new programming technique did you learn this year?

  • Descriptors
    I already knew the basics of descriptors because of some examples I wrote for cursodepython.com.br classes, but in 2013 I had the chance to use descriptors in production with Quokka CMS and I learned a lot about this and also a lot about Python.
  • SSE
    This is not a technique, but a technology/patterns, I never used before, I learned how to implement the server side and front-end and also I ran in to real world problems with it.

Which open source project did you contribute to the most this year? What did you do?

  • Quokka CMS
    I started the project in July 2013, 3 months later it already had more than 500 stars on github (thanks to Hacker News post) and a lot of contributors. Now I already put 3 websites in production using Quokka CMS and its modules cart, classes and fundraising. Quokka CMS was heavily inspired by another projects I worked this year.
  • Opps CMS
    I started the year with a new job at YACOWS and with this new job an awesome CMS project called OPPS (OPen Publishing System), we developed Opps in less than 3 months and we created 3 web portals for high traffic and nice add ons to work with live blogging, feed crawling, google analytics etc. Also I created many apps for Opps (Pools, Promos, Goalserve sports api, Feedcrawler)
  • Python-PagSeguro
    PagSeguro is the lead payment gateway in Brazil, I created the Python lib to pagseguro api version 2.
  • Flask Google Maps
    Also I created a flask extensions to render Google Maps in Jinja templates.

Which Python blogs, websites, or mailing lists did you read the most this year?

What are the top three things you want to learn next year?

  • A new language. Go or Rust (I did not decided which one) also I want to improve my Lua skills and maybe start a web project in Lua.

  • Python 3.4 new Async framework

  • RST ( Yeah I need to start to write docs on Sphinx)

What is the top software, application or library you wish someone would write next year?

  • A micro framework for web development with Lua ( I already knew kepler, but I mean something closer to Flask or Bottle)
  • An ORM to OrientDB with Flask and Flask-admin support
  • Graphical tool to deal with MongoDB aggregation framework. (Aqua Data Studio could do it!)

Plans for 2014

  • Work less and sleep well
  • PythonHub
    pythonhub.com is a project for 2014, the Python Network already under development
  • More Quokka CMS improvements and modules
    Quokka needs to be turned in to a CMS Framework, there is already opened issues covering that need, also it needs better documentation and deployment guides.
  • Write more on blog (about Python and MongoDB)
  • Finish my new book (I am writing a new book covering Python web development with Flask, it is a guide for beginners and it is writen in portuguese)
  • New training classes on cursodepython.com.br (web2py, Flask and Mongo courses)
  • Close my company (I do not want to keep paying so high taxes)
  • go to MongoDB world conference
  • go to Rottnest Island (Au) to meet a real Quokka

What about yours?

Want to participate? Copy/paste/answer the questions below then tweet your entry with the #2014pythonmeme hash tag:

New Year's Python Meme

  • What’s the coolest Python application, framework or library you discovered this year?
  • What new programming technique did you learn this year?
  • Which open source project did you contribute to the most this year? What did you do?
  • Which Python blogs, websites, or mailing lists did you read the most this year?
  • What are the top three things you want to learn next year?
  • What is the top software, application or library you wish someone would write next year?

read more

Bruno Rocha at 16:17 of 12/07/2013

requirement

Watch changes in a ftp folder, whenever a new xml file is created, or when an existing file is modified this needs to be parsed and its contents inserted in the database.

tools

  • Python 2..7
  • watchdog

Install from pip

pip install watchdog

Watchdog is a Python API library and shell utilities to monitor file system events.

How to

First create the monitoring script, it will run daemonized and will observe any changes to the given directory. In that script 3 modules/classes will be used

  • time from Python will be used to sleep the main loop
  • watchdog.observers.Observer is the class that will watch for any change, and then dispatch the event to specified the handler.
  • watchdog.events.PatterMatchingHandler is the class that will take the event dispatched by the observer and perform some action

watchforchanges.py

import time  
from watchdog.observers import Observer  
from watchdog.events import PatternMatchingEventHandler  

PatternMatchingEventHandler inherits from FileSystemEventHandler and exposes some usefull methods:

Events are: modified, created, deleted, moved

  • onanyevent: if defined, will be executed for any event
  • on_created: Executed when a file or a directory is created
  • on_modified: Executed when a file is modified or a directory renamed
  • on_moved: Executed when a file or directory is moved
  • on_deleted: Executed when a file or directory is deleted.

Each one of those methods receives the event object as first parameter, and the event object has 3 attributes.

  • event_type
    'modified' | 'created' | 'moved' | 'deleted'
  • is_directory
    True | False
  • src_path
    path/to/observed/file

So to create a handler just inherit from one of the existing handlers, for this example PatternMatchingEventHandler will be used to match only xml files.

To simplify I will enclose the file processor in just one method, and I will implement method only for onmodified and oncreated, which means that my handler will ignore any other events.

Also defining the patterns attribute to watch only for files with xml or lxml extensions.

 class MyHandler(PatternMatchingEventHandler):
    patterns = ["*.xml", "*.lxml"]

    def process(self, event):
        """
        event.event_type 
            'modified' | 'created' | 'moved' | 'deleted'
        event.is_directory
            True | False
        event.src_path
            path/to/observed/file
        """
        # the file will be processed there
        print event.src_path, event.event_type  # print now only for degug

    def on_modified(self, event):
        self.process(event)

    def on_created(self, event):
        self.process(event)

With the above handler only creation and modification will be watched now the Obserser needs to be scheduled.

if __name__ == '__main__':
    args = sys.argv[1:]
    observer = Observer()
    observer.schedule(MyHandler(), path=args[0] if args else '.')
    observer.start()

    try:
        while True:
            time.sleep(1)
    except KeyboardInterrupt:
        observer.stop()

    observer.join()

You can set the named-argument "recursive" to True for observer.schedule. if you want to watch for files in subfolders.

That's all needed to watch for modifications on the given directory, it will take the current directory as default or the path given as first parameter.

python watch_for_changes.py /path/to/directory

let it run in a shell and open another one or the file browser to change or create new .xml files in the /path/to/directory.

echo "testing" > /tmp/test.xml 

Since the handler is printing the results, the outrput should be:

rochacbruno@~/$ python watch_for_changes.py /tmp
/tmp/test.xml created
/tmp/test.xml modified

Now to complete the script only need to implement in the process method, the necessary logic to parse and insert to database.

For example, if the xml file contains some data about current track on a web radio:

<?xml version="1.0" encoding="ISO-8859-1" standalone="yes" ?> 
 <Pulsar>
  <OnAir>
     <media_type>default</media_type> 
     <media>
        <title1>JOVEM PAN FM</title1> 
        <title2>100,9MHz</title2> 
        <title3>A maior rede de radio do Brasil</title3> 
        <title4>00:00:00</title4> 
        <media_id1>#ID_Title#</media_id1> 
        <media_id2>#ID_SubTitle#</media_id2> 
        <media_id3>#ID_Album#</media_id3> 
        <hour>2013-12-07 11:44:32</hour> 
        <length>#Duration#</length> 
        <ISRC>#Code#</ISRC> 
    <id_singer>#ID_Singer#</id_singer>
    <id_song>#ID_Song#</id_song>
    <id_album>#ID_Album#</id_album>
    <id_jpg>#Jpg#</id_jpg>
     </media>
  </OnAir>
</Pulsar>

The easiest way to parse this small xml is using xmltodict library.

pip install xmltodict

With xmltodict.parse function the above xml will be outputed as an OrderedDict

OrderedDict([(u'Pulsar',
    OrderedDict([(u'OnAir',
        OrderedDict([(u'media_type', u'default'),
        (u'media', 
            OrderedDict([(u'title1', u'JOVEM PAN FM'),
                         (u'title2', u'100,9MHz'),
                         (u'title3', u'A maior rede de radio do Brasil'),
                         (u'title4', u'00:00:00'),
                         (u'media_id1', u'#ID_Title#'),
                         (u'media_id2', u'#ID_SubTitle#'),
                         (u'media_id3', u'#ID_Album#'),
                         (u'hour', u'2013-12-07 11:44:32'),
                         (u'length', u'#Duration#'),
                         (u'ISRC', u'#Code#'),
                         (u'id_singer', u'#ID_Singer#'),
                         (u'id_song', u'#ID_Song#'),
                         (u'id_album', u'#ID_Album#'),
                         (u'id_jpg', u'#Jpg#')]))]))]))])

Now we can just access that dict to create the registry on filesystem or something else. Notice that I will use a lot of get method of dict type to avoid KeyErrors.

with open(event.src_path, 'r') as xml_source:
    xml_string = xml_source.read()
    parsed = xmltodict.parse(xml_string)
    element = parsed.get('Pulsar', {}).get('OnAir', {}).get('media')
    if not element:
        return
    print dict(element)

ant the output will be:

{u'hour': u'2013-12-07 11:44:32',
 u'title2': u'100,9MHz',
 u'id_album': u'#ID_Album#',
 u'title1': u'JOVEM PAN FM',
 u'length': u'#Duration#',
 u'title3': u'A maior rede de radio do Brasil',
 u'title4': u'00:00:00',
 u'ISRC': u'#Code#',
 u'id_song': u'#ID_Song#',
 u'media_id2': u'#ID_SubTitle#',
 u'media_id1': u'#ID_Title#',
 u'id_jpg': u'#Jpg#',
 u'media_id3': u'#ID_Album#',
 u'id_singer': u'#ID_Singer#'}

Much better than XPATH, and for this particular case when the xml_source is small there will no relevant performace issue.

Now only need to get the values and populate the database, in my case I will use Redis DataModel as storage.

also I will use magicdate module to automagically convert the date format to datetime object.

import sys
import time
import xmltodict
import magicdate
from watchdog.observers import Observer
from watchdog.events import PatternMatchingEventHandler

from .models import Media


class MyHandler(PatternMatchingEventHandler):
    patterns=["*.xml"]

    def process(self, event):
        """
        event.event_type
            'modified' | 'created' | 'moved' | 'deleted'
        event.is_directory
            True | False
        event.src_path
            path/to/observed/file
        """

        with open(event.src_path, 'r') as xml_source:
            xml_string = xml_source.read()
            parsed = xmltodict.parse(xml_string)
            element = parsed.get('Pulsar', {}).get('OnAir', {}).get('media')
            if not element:
                return

            media = Media(
                title=element.get('title1'),
                description=element.get('title3'),
                media_id=element.get('media_id1'),
                hour=magicdate(element.get('hour')),
                length=element.get('title4')
            )
            media.save()

    def on_modified(self, event):
        self.process(event)

    def on_created(self, event):
        self.process(event)


if __name__ == '__main__':
    args = sys.argv[1:]
    observer = Observer()
    observer.schedule(MyHandler(), path=args[0] if args else '.')
    observer.start()

    try:
        while True:
            time.sleep(1)
    except KeyboardInterrupt:
        observer.stop()

    observer.join()

That is my usecase, but the example can be used for any kind of requirement.

Another useful module is Workflow by Massimo Di Pierro that creates workflows based on rules defined in a config file.

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 20:25 of 11/19/2013

No dia 18 eu palestrei na semana global do empreendedorismo, la na Plug'n work. A idéia da palestra foi mostrar para os empreendedores que estão começando a desenvolver suas idéias uma maneira de desenvolver seus protótipos (ou até mesmo um MVP) utilizando Python, web2py, bootstrap e o browser.

Além de apresentar Python e ressaltar sua facilidade, assim como todo o poder do web2py para este nicho de público, eu tive a intenção de focar em uma opinião pessoal que é a minha repulsa pelo termo "Sócio técnico" e como isso soa como enganação e é claro mostrar como qualquer empreendedor que saiba usar um computador e pelo menos tenha noção de estrutura de dados (ja tenha usado uma planilha excel) é capaz de desenvolver seu próprio protótipo utilizando o web2py.

Pretendo melhorar este material e quem sabe transformar em um vídeo e tambem estou disponível para dar a mesma palestra em outros eventos, universidades etc..

Seguem os slides.

Em Janeiro de 2014 darei um treinamento Python para empreendedores na Yacows Academy, o curso é 100% prático e voltado para pessoas que não sabem nada (ou sabem pouco) de programação e desejam desenvolver seu próprio web app. O único requisito e ter um notebook e saber o básico do uso de uma planilha eletronica tipo excel, google doc. Em breve mais informações aqui e no Yacows Academy

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 12:58 of 09/05/2013

Python supera os padrões e atinge novos níveis de qualidade

Este post é uma livre tradução do texto publicado no blog oficial da PSF

Ao longo da história de mais de 20 anos do Python, a sua qualidade tem estado nas mãos dos voluntários ao redor do mundo que contribuem abertamente com a linguagem.

Graças ao Coverity, estes voluntários tem sido notificados sobre muitas questões de qualidade e segurança através do Coverity Scan, um serviço que encontra defeitos em projetos C/C++ e Java sem nenhum custo.

Como o projeto CPython possui mais de 370 mil linhas de código C, totalizando 42% da base de código, muita coisa acaba ficando de fora das ferramentas de analise que a comunidade criou especificamente para Python. Mas desde 2006 o Coverity analisa este código C e encontrou cerca de 1000 defeitos sendo que 860 os colaboradores já corrigiram.

Em uma industria onde o padrão de densidade de defeitos é uma taxa de 1 defeito por 1000 linhas de código. o CPython alcançou uma taxa de 0,005 defeitos por cada 1000 linhas de código, e isto "Introduz um novo nível de qualidade para projetos de código aberto", de acordo com a Coverity.

"Python é o cidadão modelo de boas práticas e de qualidade de código e nós aplaudimos seus colaboradores e mantenedores pelos eu compromisso com a qualidade" (Jennifer Johnson, diretor de marketing da Coverity)

A Python Software Foundation e o restante da comunidade Python se junta a Coverity para aplaudir todos que já contribuiram com seu tempo e dedicação para fazer o CPython um projeto melhor, juntamente com os inúmeros outros que contribuem para uma poderosa "paisagem" de interpretadores Python.

Referencias

read more