DEV Community

Cover image for Create and Deploy your first Flask app
Kaushal Sharma
Kaushal Sharma

Posted on

Create and Deploy your first Flask app

So, you’ve been banging your head for some time now trying to understand -

what is this Flask framework and how do I use it

I know, I’ve been there too. But don’t worry, you don’t have to go through all the pain of searching articles for days to understand it’s functionalities.

Here, we’ll make a simple weather application using Flask to understand it’s functionalities and also cover some core concepts like -

  1. Creating and maintaining virtual environments using pipenv
  2. How does routes work in Flask
  3. How to render HTML Templates in Flask
  4. Interacting with public APIs
  5. Passing data from Python to HTML
  6. How to get and process user input
  7. How to serve static files(CSS, JS, images etc) in Flask
  8. How to work with environment variables in python
  9. Deploying your ****Flask app on Heroku

Right, the list seems quite a bit for a simple app but trust me, you’ll zoom past it in no time!

Here’s what we’re gonna build - Live app

And let’s get right into it!

Creating and maintaining virtual environments using pipenv

First, we’ll create a new folder for our project, I’ll name it “SimpleFlaskWeatherApp”, and then initialise a virtual environment.

For creating a virtual environment i’ll be using pipenv rather than virtualenv or pyenv. pipenv was first released in 2018 and it aims to simply the workflow by bringing the packaging concepts from bundlers, npm, yarn etc to python!

It automatically creates and manages the virtualenv for your projects by creating a Pipfile and Pipfile.lock without you having to do anything except installing the packages you need!

First install pipenv by entering the command in your terminal-

pip install pipenv
Enter fullscreen mode Exit fullscreen mode

if you have multiple versions of python then type the command,

python3.x -m pip install pipenv
Enter fullscreen mode Exit fullscreen mode

where x would be your choice of python version installed on your system

Now, to create a virtual env, go into your project folder and type -

pipenv shell
Enter fullscreen mode Exit fullscreen mode

this should create a virtualenv for your project with the name of your project folder followed by some random characters.

In my case, you can see it on the right of the image, it’s “SimpleFlaskWeatherApp-0rR_1i25”

Image description

If you list the contents of your directory, you would see that pipenv has created a Pipfile which it would use to manage your entire project dependency. It looks something like this -

Image description

If you now want to install any python package, simple type the following command

pipenv install flask
Enter fullscreen mode Exit fullscreen mode

This would install and make the package available to your project and also add it to your Pipfile automatically!

If you later want to pass this environment to someone else then you don’t have to create a requirements.txt, just give them your Pipfile.lock file. Then all they have to do is run the command pipenv install and pipenv will automatically create a virtualenv and install all the packages that are required and re-create the exact same environment.

How cool is that!

After installing flask, our Pipfile looks something like this -

Image description

If you want to exit your virtual environment, simply type exit and press enter. And to activate it again, type pipenv shell

Since we’ve covered pipenv, let’s move on to creating our project!

For context, this is what our project directory looks like -

Image description

How does routes work in Flask

Go ahead and create a file called app.py, we’ll write a starter code for a basic flask application like below -

from flask import Flask

app = Flask(__name__)

@app.route('/')
def index():
    return "<h1>This is working!</h1>"

if __name__ == '__main__':
    app.run()
Enter fullscreen mode Exit fullscreen mode

and run the python file

python app.py
Enter fullscreen mode Exit fullscreen mode

you should get the following output -

Image description

In a flask application, the function following the app.route is the logic that should be run if a user open the route specified in the @app.route decorator

Since we wrote @app.route('/') , where “/” means the index route, if the user opens localhost:5000, the index function logic would be executed and whatever the function returns would be returned to the user.

How to render HTML Templates in Flask

We’ve returned the html as a string for now just to test whether our application works or not, but this is not practical if you want to render more complex pages with data that will be returned by us later in this article.

So, instead of writing the html as a string we will create HTML files and return them instead.

Go ahead and create a templates folder in your project directory and inside this templates directory create an html file called index.html

Our directory structure looks something like this -

Image description

Inside the index.html, write a simple html code as follows -

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Flask Weather App</title>
</head>

<body>
    <h1>This template is working</h1>
</body>

</html>
Enter fullscreen mode Exit fullscreen mode

Now, in our app.py, along with Flask module also import render_template module which will allow us to serve html files to the users.

And in the index function, simply type return render_template('index.html')

So our code looks like this now -

from flask import Flask, render_template # new

app = Flask(__name__)

@app.route('/')
def index():
    return render_template('index.html') # new

