This is a how-to for building simple sites in Flask. If you want a quick personal blog, I’d recommend deploying a pre-built WordPress instance on Google Cloud or AWS. In my case, I wanted to get a little more experience with Flask and general web development, so I built a basic blog in Flask. This could easily serve as an example for deploying APIs on Google App Engine.
Complete example on GitHub.
Getting Started
First, you’ll need flask.
pip install flask
A boilerplate flask app looks like this. Serve index.html
on /test
.
from flask import Flask, send_from_directory
app = Flask(__name__)
@app.route("/test")
def index():
return send_from_directory('static', 'index.html')
if __name__ == '__main__':
app.run(host='127.0.0.1', port=8080, debug=True)
Below are are additional examples for URL routing. send_static()
serves any and all files from the static directory using the URL path as a variable. send_post()
only serves URLs from myList
.
@app.route('/static/<path:path>')
def send_static(path):
return send_from_directory('static', path)
myList = ['post1', 'post2', 'post3']
@app.route('/<any({}):path>''.format(str(myList)[1:-1]))
def send_post(path):
return send_from_directory('posts', path+'.html')
Flask Templates
send_from_directory
is useful for serving static files, but render_template
is better for dynamicly generating pages. Below I pass a variable, myList
, to the template index.html
. By default, flask looks for templates under static/
. Templates have access to python variables and can perform basic functions using the Jinja template engine. Jinja is included with Flask - check out this article for more about template syntax.`
@app.route("/test")
def index():
myList = ['post1', 'post2', 'post3']
return render_template('index.html', myList=myList)
Our template uses special tags {{ variable }}
and {% function %}
to interact with our posts
variable. We’ve also added {% block content %}
, which contains the body of our html page.
<!--- static/index.html --->
{% block content %}
<ul>
{% for p in posts %}
<li>
{{ p }}
</li>
{% endfor %}
</ul>
{% endblock %}
<!--- Output --->
∙ post1
∙ post2
∙ post3
Organizing Pages
I created a manifest file, paths.yaml
, as a list of all the URLs I want to serve. Each path includes a pointer to the html page containing the post/page content. Any other post or URL specific info could be stored here as well, such as post image, author, date, etc.
# paths.yaml
posts:
my-first-post:
name: My very first post.
html: posts/my-first-post.html
my-second-post:
name: AWS Temporary Access Tokens
html: posts/my-second-post.html
Flask loads the list of paths as routes and passes the post variable to the template as a variable, depending on the path.
posts = yaml.safe_load(open("paths.yaml","r"))['posts']
@app.route("/".format(str(list(posts.keys()))[1:-1]))
def send_post(path):
return render_template('post.html', post=posts[path])
Our template, post.html
, displays the post’s name and displays the post’s html.
{% block content %}
{{ post['name'] }}
{% include post['html'] %}
{% endblock %}
Putting it all Together
Directory structure:
main.py
paths.yaml
templates/
index.html
post.html
posts/
my-first-post.html
my-second-post.html
templates/posts/my-first-post.html
<p>This is my first post.</p>
templates/index.html
{% block content %}
<ul>
{% for p in posts %}
<li>
<a href='{{p}}'>{{ p }}</a>
</li>
{% endfor %}
</ul>
{% endblock %}
paths.yaml
posts:
my-first-post:
name: My very first post.
html: posts/my-first-post.html
my-second-post:
name: AWS Temporary Access Tokens
html: posts/my-second-post.html
main.py
#!/usr/bin/env python3
from flask import Flask, render_template
import yaml
app = Flask(__name__)
posts = yaml.safe_load(open('paths.yaml','r'))['posts']
@app.route('/<any({}):path>'.format(str(list(posts.keys()))[1:-1]))
def send_post(path):
return render_template('post.html', post=posts[path])
@app.route('/')
def index():
return render_template('index.html', posts=posts)
if __name__ == '__main__':
app.run(host='127.0.0.1', port=8080, debug=True)
Results:
<!--- curl http://127.0.0.1:8080 --->
<ul>
<li>
<a href='my-first-post'>my-first-post</a>
</li>
<li>
<a href='my-second-post'>my-second-post</a>
</li>
</ul>
<!--- curl http://127.0.0.1:8080/my-first-post --->
My very first post.
<p>This is my first post.</p>
Deploy on GAE
In order to deploy your app on GAE, you’ll just need to add an app.yml and requirements.txt before deploying.
app.yml
service: default
runtime: python37
requirements.txt
flask==1.0.2
pyyaml==5.1.2
gunicorn
GAE Deploy command
gcloud app deploy .