Flask Introduction

Introduction

Create folder and virtual environement

Python is a high-level, general-purpose programming language that is used in a wide variety of applications. It's known for its clear and readable syntax, and for its ability to work with a large number of libraries and frameworks.

Flask is a Python framework used to create web applications. It's characterized by being lightweight and flexible, making it ideal for creating simple and fast web applications. Flask allows creating web applications through the use of routes, views and templates, making it easy to learn and use.

In the specific case of this post, Flask is used as a framework to create a basic web application, and it explains step by step how to configure the virtual environment and create a simple Flask application that displays a message on the home page. It also covers other basic concepts like routing, templates and static files in Flask.

Installing Python

Download Python

Starting a Flask Project

Open a terminal, create a directory, and then create the virtual environment:

mkdir myproject
cd myproject
python3 -m venv .venv

If you see the "(venv)" on the left, it means you have successfully created a virtual environement.

If you fall in any throuble, you can review the documentation: https://docs.python.org/3/library/venv.html

If Python is not recognized, you probably need to add it to your environment variables. To do this on Windows, click the Windows key, type "env" and press enter.

Double click on "PATH" and then paste the path where you have Python. It should look like the image:

venv in this case is the virtual environment we specified before. If instead of running py -3 -m venv venv we ran py -3 -m venv fafa, now we should look for the scripts folder inside fafa. It's the name we gave it. By default it's usually left as "venv" which means "virtual environment" in English.

A virtual environment in Python is a tool that allows you to create an isolated environment that contains a specific version of Python and a series of packages and dependencies needed for a project. It's useful to be able to work on different projects that require different versions of Python or different packages without conflicts between them. The venv module is what's used to create and manage virtual environments in Python.

Install flask

pip install Flask

Coding

(I recommend downloading a code editor like Visual Studio Code, PyCharm or Atom.)

We create a file with whatever name we want inside the project folder.

touch main.py

Then we write the following code:

from flask import Flask
app = Flask(__name__)

@app.route('/')
def index():
    return "Hello Shan"

We go back to the terminal and execute:

export FLASK_APP=main.py

Main is called in my case but it can have any name.

$env:FLASK_APP = "main.py"

Run server:

flask run

Access the port that the console tells you and you should have something like this:

In the context of a backend, a route or endpoint is a specific URL that the server can handle to perform a particular action. Each endpoint is associated with a function or method in the backend that handles the corresponding HTTP request. For example, a /login route can be associated with a function that handles user login requests. Routes are a fundamental part of the backend as they enable communication between frontend and backend, and are essential for building interactive web applications.

Congrats, your first backend running locally 😀

To deactivate the virtual environment we have to type deactivate from the root folder. BE CAREFUL not to confuse it with deactivate.bat because when we activated it we ran with .bat but in this case just typing it will deactivate it. Like in the following example:

And notice how the prefix (venv) disappeared from my console.


Routes

Now we can go back to our code and add a new route:

from flask import Flask
app = Flask(__name__)

@app.route('/')
def index():
    return "Hello shan"

@app.route('/login')
def login():
    return "A login should go here"

Now we navigate to that web and...

http://127.0.0.1:5000/login

HOW NOT FOUND?

I don't know, I felt cheated the first time.

What happens is that we have to run the server again with:

flask run

But it would be really slow to keep executing the server again every time we add code. For this, Flask offers us a solution, instead of executing flask run we can execute flask run --debug

This will allow us to modify the code and when we save it, we'll refresh the page and see the changes reflected.

⚠️ It can happen that if we have an error in the code, we have to stop the server and run it again.

If it doesn't work and runs, you might have to re-export the app.

Go to the URL and you'll see your page.

Try changing the function name or the route and you'll see that it updates without needing to run everything again.

If you make a mistake in the code, for example you're writing, Flask can show you errors on the page or terminal. But as long as you reload when this happens, it will keep working fine (as long as the code is written correctly).

I clarify this mainly so you don't worry if it looks bad when you're typing.


Variables

You can pass variables to routes in the following way:

