Flask
Why is it so popular?
Introduction
Okay, I know Python. I have written so many different classes and functions. I have created several web crawlers for data extraction. I have also written about several ML-related technologies. Now, I wanted to create a website. I asked my friend how to do it and he replied “Use a framework like Django or Flask”. My first thought was why? Can I not create a website without using these frameworks?
Based on https://pythonbasics.org,
Flask is a web framework, it’s a Python module that lets you develop web applications easily. It has a small and easy-to-extend core: it’s a microframework that doesn’t include an ORM (Object Relational Manager) or such features.
It does have many cool features like URL routing and a template engine. It is a WSGI web app framework.
Features of Flask
- Development server and debugger
- Integrated support for unit testing
- RESTful request dispatching
- Uses Jinja templating
- Support for secure cookies (client side sessions)
- 100% WSGI 1.0 compliant
- Unicode-based
- Complete documentation
- Google App Engine compatibility
- Extensions available to extend functionality
- and more ….
Development Server and debugger
Development servers are used to quickly develop applications. They provide a runtime environment which is incredibly useful regardless of the size of your project and where you stand in your developer journey.
Flask includes a lightweight development server that allows you to run your application locally during the development phase. This server is not meant to be used in a production environment but rather as a convenient way to quickly test and iterate on your code.
Flask is able to provide a development server through the use of the Werkzeug library, which is a powerful toolkit for building Python web applications. Werkzeug includes a WSGI (Web Server Gateway Interface) server called Werkzeug’s development server.
Here’s an overview of how Flask utilizes Werkzeug’s development server:
- Flask creates an instance of the Flask application object: In your Flask application code, you typically create an instance of the Flask class, often named
app
, to represent your web application. This object holds all the configurations, routes, and views of your application.
from flask import Flask
app = Flask(__name__)
2. The Flask application object interacts with the Werkzeug development server: Flask provides an interface to the development server through the app.run()
method. When you call this method, Flask internally invokes the development server from Werkzeug to handle incoming requests and manage the application's lifecycle.
3. The app.run()
method triggers the execution of the Flask application and starts the Werkzeug development server. By default, the server listens on localhost
(127.0.0.1) and port 5000
. You can access your Flask application by visiting http://localhost:5000
in a web browser.
4. The Werkzeug development server handles HTTP requests: Once the development server is running, it continuously listens for incoming HTTP requests on the specified host and port. When a request arrives, the server receives it and forwards it to the Flask application for processing.
5. The development server is responsible for managing the request-response cycle, handling concurrency, and dispatching requests to the appropriate route handlers within the Flask application.
6. Auto-reloading during development: One notable feature of the development server is its ability to automatically reload the Flask application when changes are detected in your code. This automatic reloading behavior allows you to make modifications to your application’s code and see the changes reflected immediately without needing to manually restart the server.
7. When a file modification occurs, Werkzeug’s development server uses Python’s built-in reload
function to reload the Flask application module. This behavior greatly enhances the development experience, as you can iteratively make changes and test your application without interrupting the workflow.
Overall, Flask’s integration with Werkzeug’s development server provides a convenient way to develop and test Flask applications locally.
However, it’s important to note that the Werkzeug development server is not intended for production use, as it lacks certain performance optimizations and security features present in production-ready web servers like Gunicorn or uWSGI.
Integrated support for unit testing
Flask provides support for unit testing by offering a testing framework that facilitates the creation and execution of tests for Flask applications. This framework is designed to help you write comprehensive tests to verify the behavior and functionality of your Flask application.
Testing Client: Flask provides a test client that simulates interactions with your application without the need for running an actual server. The test client allows you to send HTTP requests to your application and examine the responses, enabling you to test your application’s routes, views, and other components.
The test client is created by calling the app.test_client()
method on your Flask application instance. It returns a test client object that you can use to make requests and retrieve the responses.
Testing Context: Flask’s testing framework provides a testing context that allows you to set up a clean and isolated environment for each test. This context ensures that tests are independent of each other and prevents interference between them.
The testing context provides features such as handling request context, session handling, and application configuration
Assertion Helpers: Flask integrates with popular testing frameworks like unittest
and pytest
, allowing you to utilize their assertion helpers to perform more complex assertions and streamline the testing process.
How does it work in detail ?
- Initialization: Flask initializes the test client by creating an instance of the
FlaskClient
class, which extends the base class of Werkzeug’sClient
class. - Application Context: Flask sets up an application context for the test client. The application context contains important information about the Flask application, such as its configuration, URL routing, and other context-related data. The application context ensures that the test client operates in the correct context and can access the necessary resources.
- Request Context: Flask also establishes a request context for the test client. The request context represents an incoming request to the Flask application and contains information about the request, such as the request method, URL, headers, and form data. The request context enables the test client to simulate requests to your application accurately.
- Testing Client Object: Once the application and request contexts are set up, Flask creates a test client object using the initialized
FlaskClient
instance. This test client object represents your Flask application and provides methods for making HTTP requests, such asget()
,post()
,put()
,delete()
, and others. - Handling Requests: When you call a method on the test client object, such as
client.get('/path')
, Flask processes the request internally. It generates a mock request object based on the provided method and URL and dispatches it to the Flask application for handling. - Flask’s routing mechanism matches the URL of the mock request with the corresponding route defined in your Flask application. It then invokes the associated view function or method, which generates a response.
- Response Handling: Once the view function generates a response, Flask captures it and wraps it in a response object. This response object contains information such as the HTTP status code, headers, and response body.
- The test client retrieves the response object from the Flask application and returns it to your testing code. You can then inspect the response object to validate the expected behavior of your application.
- Cleanup: After each request made through the test client, Flask performs necessary cleanup operations. It removes the request and application contexts associated with the test client, ensuring that subsequent requests are handled in isolation.
RESTful request dispatching
REST (Representational State Transfer) is an architectural style for designing networked applications, commonly used in building web services and APIs. In a RESTful application, different HTTP methods (such as GET, POST, PUT, DELETE) are used to perform different operations on resources (e.g., data entities or objects).
RESTful request dispatching refers to the mechanism by which an application routes incoming HTTP requests to the appropriate handlers or controllers based on these RESTful principles.
Flask, being a flexible web framework, allows you to define routes and map them to specific functions or methods that handle the corresponding HTTP requests. This mapping is typically achieved through the use of decorators.
from flask import Flask
app = Flask(__name__)
@app.route('/books/<book_id>', methods=['GET'])
def get_book(book_id):
# Logic to retrieve book with the given ID
# ...
return 'Details of book with ID {}'.format(book_id)
A decorator is a design pattern in Python that allows a user to add new functionality to an existing object without modifying its structure. Decorators are usually called before the definition of a function you want to decorate. Decorators provide a flexible and convenient way to add extra functionality to existing functions, making them more reusable and modular.
In this example, the @app.route()
decorator is used to associate the /books/<book_id>
URL pattern with the get_book()
function. The <book_id>
part in the URL is a variable that captures the value from the URL and passes it as an argument to the function.
RESTful request dispatching in Flask allows you to structure your application’s URL endpoints in a meaningful way, following REST principles, and map them to appropriate handlers for processing the corresponding HTTP requests.
Jinja templating
Jinja is a popular templating engine for Python web applications, including Flask. It is a fast, expressive, extensible templating engine. Special placeholders in the template allow writing code similar to Python syntax. Then the template is passed data to render the final document. It generates dynamic content in web pages by combining static HTML templates with dynamic data and allows the separation of presentation logic from application logic and promotes code readability and maintainability.
It includes:
- Template inheritance and inclusion.
- Define and import macros within templates.
- HTML templates can use autoescaping to prevent XSS from untrusted user input.
- A sandboxed environment can safely render untrusted templates.
- Async support for generating templates that automatically handle sync and async functions without extra syntax.
- I18N support with Babel.
- Templates are compiled to optimized Python code just-in-time and cached, or can be compiled ahead-of-time.
- Exceptions point to the correct line in templates to make debugging easier.
- Extensible filters, tests, functions, and even syntax.
With Jinja templating,
from flask import Flask, render_template
app = Flask(__name__)
@app.route('/')
def index():
names = ['Alice', 'Bob', 'Charlie']
return render_template('index.html', names=names)
<ul>
{% for name in names %}
<li>{{ name }}</li>
{% endfor %}
</ul>
Without Jinja templating,
from flask import Flask
app = Flask(__name__)
@app.route('/')
def index():
names = ['Alice', 'Bob', 'Charlie']
# Generate HTML content using string concatenation
html = "<ul>"
for name in names:
html += "<li>{}</li>".format(name)
html += "</ul>"
return html
Returning such HTML code is important in scenarios such as,
Traditional Web Pages: If you are building a traditional web application that generates dynamic HTML pages, returning HTML is the natural choice. HTML allows you to define the structure, layout, and presentation of the entire page, including headers, footers, navigation menus, and other components.
- Server-side Rendering: Returning HTML is commonly used in server-side rendering (SSR) scenarios. SSR involves generating the HTML content on the server, including dynamic data, before sending it to the client. This approach is suitable for applications that require search engine optimization (SEO) or need initial rendering on the server to provide faster page loads and improved user experience.
- Template Rendering: When using templating engines like Jinja (or other similar tools), returning HTML allows you to leverage the power of templates to separate the presentation logic from the application logic. Templates allow you to reuse common layout elements, handle dynamic data injection, and maintain consistency across multiple pages. By returning HTML, you can benefit from the templating features and easily integrate dynamic data into the HTML structure.
- Web Page Interactivity: HTML, combined with client-side JavaScript and CSS, enables interactivity and dynamic behavior in web pages. By returning HTML, you have the flexibility to include interactive elements, event handling, form submissions, and other client-side functionality in the web page itself.
- Progressive Enhancement: Returning HTML allows for progressive enhancement. You can provide a basic HTML version of the page that is accessible and usable by all users, regardless of their client capabilities. Then, you can enhance the page with additional interactivity and features using JavaScript to create a richer experience for clients that support it.
- Browser Compatibility: HTML is natively supported by web browsers, ensuring wide compatibility across different devices and platforms. It allows you to take advantage of the rendering capabilities and built-in browser features without relying solely on client-side JavaScript.
Support for secure cookies
Secure cookies are cookies that are only sent over HTTPS connections, providing an extra layer of security by protecting the cookie’s content from being transmitted over unsecured HTTP connections.
Flask provides support for secure cookies through the use of the Secure
flag and the SECRET_KEY
configuration option. Here's how Flask enables secure cookies:
from flask import Flask, make_response
app = Flask(__name__)
app.config['SECRET_KEY'] = 'your-secret-key'
@app.route('/')
def set_secure_cookie():
response = make_response('Setting secure cookie')
response.set_cookie('cookie_name', 'cookie_value', secure=True)
return response
- Setting the
Secure
Flag: When you set a cookie in Flask, you can include the secure parameter to indicate that the cookie should only be sent over secure HTTPS connections. This ensures that the cookie is not transmitted over unencrypted HTTP connections, reducing the risk of eavesdropping and cookie tampering. - In this example above, the
set_secure_cookie()
function sets a secure cookie namedcookie_name
with the valuecookie_value
. By includingsecure=True
, Flask ensures that the cookie is only sent over HTTPS connections. - Enabling HTTPS: To use secure cookies, your Flask application must be configured to run over HTTPS. Flask itself does not handle the HTTPS configuration; it relies on a web server or reverse proxy (such as Nginx or Apache) to handle the SSL termination and encryption. You need to configure the web server to terminate SSL and forward the secure requests to your Flask application. To do so we need to set up a NGINX configuration file in Flask. It’s important to note that using secure cookies without HTTPS is not effective. The
secure
flag instructs the browser to send the cookie only over secure connections, but if the connection is not encrypted (i.e., HTTP), the cookie can still be intercepted or modified. - SECRET_KEY Configuration: Flask requires a
SECRET_KEY
configuration option to securely sign and encrypt the cookie data. TheSECRET_KEY
is used to generate a cryptographic signature for the cookie, ensuring its integrity and preventing tampering. It is also used to encrypt and decrypt the cookie data when theSESSION_COOKIE_SECURE
option is enabled. It’s recommended to set a strong and uniqueSECRET_KEY
for your Flask application. The value should be kept secret and not shared or disclosed publicly. For increased security, consider using a long random string or a secure key generation mechanism.
app.config['SECRET_KEY'] = 'your-secret-key'
By combining the secure=True
flag, enabling HTTPS, and setting a secure SECRET_KEY
, Flask provides support for secure cookies. These measures help protect sensitive information stored in cookies, ensuring they are only transmitted over encrypted connections and cannot be tampered with.
100% WSGI 1.0 compliant
WSGI compliant means that a web application or server follows the specifications outlined by the Web Server Gateway Interface (WSGI). WSGI is a standard interface between web servers and web applications or frameworks in Python, defining a common protocol for communication and interoperability.
Here are some key aspects of WSGI compliance:
- Conforming to the WSGI Specification: A WSGI-compliant application or server adheres to the specifications defined in the PEP 3333 document. This specification outlines the rules and guidelines for implementing the WSGI interface, including the expected behavior, method signatures, and conventions.
- Implementing the WSGI Callable: In WSGI, the core component is the WSGI application callable, often referred to as the “application” or “app.” It is a Python callable (typically a function or an object with a
__call__
method) that takes two arguments: theenviron
dictionary representing the request environment, and astart_response
function for sending the response headers. - Handling Request and Response: WSGI-compliant applications or frameworks handle incoming HTTP requests by parsing the
environ
dictionary to extract request information, such as the request method, headers, and query parameters. They generate a response body and use thestart_response
function to send the response headers and start the response. The response body is typically an iterable (such as a list or generator) that produces the response data. - Adhering to the WSGI Middleware Concept: WSGI middleware is a software component that sits between the web server and the WSGI application, providing additional functionality or processing. WSGI-compliant servers and frameworks allow the use of middleware components to add features like request/response modification, session management, authentication, or logging. Middleware components conform to the WSGI interface and can be chained together to form a processing pipeline.
- Interoperability: One of the primary goals of WSGI is to enable interoperability between web servers and web applications or frameworks. A WSGI-compliant application can be deployed on any WSGI-compliant server without requiring modifications or rewrites. Similarly, a WSGI-compliant server can run any WSGI-compliant application, promoting flexibility, portability, and interchangeability.
- Compliance Testing: There are tools and frameworks available to test the compliance of web applications and servers with the WSGI specification. These tools verify that the implementation adheres to the required behaviors and interfaces defined by WSGI, ensuring compatibility and standardization.
WSGI compliance fosters compatibility between different components in the Python web ecosystem. It allows developers to build web applications and servers that can work together seamlessly, promoting code reusability, interoperability, and the ability to leverage a wide range of existing WSGI-compatible middleware and frameworks.
Conclusion
In conclusion, flask out of the box provides a lot of features that allows us to write secure and modular code easily. Hence, even thought someone can build their own framework from scratch it makes a lot more sense to use flask to reduce build time and create the most beautiful website for their clients.