Chocolate cake and static sites—How I shared grandma’s amazing recipe with the world using Contentful, Python and Frozen-Flask

Grandma s recipe

Nothing brings back warm childhood memories like grandma’s chocolate cake recipe. My slight alteration of her original recipe is to add a touch of Python and share it with the world through a static site.

By reading this article, you’ll learn how to create a Contentful-powered Flask app. You’ll also learn how to turn that Flask app into a static site using Frozen-Flask—and how to deploy it all to Surge.sh.

Why static sites?

Static sites are fast, lightweight and can be deployed almost everywhere for little or no money. Using a static site generator to turn your Flask app, with all its inner logic and dependencies, into a static site reduces the need for complex hosting environments.

Why Contentful?

Contentful is content infrastructure for any digital project. For our chocolate cake app this means that we’ll retrieve the recipe together with the list of ingredients from Contentful’s Content Delivery API (CDA).

Creating the chocolate cake content type

With Contentful, a content type is similar to a database table in that it defines what kind of data it can hold. We’ll create the content type so that it can hold any recipe. Because truth be told, grandma also made some fantastic pancakes, and I would like to share that recipe too one day.

So let’s start by naming our new content type Grandma’s kitchen like so:

Chocolate cake and static sites image2

We can then add different field types to this content type:

Chocolate cake and static sites image5

This recipe we’ll need the following field types:

  • Medium - that will contain the image of our beautiful creation
  • Short text - that will contain the recipe name
  • Long text - that will contain our list of ingredients
  • Long text - that will contain instructions
  • Boolean - to make sure that delicious == true

With the added field types our content type will look like so:

Chocolate cake and static sites image6

Adding the ingredients

Now that we have our content type set up, we’ll go ahead and add our chocolate cake recipe.

Chocolate cake and static sites image1

Setting up the Flask app

With the recipe in place, it’s time put together our Flask app. When a user visits /chocolatecake/, this minimalist app will render the recipe through a the recipe.html template as seen below:

1
2
3
4
5
6
from flask import Flask, render_template
app = Flask(__name__)

@app.route("/chocolatecake/")
def cake():
    return render_template("recipe.html")

But we of course need a way to get our chocolate cake recipe data from the Contentful CDN and pass that data to the render_template function as a variable…..

Getting the data from Contentful into Flask

To pull data from Contentful into Flask, you will need an access token to authorize your API calls. Note that the access token is personal so you need to generate your own to get things to work.

While we can interact with Contentful’s endpoints using bare HTTP calls, the Python SDK makes dealing with response objects easier. Run pip install contentful to install it. We also need to install support for rendering the Markdown-formatted parts of the response: pip install Flask-Markdown

Now we’ll create a method in our Flask app that does the following:

  • Connects to Contentful using our unique access token
  • Grabs the content of our chocolate cake recipe
  • Handles the JSON response from Contentful
  • Sends the data to be rendered to our template
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
def getRecipe():
    SPACE_ID = '1476xanqlrah'
    ENTRY_ID = '4kgJZqf18AYgYiyYkgaMy0'
    ACEESS_TOKEN = '457ba5e5af020499b5b8e7c22ae5da0ffeaf314028e28a6b0bdba4f28e35222c'

    client = Client(SPACE_ID, ACEESS_TOKEN)
    entry = client.entry(ENTRY_ID)

    imageURL = entry.image.url()
    recipeName = entry.recipe_name
    listOfIngredients = entry.list_of_ingredients
    instructions = entry.instructions
    isDelicious = entry.is_delicious

    return {
    'imageURL': 'https:{0}'.format(imageURL),
    'recipeName': recipeName,
    'listOfIngredients': listOfIngredients,
    'instructions': instructions,
    'isDelicious': isDelicious
    }

And to send our dictionary data structure of recipe data for rendering by the template we modify the /chocolatecake/ route to look like so:

1
2
3
4
5
@app.route("/chocolatecake/")
def cake():
    recipe = getRecipe()
    return render_template("recipe.html", recipe=recipe)

The template that we’ll render, recipe.html, has the following content:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
<!DOCTYPE html>
<html>

<head>
  <meta charset="utf-8">
  <title>Grandma's Chocolate Cake</title>
  <link rel="stylesheet" href="{{ url_for('static', filename='style.css') }}">
</head>

<body>
  <div>
    <h2>{{ recipe.get("recipeName")}}</h2>
  </div>

  <div>
    <img src="{{ recipe.get(" imageURL ")}}">
  </div>

  <div>
    Is delicious: {{ recipe.get("isDelicious")}}
  </div>

  <div>
    {{ recipe.get("listOfIngredients") | markdown }}
  </div>

  <div>
    {{ recipe.get("instructions") | markdown }}
  </div>
</body>
</html>

We now have a working Flask app running locally. This app grabs its content from Contentful’s CDN using an API call, and then renders a page like this:

Chocolate cake and static sites image3

We could stop here and just deploy our creation to Heroku or any other platform that can run Flask apps. But we want more—and we want it to be static.

Adding Frozen-Flask

To turn our Contentful-powered Flask app into a static site, we’ll be using Frozen-Flask. Install it using pip: pip install Frozen-Flask

Creating the static site

We’ll create file called freeze.py with the following content:

1
2
3
4
5
6
7
8
from flask_frozen import Freezer
from app import app

freezer = Freezer(app)

if __name__ == '__main__':
    freezer.freeze()

After running freeze.py, we have a build directory containing:

1
2
3
├── chocolatecake
└── static
    └── style.css

This is exactly what we want—HTML and styling. So let’s ship it!

Deploying the static site to surge.sh

Surge.sh is a single-command web publishing platform. It allows you to publish HTML, CSS, and JavaScript for free—without leaving the command line. In other words: it’s a great platform for our static site.

Run npm install --global surge to install the Surge and to create your free account.

All we need to deploy our static site is to run the surge command from the build directory:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
    Surge - surge.sh

              email: robert.svensson@contentful.com
              token: *****************
       project path: /Users/robertsvensson/Code/cake/build/
               size: 2 files, 2.9 KB
             domain: loud-jeans.surge.sh
             upload: [====================] 100%, eta: 0.0s
   propagate on CDN: [====================] 100% 
               plan: Free
              users: robert.svensson@contentful.com
         IP Address: 48.68.110.122

    Success! Project is published and running at loud-jeans.surge.sh

When you deploy a site using Surge.sh it generates the URL based on two random words — this time we got the words loud and jeans. So now all that’s left to do is to browse to http://loud-jeans.surge.sh/chocolatecake/ and make sure that static site version of grandma’s chocolate cake recipe deployed successfully.

6 loud jeans static site

It sure did 🚢

Summary

All it takes to create a static site from your Contentful-powered Flask app is Frozen-Flask. You get the best of both worlds by having both writers and editors working on the app’s content in Contentful, and then generating a static site that can be deployed almost anywhere.

This workflow will save you both time and money: Your creative writers get access to a feature-rich editing platform within Contentful, and you get the chance to deploy a fast and lightweight static site for little or no money.

Blog posts in your inbox

Subscribe to receive most important updates. We send emails once a month.