@app.route('/login/<user>')
def login(user):
    return "Hello " + user

And now if we navigate to the route /login/shan

It returns the following:

You can force variables to be of some type, for example like this:

@app.route('/dias/<int:date>')
def fecha(date):
    return 'this is an int: ' + date

Notice how I put an int right in there.

That's so it only takes int as variables.

Now, this won't work for us. Because I'm returning a return that has a string + int and that can't be combined in Python.

I recommend you try it so you understand what I mean ;)

TIME FOR MATE 🧉


METHODS

Let's see how we can send HTTP METHODS. This is the desired action on the route we want to make.

HTTP methods are verbs that indicate the action to be performed on a resource provided by a server. The most commonly used methods are:

  • GET: used to request data from a specific resource. Should not have side effects on the server.

  • POST: used to send data to create or update a resource on the server. Can have side effects on the server.

  • PUT: used to update an existing resource on the server. If the resource doesn't exist, a new one is created.

  • DELETE: used to delete a specific resource from the server.

  • PATCH: used to partially update an existing resource on the server.

We add in an array, the methods we want to execute that function:

@app.route('/user/<id>', methods=['GET', 'POST'])
def fecha(id):
    return 'It is this user ' + id

For now everything is still working great.

When we navigate to the web from Chrome, it made a GET.

POST

To test "hitting the backend" (communicating with it).

Let's execute the following command:

CURL -X PUT http://localhost:5000/register/user-name

The console should return some error. Because we told our function that it could receive POST and GET. But we didn't tell it that we could receive a PUT to that route.

In fact, in the console where the server is running you'll see strange things.

Let's modify the code to accept a POST.

Therefore now let's execute the following command to test that it works if we send POST or GET:

curl -X POST http//:localhost:5000/register/user-name

We should see something like this in the console:

If you add the GET method, it should also works for you.

I want to highlight that in a very similar way our frontend will communicate when it sends us requests to save to database or do something special.


Different routes, same method, or vice versa.

Now, something very normal in backend is to have the same route, the same path, but for different things.

For example /user with the POST method creates a user. While the same route /user with the GET method returns a user.

There are two ways to do this. One is reusing the route but changing the methods it receives. Like this:

@app.route('/user/<id>', methods=['POST'])
def saveUser(id):
    return 'Create user ' + id

@app.route('/user/<id>', methods=['GET'])
def getUser(id):
    return 'Return user ' + id

Notice how they're almost the same but I changed the method name. Same route, but one function is called getUser() and the other saveUser().

Plus inside "method" one says GET and the other POST.

Another way is to ask with an if what type of request it is. Like this:

@app.route('/user/<id>', methods=['POST', 'GET'])
def saveOrGetUser(id):
    if request.method == 'GET':
        return 'Create user ' + id
    else:
        return 'Return user ' + id

I'm more of a fan of the first option. But I use this as an excuse for you to see how I can access the request inside the method ;)

If you can't access 'request' maybe it's because you didn't import the libraries correctly. Now the first line of code should look like this:

from flask import Flask, request


Forms

To be able to see what we're going to be sending through forms, we can write the following:

To test it we're going to have to throw the following command:

@app.route('/form', methods=['POST'])
def formulario():
    print(request.form)

Notice how I specify that it's a POST method. Because by default, routes are created with the GET method. So sending a form wouldn't work for me.

Let's go to a terminal and execute:

curl -d "nombre=shanick&profesion=programador" -X POST http://localhost:5000/form

curl is the tool we're using to send requests to our backend.

-d is to specify the data we send.

Then the data comes in key-value format. Where nombre is the key. While shanick is the value.

The ampersand, &, is used to concatenate data, we could have sent all we wanted.

-X is to specify the type of method, in this case POST.

And lastly the route.

And in the terminal you'll find a giant form with everything you wanted. The form.

You can specify what data you want.

@app.route('/form', methods=['POST'])
def formulario():
    print(request.form['nombre'])
    print(request.form['profesion'])
    return 'done'

In the right terminal, from where I sent the command, you'll see that it responds with the 'done' we returned in the method. While in the left terminal, the "server", what you're seeing are the server's prints.

