Building My Site in Flask

Published by wtfender on

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.

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/')
def send_static(path):
    return send_from_directory('static', path)
    
myList = ['post1', 'post2', 'post3']
@app.route('/''.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("/<any({}):path>".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('/'.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
deploy
gcloud app deploy .

More examples

The source for old.wtfender.com can be viewed on GitHub.

Categories: Uncategorized

0 Comments

Leave a Reply