Flask

repository·main·Indexed 13 days ago

https://github.com/pallets/flask

A lightweight WSGI web application framework for Python, version 3.2.0.dev. Flask is an unopinionated framework that wraps Werkzeug and Jinja, providing core essentials for web development including routing, request/app contexts, and a flexible signal system via Blinker.

Tokens
93.4K
Snippets
314
Records
399
Agent score
97%

What's inside Flask

  1. Overview of JavaScript Ajax patterns in Flask

    main

    This example demonstrates how to post form data to a Flask backend and process JSON responses using JavaScript. This pattern allows for making asynchronous requests without navigating away from the current page. The example covers three common methods:

    • fetch API
    • XMLHttpRequest object
    • jQuery.ajax method
  2. What is Flask?

    main
    Flask is a lightweight WSGI web application framework designed for quick starts and scalability. It acts as a wrapper around Werkzeug (a WSGI utility library) and Jinja (a template engine). Flask is unopinionated, meaning it does not enforce specific dependencies or project layouts, allowing developers to choose their own tools and extensions.
  3. Choose a deployment method for Flask

    main

    Deployment options for Flask generally fall into two categories: Self-Hosted WSGI servers or Managed Hosting Platforms.

    Self-Hosted WSGI Servers

    You can run your application using a dedicated WSGI server. Common options include:

    • gunicorn
    • waitress
    • mod_wsgi
    • uwsgi
    • gevent
    • asgi (for asynchronous applications)

    Managed Hosting Platforms

    If you prefer not to maintain your own server, networking, or domain, you can use a hosting service. Common platforms with support for Flask/Python include:

    • PythonAnywhere
    • Google App Engine
    • Google Cloud Run
    • AWS Elastic Beanstalk
    • Microsoft Azure
  4. What is an Application Factory in Flask?

    main

    Instead of creating a global Flask instance at the top level of your code, an application factory is a function that creates and configures a Flask instance and then returns it. This pattern is recommended for growing projects as it allows for better configuration management and easier testing by creating distinct application instances with different settings (e.g., for testing vs. production) within the same process.

    def create_app(test_config=None):
        app = Flask(__name__)
        # ... configuration and setup ...
        return app
  5. Handle 404 and 405 errors in Blueprints

    main

    There is a critical caveat when using @blueprint.errorhandler for 404 Not Found and 405 Method Not Allowed exceptions:

    These handlers are only invoked if they are explicitly triggered via a raise statement or a call to abort() from within a view function belonging to that Blueprint. They are not invoked when a user attempts to access an invalid URL that does not match any route in the application.

    Because a Blueprint does not 'own' a specific URL space, the Flask application cannot determine which Blueprint's error handler to use for an invalid URL.

    Recommended Pattern: To implement different handling strategies for 404/405 errors based on URL prefixes (e.g., returning JSON for /api/ routes and HTML for others), define the handlers at the application level using the request proxy to inspect the path.

    @app.errorhandler(404)
    @app.errorhandler(405)
    def _handle_api_error(ex):
        if request.path.startswith('/api/'):
            return jsonify(error=str(ex)), ex.code
        else:
            return ex
  6. How Flask converts view return values into responses

    main

    Flask automatically converts the return value of a view function into a response object. The conversion logic follows these rules:

    1. Response objects: If you return a response object, it is used directly.
    2. Strings: Returned as a response with the string as the body, a 200 OK status, and a text/html mimetype.
    3. Iterators/Generators: If they yield strings or bytes, they are treated as a streaming response.
    4. Dicts or Lists: Automatically converted to a JSON response using flask.json.jsonify.
    5. Tuples: Can provide extra metadata in the forms (response, status), (response, headers), or (response, status, headers). The status overrides the default code, and headers can be a list or dictionary.
    6. WSGI Applications: If no other type matches, Flask attempts to treat the return value as a valid WSGI application.
  7. Prevent SQL Injection in SQLite queries

    main

    When passing variable parts to an SQL statement, never use Python string formatting (e.g., f"SELECT ... WHERE name = '{name}'"). This makes your application vulnerable to SQL Injection attacks.

    Instead, use a question mark (?) as a placeholder in your SQL string and pass the arguments as a list or tuple to the .execute() method.

    # CORRECT
    query_db('select * from users where username = ?', [the_username])
    
    # INCORRECT (Vulnerable)
    # query_db(f'select * from users where username = "{the_username}"')
  8. Understand the Sansio layer in Flask

    main

    The Sansio layer contains core logic designed to be used by alternative Flask implementations (such as Quart). Because this layer is intended to be implementation-agnostic, it adheres to strict constraints:

    • No I/O: The code cannot perform any Input/Output operations, nor can it be part of a path that triggers I/O.
    • No Flask Globals: The code cannot access or rely on Flask's global objects (like request, session, or g).

    Developers building alternative web frameworks or specialized implementations should look to this layer for core logic that remains decoupled from specific I/O or global state management.

  9. Signals and Request Context

    main
    Context-local proxies (like flask.g and flask.request) are available between the request_started and request_finished signals. You can safely rely on these proxies within your signal subscribers during this window.
  10. Dispatch applications by URL path prefix

    main

    You can dispatch requests to different Flask applications based on the first segment of the URL path. This is similar to subdomain dispatching but looks at PATH_INFO instead of the Host header.

    Key Differences from Subdomain Dispatching

    • Fallback Mechanism: Unlike the subdomain dispatcher, a path dispatcher typically includes a default_app. If the creation function returns None (meaning no specific application exists for that path), the request is delegated to the default_app.
    • Path Shifting: When a match is found, the dispatcher should use wsgiref.util.shift_path_info to strip the prefix from the environment before passing it to the sub-application, so the sub-application sees the path relative to its own root.
    from threading import Lock
    from wsgiref.util import shift_path_info
    
    class PathDispatcher:
        def __init__(self, default_app, create_app):
            self.default_app = default_app
            self.create_app = create_app
            self.lock = Lock()
            self.instances = {}
    
        def get_application(self, prefix):
            with self.lock:
                app = self.instances.get(prefix)
                if app is None:
                    app = self.create_app(prefix)
                    if app is not None:
                        self.instances[prefix] = app
                return app
    
        def __call__(self, environ, start_response):
            # Peek at the first segment of PATH_INFO
            segments = environ.get("PATH_INFO", "").lstrip("/").split("/", 1)
            prefix = segments[0] if segments else None
            
            app = self.get_application(prefix)
            if app is not None:
                shift_path_info(environ)
            else:
                app = self.default_app
            return app(environ, start_response)
    
    # Usage example
    def make_app(prefix):
        user = get_user_for_prefix(prefix)
        if user is not None:
            return create_app(user)
    
    application = PathDispatcher(default_app, make_app)
  11. The lifecycle of the App and Request Context

    main

    The context follows a stack-based lifecycle. When an activity (like a request) begins, a context is 'pushed' onto the stack, making the proxies available. When the activity ends, the context is 'popped'.

    Request Lifecycle Steps

    1. The app context is pushed; proxies become available.
    2. The appcontext_pushed signal is sent.
    3. The request is dispatched.
    4. teardown_request decorated functions are called.
    5. The request_tearing_down signal is sent.
    6. teardown_appcontext decorated functions are called.
    7. The appcontext_tearing_down signal is sent.
    8. The app context is popped; proxies are no longer available.
    9. The appcontext_popped signal is sent.

    Note on Teardown Callbacks: Functions decorated with @app.teardown_request or @app.teardown_appcontext are called when the context is popped. They are executed even if an unhandled exception occurred. These functions should be written to be independent of other callbacks, as there is no guarantee regarding the order or state of other parts of the request dispatch.

  12. Dispatch applications by subdomain

    main

    To create unique Flask application instances for different subdomains (e.g., user1.example.com, user2.example.com), you can implement a custom WSGI dispatcher. This dispatcher inspects the HTTP_HOST environment variable to identify the subdomain and uses an application factory to instantiate the corresponding Flask app.

    Implementation Pattern

    1. Use an Application Factory pattern to create new instances on demand.
    2. Implement a WSGI class that maintains a cache of instantiated applications to avoid repeated creation.
    3. Use werkzeug.exceptions.NotFound if a requested subdomain does not map to a valid user/application to ensure a proper 404 response.

    Note: This pattern requires the webserver to be configured to route all subdomains to your application.

    from threading import Lock
    from werkzeug.exceptions import NotFound
    
    class SubdomainDispatcher:
        def __init__(self, domain, create_app):
            self.domain = domain
            self.create_app = create_app
            self.lock = Lock()
            self.instances = {}
    
        def get_application(self, host):
            host = host.split(':')[0]
            assert host.endswith(self.domain), 'Configuration error'
            subdomain = host[:-len(self.domain)].rstrip('.')
            with self.lock:
                app = self.instances.get(subdomain)
                if app is None:
                    app = self.create_app(subdomain)
                    self.instances[subdomain] = app
                return app
    
        def __call__(self, environ, start_response):
            app = self.get_application(environ['HTTP_HOST'])
            return app(environ, start_response)
    
    # Usage example
    def make_app(subdomain):
        user = get_user_for_subdomain(subdomain)
        if user is None:
            return NotFound()
        return create_app(user)
    
    application = SubdomainDispatcher('example.com', make_app)