if __name__ == '__main__':
    app.run()
Enter fullscreen mode Exit fullscreen mode

Now, if you run your application, you should see the output as below -

Image description

The render_template function looks for the html template specified inside the templates folder that we created earlier.

Interacting with public APIs

Now that we have our flask application set up to render html templates, let’s start working with public APIs.

We’ll be using the Current Weather Data of openweathermap.org to get weather data for free

Sign up on the website and subscribe to the Current Weather Data API.

They should provide you with an API key to access that API. You can find it here. We’ll be using this API Key to make API calls to get the current weather data!

First, Let’s do a simple call to the API and make sure that the API is returning data as expected.

We’ll call the API using the requests library in python as follows -

API = 'http://api.openweathermap.org/data/2.5/weather?q={city}&units=imperial&appid={api_key}'
city = 'Mumbai'
api_key = "<your_openweather_api_key>"
try:
    weather = requests.get(API.format(
        city=city, api_key=api_key)).json()
except Exception as e:
    print(f"Error in fetching data")
Enter fullscreen mode Exit fullscreen mode

It is always a good idea to use try except blocks wherever you are not sure about the output

Now this piece of code will make a call to the API using the city and api_key variables and store the result in weather variable

but before we can display this result to the user, we must first check that infact, we have got back a correct result!

In HTTP status codes, 200 means that the API call was success and you have received some data and 404 means there was some error when you sent the API request.

We’ll be checking the status code of our API call by checking the key value cod that would be present in the weather variable after the API call was made. if the value of cod is 200, that means we have correct data, if it is 404 or if we did not receive an output from the API, then it means we made a mistake while calling the API.

The code for that would be like this -

if weather["cod"] == 200:
                # We got correct result
        return render_template('index.html')
    else:
            # We got incorrect/no result
        return render_template('error.html')
Enter fullscreen mode Exit fullscreen mode

Create an error.html file alongside your index.html file in the templates folder, and just write a simple code to let you know that some error has occured

<h1>Some error ocurred while making the API call</h1>
Enter fullscreen mode Exit fullscreen mode

Ideally, you should have proper error handling for all the status codes like 4xx and 5xx , but for the context of this article is should suffice.

Our code for app.py looks something like this now -

import requests
from flask import Flask, render_template

app = Flask(__name__)

@app.route('/')
def index():
    URL = 'http://api.openweathermap.org/data/2.5/weather?q={city}&units=imperial&appid={api_key}'
    city = 'Mumbai'
    api_key = "<your_openweather_api_key>"
    try:
        weather = requests.get(URL.format(
            city=city, api_key=api_key)).json()
    except Exception as e:
        print(f"Error in fetching data")
    if weather["cod"] == 200:
        return render_template('index.html')
    else:
        return render_template('error.html')

if __name__ == '__main__':
    app.run()
Enter fullscreen mode Exit fullscreen mode

If you restart the app and open localhost:5000, you would still get the “This template is working” message which we wrote in the html.

Passing data from Python to HTML

You must be wondering

Why is the data that we received from the API call not showing in our template?

That’s because we have to pass that data, or infact any data that we want to show to the user, to the template in the render_template function

Here’s how we would do it -

return render_template('index.html', weather=weather)
Enter fullscreen mode Exit fullscreen mode

And in your index.html file, simply write the variable name enclosed within two curly braces like below -

...
<body>
    <h1>This template is working</h1>
    <div>{{ weather }}</div>
</body>
...
Enter fullscreen mode Exit fullscreen mode

Now if you restart your app and refresh the page, you should be able to see the data that is returned by the API we called!

Image description

Sweet! now we can pass data from our python code and show it to the user!

Python frameworks like Flask or Django use a templating language for html called Jinja2 which enables us to pass data from our python code and display it to the user and also get data from the html given by user and use it in our python logic, which is how we were able to pass the data from python to html.

But we do not want to show the user all of the data. how do we select only certain parts it?

Since it’s a dictionary, we can access it the way we acess a dictionary, but there is a twist to it.

we cannot access the dictionary like my_dict["key"] . To do it in html, or rather jinja, we would do my_dict.key, we have to use the dot notation!

Let’s filter out the raw weather data and keep only the useful bits! Our index.html after filtering would look like this -

...
<body>
    <h1>This template is working</h1>
    <div>
        <div>{{ weather.timeOfDay }}</div>
        <div>{{ weather.weather.0.main }}</div>
    </div>
    <div>
        <p>{{ weather.name }} <span>{{ weather.sys.country }}</span></p>
    </div>
    <div>
        <p>{{ weather.main.temp }}</p>
        <div>
            <div>
                <p>{{ weather.main.humidity }}</p>
            </div>
            <div>
                <p>{{ weather.wind.speed }}</p>
            </div>
        </div>
    </div>
