Building My Site in Flask
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 .