Introduction
Flask is a versatile and lightweight web framework for Python, well-suited for developing web applications and APIs with simplicity and flexibility. Flask plays a crucial role in deploying and serving machine learning models as web services. It allows data scientists and developers to build scalable and interactive applications around their machine learning models.
If you haven't already, I highly recommend you, checking out the first blog in my MLOps series on Git and GitHub essentials here.
Exploring Official Flask Documentation
Before we start coding, it's beneficial to visit the official Flask documentation to understand its core concepts and features, ensuring a solid foundation for our project.
Setting Up the Environment
Creating a Conda Environment
To manage dependencies and isolate our project, we'll create a virtual environment using Conda.
conda create -p venv python==<if_you_want_a_specific_version_like_3.9>
Activate the environment:
conda activate venv
Managing Dependencies with requirements.txt
Create a requirements.txt
file to list project dependencies:
echo Flask > requirements.txt
Install dependencies:
pip install -r requirements.txt
Creating a Simple Flask Application
Let's build a basic Flask application to understand its fundamental concepts.
Create a file and name it as 'app.py'
app.py
from flask import Flask
# Create an instance of Flask
app = Flask(__name__)
# Define a route and its handler
@app.route('/')
def hello():
return "Hello, World!"
if __name__ == "__main__":
app.run(debug=True)
Flask Instance:
When we initialize a Flask application instance using app = Flask(__name__)
, we are creating the core of our web application. Here’s what happens in detail:
from flask import Flask
# Create an instance of Flask
app = Flask(__name__)
Explanation:
Flask(__name__)
creates an instance of the Flask class. The__name__
variable is a special Python variable that represents the name of the current module. When using a single module (as in most Flask applications),__name__
is typically set to"__main__"
.This instance
app
will be used to configure and run the Flask application.
Route Definition:
Flask uses decorators to bind URL paths to Python functions that handle them. Here’s how we define a route:
@app.route('/')
def hello():
return "Hello, World!"
Explanation:
@app.route('/')
is a decorator provided by Flask. It binds the URL path'/'
to thehello()
function.When a user visits the root URL of our web application (
'/'
), Flask calls thehello()
function and returns"Hello, World!"
as the response.Decorators in Python are functions that modify the behavior of other functions or methods. In this case,
@app.route('/')
tells Flask that thehello()
function should be executed when someone accesses the root URL.
if __name__ == "__main__":
This conditional statement ensures that the Flask application runs only when the script is executed directly, not when it is imported as a module into another script.
if __name__ == "__main__":
app.run(debug=True)
Explanation:
__name__
is a special Python variable that is automatically set to"__main__"
when the Python script is executed directly.app.run
()
starts the Flask development server. Thedebug=True
argument enables debug mode, which helps in troubleshooting by providing detailed error messages and automatic reloading of the server when changes are made to the code.By using
if __name__ == "__main__":
, we ensure thatapp.run
()
is only executed when the script is run directly usingpython
app.py
, and not when the moduleapp
is imported into another Python script.
Routing in Flask
Handling Different Routes and HTTP Methods
Routing in Flask maps URLs to functions that handle HTTP requests. Let's explore different routes and methods.
Example: Routing and Methods
from flask import Flask
app = Flask(__name__)
@app.route('/')
def index():
return "Index Page"
@app.route('/login', methods=['POST'])
def login():
return 'Login Page'
In Flask, the @app.route()
decorator is used to associate a URL path with a Python function that handles requests to that path.
@app.route('/')
def index():
return "Index Page"
@app.route('/login', methods=['POST'])
def login():
return 'Login Page'
Explanation:
@app.route('/path')
is a decorator that maps a specific URL path ('/'
,'/login'
in these examples) to a corresponding view function (index()
,login()
).When a client makes an HTTP request to the specified path (
'/'
or'/login'
), Flask invokes the associated view function (index()
orlogin()
).Each view function returns a response that will be sent back to the client. Here,
"Index Page"
and"Login Page"
are simple strings, but typically, these functions would render HTML templates or return JSON responses.
methods:
The methods
parameter in the @app.route()
decorator specifies which HTTP methods are allowed for accessing the route. By default, routes in Flask only respond to GET
requests. Here’s how we specify other methods like POST
:
@app.route('/login', methods=['POST'])
def login():
return 'Login Page'
Explanation:
methods=['GET']
inside the decorator indicates that the route ('/'
in this case) will only respond to HTTPGET
requests by default.In the second example,
methods=['POST']
specifies that thelogin()
function should handle HTTPPOST
requests to the/login
endpoint.Allowed methods can include
GET
,POST
,PUT
,DELETE
, and more. This parameter helps define how clients interact with the web application and what actions they can perform.
URL Parameters and Variable Rules
URL parameters allow dynamic URLs with variable values. Let's handle user-specific routes.
@app.route('/user/<username>')
def show_user_profile(username):
return f'User {username}'
Explanation:
Variable Rules:
<username>
in the route captures URL segments as variables (username
inshow_user_profile(username)
).
In Flask, variable rules allow us to create dynamic URLs by capturing parts of the URL and passing them as parameters to view functions. This is achieved using
<variable_name>
syntax within the route definition.@app.route('/user/<username>') def show_user_profile(username): return f'User {username}'
Explanation:
<username>
in the route path ('/user/<username>'
) is a variable rule. It captures the part of the URL after/user/
and passes it as an argument to theshow_user_profile()
function.When a request is made to a URL like
/user/kanishk
, Flask extractskanishk
from the URL and passes it as theusername
argument to theshow_user_profile()
function.The view function
show_user_profile(username)
then uses this captured value (username
) to customize the response. In this case, it returns"User kanishk"
, but typically it would fetch data related to the username from a database or another source.
Benefits of Variable Rules:
Dynamic URLs: Variable rules allow for the creation of dynamic and user-friendly URLs where different users or resources can be accessed using a single route definition.
Flexibility: They provide flexibility in handling varying URL patterns without the need to define multiple routes for each possible combination.
Parameter Passing: By capturing URL segments as variables, Flask simplifies the process of passing data between the client and the server, enhancing the interactivity and functionality of web applications.
Templates and Rendering
Using Templates with Flask
Flask uses the Jinja2 templating engine to render dynamic HTML templates. Let's create a form using templates.
First, make a folder named 'templates' and create a file named 'form.html'.
templates/form.html
<html>
<head>
<title>Student Marks Form</title>
</head>
<body>
<h2>Enter Marks</h2>
<form action="{{ url_for('form') }}" method="POST">
<label for="maths">Maths:</label>
<input type="number" id="maths" name="maths" required><br><br>
<label for="science">Science:</label>
<input type="number" id="science" name="science" required><br><br>
<label for="sanskrit">Sanskrit:</label>
<input type="number" id="sanskrit" name="sanskrit" required><br><br>
<button type="submit">Submit</button>
</form>
</body>
</html>
Explanation:
action="{{ url_for('form') }}"
: Generates the URL for theform
endpoint defined inapp.py
.method="POST"
: Specifies the HTTP method to send form data.
action="{{ url_for('form') }}"
:In HTML forms used with Flask applications, the
action
attribute specifies the URL where the form data should be submitted. Theurl_for()
function in Flask generates URLs for endpoint functions defined in yourapp.py
file.<form action="{{ url_for('form') }}" method="POST"> <!-- Form fields --> <button type="submit">Submit</button> </form>
Explanation:
action="{{ url_for('form') }}"
dynamically generates the URL for the form'saction
attribute. Theurl_for()
function takes the name of the endpoint ('form'
in this case) and returns the URL that corresponds to theform()
function inapp.py
.This approach ensures that if the URL pattern for the
form
endpoint changes inapp.py
, the form HTML automatically uses the updated URL without manual editing.Using
url_for()
enhances maintainability by centralizing URL definitions and reducing the risk of broken links when endpoint URLs are modified.
Benefits of url_for()
in Flask:
URL Flexibility: Allows developers to change URL routes in Flask applications without needing to update every HTML file manually.
Route Centralization: Consolidates URL routing logic within Python code (
app.py
), promoting cleaner separation of concerns between application logic and presentation.Enhanced Maintenance: Simplifies URL management and maintenance, especially in larger applications with multiple routes and templates.
"POST"
:
The method
attribute in HTML forms specifies the HTTP method to be used when submitting form data to the server. In this case, "POST"
is used to send data to the server in the body of the HTTP request.
<form action="{{ url_for('form') }}" method="POST">
<!-- Form fields go here -->
<button type="submit">Submit</button>
</form>
Explanation:
method="POST"
indicates that the form data should be sent to the server using the HTTPPOST
method. This method is commonly used for submitting data that modifies server state, such as adding or updating resources.When the form is submitted (
<button type="submit">Submit</button>
), the browser sends an HTTPPOST
request to the URL specified in theaction
attribute ({{ url_for('form') }}
), along with the data entered in the form fields.POST
requests are suitable for sensitive data or operations that modify server-side data, as they do not expose form data in the URL and can handle larger data payloads compared toGET
requests.
Usage Considerations:
- Data Handling:
POST
requests are appropriate when dealing with form submissions or any operation that requires sending data to the server for processing or storage.
Handling Form Data and Redirects
In Flask, handling form submissions involves capturing user input, processing it, and redirecting users based on the processed data. Let's explore the example provided in app.py
with a focus on each key component.
from flask import Flask, render_template, request, redirect, url_for
app = Flask(__name__)
@app.route('/form', methods=['GET', 'POST'])
def form():
if request.method == 'POST':
maths = float(request.form['maths'])
science = float(request.form['science'])
sanskrit = float(request.form['sanskrit'])
average_marks = (maths + science + sanskrit) / 3
result = "Pass" if average_marks >= 35 else "Fail"
return redirect(url_for(result, score=average_marks))
return render_template('form.html')
if average marks >= 35, the student will pass:
if the average marks <= 35, then the student will fail:
Explanation:
Form Submission Handling (
@app.route('/form', methods=['GET', 'POST'])
):- Defines a route
/form
that handles bothGET
andPOST
requests. This route is responsible for processing form submissions and rendering the initial form.
- Defines a route
Processing Form Data (
if request.method == 'POST':
):Checks if the request method is
POST
, indicating that the form has been submitted.Retrieves form data (
maths
,science
,sanskrit
) fromrequest.form
and converts them to floats to perform calculations.
Calculating Average Marks and Result:
Computes
average_marks
by summing up the scores and dividing by the number of subjects.Uses a conditional statement (
result = "Pass" if average_marks >= 35 else "Fail"
) to determine whether the student passed or failed based on the calculated average marks.
Redirecting Based on Result (
redirect(url_for(result, score=average_marks))
):Uses
redirect()
andurl_for()
functions to redirect the user dynamically based on theresult
variable.url_for(result, score=average_marks)
generates the URL for either thePass
orFail
endpoint, passingscore=average_marks
as a query parameter.
Rendering Form Template (
return render_template('form.html')
):- Renders the
form.html
template when the request method isGET
or after processing aPOST
request.
- Renders the
@app.route('/Pass')
def Pass():
return f"The student has passed with an average score of {request.args.get('score')}."
@app.route('/Fail')
def Fail():
return f"The student has failed with an average score of {request.args.get('score')}."
Explanation:
Dynamic Routes (
@app.route('/Pass')
and@app.route('/Fail')
):Defines two routes
/Pass
and/Fail
that handle redirections based on whether the student passed or failed.These routes receive the
score
query parameter passed from theform()
function viaredirect(url_for(result, score=average_marks))
.
Displaying Result Messages:
Pass()
andFail()
functions retrieve thescore
query parameter usingrequest.args.get('score')
.They dynamically generate and return messages indicating whether the student passed or failed, along with their average score.
Usage of
request.args.get('score')
:request.args.get('score')
retrieves the value of thescore
query parameter passed from theform()
function viaredirect(url_for(result, score=average_marks))
.This parameter allows the
Pass()
andFail()
functions to display personalized messages based on the calculated average score.
Creating an API Endpoint
Building a Simple API with Flask
Flask allows us to create API endpoints easily. Let's build an API endpoint that calculates the product of two numbers from a JSON payload.
app.py
from flask import Flask, request, jsonify
app = Flask(__name__)
@app.route('/api', methods=['POST'])
def product():
data = request.get_json()
value_of_a = float(data['a'])
value_of_b = float(data['b'])
product_value = value_of_a * value_of_b
return jsonify({"product": product_value})
if __name__ == "__main__":
app.run(debug=True)
Explanation:
request.get_json()
: Retrieves JSON data from the request.JSON Handling: Converts JSON data (
{'a': 13, 'b': 42}
) into Python dictionary and performs calculations.
Testing the API with Postman
Now, we'll test our API with the help of Postman, by sending a POST request with a JSON payload.
Testing API Endpoint with Postman
Open Postman and select the POST method.
Enter the URL (
http://localhost:5000/api
).Go to the Body tab, select
raw
, choose JSON format, and paste the JSON object ({"a": 10, "b": 20}
).Click
Send
to see the response.
Conclusion
In this blog, we explored essential aspects of Flask, giving you foundational knowledge to build robust web applications and APIs. From setting up Flask environments to defining routes, handling form data, rendering templates, and creating APIs.
In the next blog, we will set up our project and explore best practices for its development. Read the next blog here.