Archive
[flask] Jinja2: don’t print empty lines
Problem
When using Flask (or Django), I don’t care much about the generated HTMLs. They may be ugly, who cares. However, there is one thing that bothers me. When I write this for instance in a template:
<div>
{% if True %}
yay
{% endif %}
</div>
the generated output looks like this:
<div>
yay
</div>
See? Jinja2 litters the output with empty lines. How to get rid of them?
Solution
The official documentation talks about this here. It says you need to enable both trim_blocks and lstrip_blocks. In Flask, you can do that like this:
... app = Flask(__name__) app.jinja_env.trim_blocks = True app.jinja_env.lstrip_blocks = True ...
Done.
[flask] generate a secret key
Problem
To implement CSRF protection, Flask-WTF needs the application to configure an encryption key. This key is used to generate encrypted tokens that are used to verify the authenticity of requests with form data.
It looks like this:
app = Flask(__name__) app.config['SECRET_KEY'] = '<the super secret key comes here>'
What secret key to use? How to generate this secret key?
Solution #1
In the official Quickstart the following method is suggested:
>>> import os
>>> os.urandom(24)
'\xfd{H\xe5<\x95\xf9\xe3\x96.5\xd1\x01O<!\xd5\xa2\xa0\x9fR"\xa1\xa8'
Just take that thing and copy/paste it into your code and you’re done.
Solution #2
In Django, when you create a new project, you get a settings file that contains a freshly generated 50 characters long secret key. Why not reuse this part from Django? The relevant section was easy to locate in the source code of Django:
import random
random = random.SystemRandom()
def get_random_string(length=12,
allowed_chars='abcdefghijklmnopqrstuvwxyz'
'ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789'):
"""
Returns a securely generated random string.
The default length of 12 with the a-z, A-Z, 0-9 character set returns
a 71-bit value. log_2((26+26+10)^12) =~ 71 bits.
Taken from the django.utils.crypto module.
"""
return ''.join(random.choice(allowed_chars) for i in range(length))
def get_secret_key():
"""
Create a random secret key.
Taken from the Django project.
"""
chars = 'abcdefghijklmnopqrstuvwxyz0123456789!@#$%^&*(-_=+)'
return get_random_string(50, chars)
Its usage is very simple: just call the “get_secret_key()” function and copy/paste the output into your code.
Flask: cannot fetch a URL on localhost
Problem
I had a simple Flask application that included a web service, i.e. calling an address returns some value (a JSON result for instance). I wanted to reuse this service inside the app. by simply calling it (via the HTTP protocol) and getting the return value. However, this call never finished. The browser was loading and I got no result.
What happened?
I posted the problem here and it turned out that “the development server is single threaded, so when you call a url served by that application from within the application itself, you create a deadlock situation.” Hmm…
My first idea was to replace the dev. server with a more serious one. With gunicorn I could make it work:
gunicorn -w 4 -b 127.0.0.1:5000 hello:app
However, I deploy the app. on Heroku, where you have just 1 worker for free, so it behaves just like the dev. server!
Solution
I had to rewrite the code to eliminate this extra call. (Or, I could have kept this call if I had had at least 2 worker threads.)
Example
Here is a simplified code that demonstrates the problem:
#!/usr/bin/env python
# hello.py
from flask import Flask
from flask import url_for
import requests
app = Flask(__name__)
@app.route('/')
def hello_world():
return "Hello, World!"
@app.route('/get')
def get():
url = url_for("hello_world", _external=True) # full URL
print '!!!', url # debug info
r = requests.get(url) # it hangs at this point
return "from get: " + r.text
if __name__ == "__main__":
app.run(debug=True)
Heroku: development and production settings
Problem
You have a project that you develop on your local machine and you deploy it on Heroku for instance. The two environments require different settings. For example, you test your app. with SQLite but in production you use PostgreSQL. How can the application configure itself to its environment?
Solution
I show you how to do it with Flask.
In your project folder:
$ heroku config:set HEROKU=1
It will create an environment variable at Heroku. These environment variables are persistent – they will remain in place across deploys and app restarts – so unless you need to change values, you only need to set them once.
Then create a config.py file in your project folder:
import os
class Config(object):
DEBUG = False
TESTING = False
DATABASE_URI = 'sqlite://:memory:'
class ProductionConfig(Config):
"""
Heroku
"""
REDIS_URI = os.environ.get('REDISTOGO_URL')
class DevelopmentConfig(Config):
"""
localhost
"""
DEBUG = True
REDIS_URI = 'redis://localhost:6379'
class TestingConfig(Config):
TESTING = True
Of course, you will have to customize it with your own settings.
Then, in your main file:
...
app = Flask(__name__)
if 'HEROKU' in os.environ:
# production on Heroku
app.config.from_object('config.ProductionConfig')
else:
# development on localhost
app.config.from_object('config.DevelopmentConfig')
...
Now, if you want to access the configuration from different files of the project, use this:
from flask import current_app as app ... app.config['MY_SETTINGS']
Redis
Let’s see how to use Redis for instance. Apply the same idea with other databases too. Opening and closing can go in the before_request and teardown_request functions:
from flask import g
import redis
@app.before_request
def before_request():
g.redis = redis.from_url(app.config['REDIS_URI'])
@app.teardown_request
def teardown_request(exception):
pass # g.redis doesn't need to be closed explicitly
If you need to access redis from other files, just import g and use g.redis .
Links