</body>
...
Enter fullscreen mode Exit fullscreen mode

We can also use control flow like if else or for loop in our html file! we’ll wrap all the divs containing reference to weather variable after our h1 tag under an if statement so that we only show the result from the API call if we have some data in the weather variable, else we do not show anything

<body>
    <h1>This template is working</h1>
    {% if weather %}  <!-- new -->
    <div>
        <div>{{ weather.timeOfDay }}</div>
        . . .
        </div>
    </div>
    {% endif %}  <!-- new -->
</body>
Enter fullscreen mode Exit fullscreen mode

Now if you restart your app and refresh, you should see something like this -

Image description

This is all the data that we have to show the user!

How to get and process user input

Let’s now add a search box that will allow the user to search for places they like and get details of those places. Add a form and inside that form add a input search box to get input of city and a button to submit the query.

...
<h1>This template is working</h1>
    <div>
        <form method="post" action="{{ url_for('index') }}">
            <input type="search" name="city">
            <button type="submit">Submit</button>
        </form>
    </div>
    {% if weather %}
    ...
Enter fullscreen mode Exit fullscreen mode

The form in html needs the action attribute which would typically be a url the html will redirect to once the form is submitted. Here, we provide the url for our index page itself using the url_for function inside double curly braces. Note inside the url_for we give the function name and not the route, that’s because in flask the route and function name should be identical!

This is also one of Jinja2’s features!

Since now are are submitting a post request from our page, we need to handle that in our function as well.

Flask let’s us identify whether a request is GET request or a POST request simply by doing request.method inside the function.

We will now update the index function in our app.py by checking the type of request the function is receiving.

If the request is a GET request then just render the index.html form without any processing or API calls since it’s the first time user is loading the page.

If the request is a POST request, then we would read the city name that was submitted by the user and make the API call to get the weather data for that city and return the data to our index.html so that we can show the data to the user

Let’s update the index function to handle different types of requests,

from crypt import methods
import requests
from flask import Flask, render_template, request # new

app = Flask(__name__)

@app.route('/', methods=['GET', 'POST']) # new
def index():
    if request.method == 'POST': # new
        URL = 'http://api.openweathermap.org/data/2.5/weather?q={city}&units=imperial&appid={api_key}'
                city = request.form.get('city') # new
        api_key = '<your_openweather_api_key>'
        try:
            weather = requests.get(URL.format(
                city=city, api_key=api_key)).json()
        except Exception as e:
            print(f"Error in fetching data")
        if weather["cod"] == 200:
            return render_template('index.html', weather=weather)
        else:
            return render_template('error.html')
    else:
        return render_template('index.html')

if __name__ == '__main__':
    app.run()
Enter fullscreen mode Exit fullscreen mode

Now, if you restart the app and refresh the page, you’ll get an empty page because when the page is first loaded the form is not submitted hence it is a GET request.

If you now enter a city name and press submit, you should get weather data for that city!

Image description

But this page looks a bit dull to be honest, let’s add some CSS styling and make it come alive!

How to serve static files(CSS, JS, images etc) in Flask

First, create a static folder in your project root directory alongside the templates folder, and add a style.css to it

Image description

Add some basic code in your style.css

@import url("https://fonts.googleapis.com/css2?family=Source+Sans+Pro:ital,wght@0,300;0,400;0,600;1,400&display=swap");

*,
*::before,
*::after {
  -webkit-box-sizing: border-box;
          box-sizing: border-box;
}

body {
  height: 100vh;
  width: 100vw;
  font-family: "Source Sans Pro", sans-serif;
  margin: 0;
    background: linear-gradient(blueviolet, skyblue);
}
Enter fullscreen mode Exit fullscreen mode

And to load this CSS in your html file, add a link tag to your html

...
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <link rel="stylesheet" href="{{ url_for('static', filename='style.css') }}"> <!-- new -->
    <title>Flask Weather App</title>
</head>
...
Enter fullscreen mode Exit fullscreen mode

To load the CSS input html, we again use url_for but this time we give it two arguments.

The first is the name of our static folder which is static and second we give it a filename which in our case is style.css

You should see something like this,

Image description

And now you can customise your page however you like!

Since styling a page is very subjective, i’ll leave it upto you now. However, if you want to style it like how it is in the video at the start, i’ll link my github repo at the end of this article!

How to work with environment variables in python