Redirection

If at any moment we want to redirect the user. We can do it through the functions of another path.

@app.route('/estaRuta', methods=['GET'])
def estaRuta():
    return redirect(url_for('redirecciona'))

@app.route('/redirecciona', methods=['GET'])
def here():
    return 'here'

Let's go by parts.

url_for('') is a function that returns us the function of where we want to go. Notice that I put 'redirecciona' inside the parentheses, because it's the path '/redirecciona' where we want to send it.

So it's going to take me to what the function here() does.

redirect() serves precisely to redirect to the URL we got with the url_for() method.

And that's how at any moment we can redirect a user.

If it didn't happen automatically by writing the methods, you have to add the imports manually. Line 1 should look like this:

from flask import Flask, request, url_for, redirect

HTTP Codes

We can return errors to the user. The famous 404 or 403 are good examples.

With abort() we can achieve this.

First in our first line we import abort.

from flask import Flask, request, url_for, redirect, abort

For example with the following code:

@app.route('/error', methods=['GET'])
def error():
    abort(403)

A code like the following:

@app.route('/error', methods=['GET'])
def error():
    abort(404)

returns us a 404.

Well, and so with all the codes we need.

Returning HTML

So far we've only returned very vague pages to the user, practically errors or some other message. But we can return a complete HTML page.

First we have to create a /templates folder and inside a file that we want to return, for example, myPage.html

In our first line of code we have to import this new function.

from flask import Flask, request, url_for, redirect, abort, render_template

That's how it should look so far.

The code should look something like this:

@app.route('/page', methods=['GET'])
def aca():
    return render_template('myPage.html')

The files should look like this:

It's very important that the name we write inside the render_template('') function is exactly the same as the file we created inside the templates folder, we also can't change the folder name.

The HTML file contains the following:

<H1>This is your page</H1> (very basic)

If we see it, congratulations, you returned your first HTML

Returning JSON

Something very common is wanting to return a JSON from the backend. Fortunately in Flask this is quite simple, and is done between braces following JSON format. So there's not much to it.

@app.route('/json', methods=['GET'])
def getJson():
    return {
        "userName":'shan',
        "email":'shan@gmail.com',
        "age": 23
    }

Notice how '23' is an int while the rest of the data are string this will help us in the client to use it as we need.

The page should return us something like this:

SQL Database

To use SQL in the project, we have to integrate the SQL connector for Python.

First we go to the console where our project runs, and press ctrl + C

In the console we execute the following command:

pip3 install mysql-connector-python

Depending on where you are, the command might be pip instead of pip3 depending on your operating system.

And finally we start the server again. flask run

Now we go to the beginning of our script and import mysql connector.

import mysql.connector

⚠️ It might happen that when you write `mysql` it's still not recognized. Some IDEs can help you install it, you hover over it and they usually tell you how to solve it.

We continue:

We start the database and the connector in our code and start the cursor:

sqlRepository = mysql.connector.connect(
    host="localhost",
    user="ninshan",
    password="123456",
    database="kettoDb"
)
cursor = sqlRepository.cursor()

In this case we create a method in the path /game that returns us a JSON with the key games and inside contains everything it got from the SQL query select * from Games, this query would be something like 'take everything that's inside the Games table'

@app.route('/game')
def getGames():
    cursor.execute('select * from Games')
    games = cursor.fetchall()
    return {
        "games": games
    }

And in the same path, but with the POST method, I'm inserting into the Games table, two variables. gamename and mode.

@app.route('/game', methods=['POST'])
def setGames():
    gamename = 'some name'
    mode = 'smash'
    sql = "insert into Games (gamename, mode) values (%s, %s)"
    values = (gamename, mode)
    cursor.execute(sql, values)
    sqlRepository.commit()
    return 'done'

This if we try to run it won't help us much because we don't have any database really created.

That's why I'm going to create a datasource with my IDE to be able to really interact with a DB.

Pay attention that the configuration names match those we put in our sqlRepository variable.

Last updated