Remember how we are using an API key to make the API calls. Well that API key is specific to you and should always be hidden from others. You should also never upload this key anywhere, even on github.

But then you might ask,

How can we use it in out project, without having it in our project ?!

Well, we can make use of something called environment variables. We can add some variables to the environment that is running our code, and access those variables in our code whenever we want!

To accomplish this, first create a .env file in our project root directory

Image description

And inside this .env file, assign your API key to a variable like so

Image description

Make sure you don’t add any quotes, just paste your key right after the equals to sign

Now to access this API key in our project, we need to install a library called python-dotenv

pipenv install python-dotenv
Enter fullscreen mode Exit fullscreen mode

And we now import and initialize the packages we just installed in our project

import os #new
from crypt import methods
import requests
from flask import Flask, render_template, request
from dotenv import load_dotenv, find_dotenv # new

load_dotenv(find_dotenv()) # new

app = Flask(__name__)

...
Enter fullscreen mode Exit fullscreen mode

The find_dotenv function will find the .env file located in our project and the load_dotenv function will load it into our project so we can use it

Now, to reference the api key variable in our .env file in your project we will make use of the os library that we imported,

...
if request.method == 'POST':
        URL = 'http://api.openweathermap.org/data/2.5/weather?q={city}&units=imperial&appid={api_key}'
        city = request.form.get('city')
        api_key = os.getenv("OPENW_API_KEY") # new
        try:
            weather = requests.get(URL.format(
                city=city, api_key=api_key)).json()
        except Exception as e:
            print(f"Error in fetching data")
...
Enter fullscreen mode Exit fullscreen mode

Restart your app and you would see your application is working just like before!

You can add any number of environment variables in the .env file and use them anywhere in your project!

By using environment variables, we can secure information that is private to us so that no other person can misuse it

Deploying your Flask app on Heroku

This project works fine on my laptop but -

what if i want to show it to the people online? How can I deploy my project?

That’s where heroku comes in

Heroku is a cloud platform that supports deployment of projects created in many different languages, even python!

So go ahead and create an account. After you login, you would see the screen something like this

Image description

To deploy our flask weather application, we’ll have to create a new app on heroku. So click on new

Image description

and select Create new app

after that, type in your App name and click Create app

Image description

After that scroll down to Deploying using Heroku Git section

Image description

and simply follow these steps,

Image description

Let’s deploy our app!

First log in to heroku in your terminal, When you type heroku login it will open a browser to log you in.

After you’ve logged in, make sure you are in your project root directory, type git init to initialise a git repository

Image description

Now add all the changes and commit to the main branch,

git add .
git commit -m 'pushing to heroku'
Enter fullscreen mode Exit fullscreen mode

Image description

Now, set the remote as shown on the heroku website, and push your changes

heroku git:remote -a <name_of_you_app>
git push heroku main
Enter fullscreen mode Exit fullscreen mode

Image description

Note: since we are using environment variables, any deployment platform does not support .env files

You’ll have to manually add your environment variables on the website in your app settings

In heroku, go over to the settings tab, you should be able to see Reveal Config Vars button, click on it.

Image description

It will now allow you to add your environment variables just as you did in your .env file!

Image description

Now if we open our app typing in the terminal,

heroku open
Enter fullscreen mode Exit fullscreen mode

We see an application error!

This is because we have only pushed our application to the platform, we haven’t run it yet!

To run you python or any application on platform like these, you need to have a Procfile. **Inside a **Procfile you add all the process that you want the platform to execute.

Let’s add a Procfile to our root project folder

Image description

Note that the Procfile does not have any extension, this is super important!

And inside the procfile add this line,

Image description

Let’s also install gunicorn in our virtual environment by typing the following command in the terminal,

pipenv install gunicorn
Enter fullscreen mode Exit fullscreen mode

Now follow the process to push the changes to heroku again,

git add .
git commit -m 'adding procfile'
git push heroku main
Enter fullscreen mode Exit fullscreen mode

And now if you open your application,

Image description

TA-DA! our application is deployed and running online! 🥳

Now you can share what you’ve built over the course of this article with other people!

I know it was quite a long read but hope you got to learn something new, if not new then maybe a better way of doing things.

Likes and Shares would be deeply appreciated!

Please do not hesitate to message me, you can text/follow me on LinkedIn or Instagram or Twitter

Make sure you follow to not miss any future articles! 😉

And as promised, my github repository

Discussion (2)

Collapse
grahammorby profile image
Graham Morby

Great Stuff! Really cool stuff

Collapse
skazi019 profile image
Kaushal Sharma Author

Thanks Graham!