• E Diel, Dhjetor 28, 2025

URL routing is a fundamental concept in web development, defining how specific URLs are associated with corresponding handlers or functions within a web application. When a user navigates to a URL, the routing system determines which function should process the request and generate an appropriate response. This mechanism is vital for structuring the flow of web applications, ensuring that various pages or functionalities are accessible through intuitive and logical URLs. Effective URL routing facilitates the correct arrangement of back-end logic, guaranteeing that requests are directed to the suitable processing components.

Understanding how routing operates within the platform is essential for developers aiming to build or extend its functionalities, as it governs the accessibility of web pages, forms, and APIs. In the platform's Model-View-Controller (MVC) architecture, the Controller component is responsible for managing web requests. Controllers process incoming requests, interact with the model and view layers, and then deliver the appropriate responses, such as HTML pages or JSON data. Routes are the defined paths or URLs that map to these controller methods, enabling the platform to serve distinct pages or data when a particular URL is accessed.

Controllers and HTTP Requests

In the context of this web framework, a controller is a Python class specifically designed to manage web requests. It acts as a crucial intermediary between the server-side business logic and the user's web browser. Controllers are responsible for processing all incoming HTTP requests, communicating with the underlying business logic (models), and ultimately delivering the correct responses back to the client.

The primary mechanism for routing HTTP requests to controllers is the @http.route decorator. This powerful decorator allows developers to precisely map specific URLs to controller methods, thereby defining the application's behavior when a user accesses those particular URLs. This routing mechanism is fundamental, ensuring that incoming requests are consistently directed to the appropriate controller and its designated method based on the URL provided.

Controllers within this framework are capable of handling various HTTP methods, which include:

  • GET: Commonly used to retrieve resources and load pages.
  • POST: Typically employed for submitting data to the server, such as when filling out a form.
  • PUT: Used for updating existing resources on the server.
  • DELETE: Utilized for removing a specific resource.

By effectively mapping these HTTP methods to specific routes, the framework enables highly flexible and robust web request handling.

The @http.route Decorator

The @http.route decorator forms the core of the platform's URL routing system. It empowers developers to define precisely which URL patterns will trigger the execution of specific controller methods. The fundamental syntax for utilizing this decorator is as follows:

@http.route('/url_path', type='http', auth='public', methods=['GET'])
def controller_method(self, **kwargs):
    return 'Response content'
  • '/url_path': This parameter specifies the exact URL pattern that the route should match.
  • type='http': Indicates that the route is configured to return a standard HTTP response. Alternatively, type='json' can be used to specify that the method will return JSON formatted data.
  • auth='public': Defines the authentication level required to access this particular route. Other common values include 'user' (requiring a logged-in user) and 'none' (requiring no authentication at all).
  • methods=['GET']: Specifies the allowed HTTP methods for this route, such as GET, POST, PUT, or DELETE.

It is possible for developers to define multiple routes within a single controller by applying several @http.route decorators. This flexibility allows for mapping various URL patterns or distinct request methods to different functionalities within the same controller class.

Example of a Simple Route

To illustrate, here’s a straightforward example demonstrating how to map a URL to a controller method and subsequently return an HTML response:

from odoo import http

class MyController(http.Controller):
    @http.route('/hello', type='http', auth='public', methods=['GET'])
    def hello_world(self, **kwargs):
        return '<h1>Hello, World!</h1>'
  • When accessed through a web browser, the '/hello' route will invoke the hello_world method.
  • The method's simple HTML response displays "Hello, World!" on the website.

The framework also provides robust support for handling JSON responses, as demonstrated below:

@http.route('/api/data', type='json', auth='public', methods=['POST'])
def return_json(self, **kwargs):
    data = {'key': 'value'}
    return data

When accessed via a POST request, this route generates JSON data. This capability is particularly useful for building API endpoints or facilitating Asynchronous JavaScript and XML (AJAX) calls within web applications.

Advanced Routing Techniques

Routes within this framework can incorporate dynamic segments, allowing for the direct extraction of variables from the URL itself. These extracted segments can then be seamlessly passed as parameters to the corresponding controller method, enhancing flexibility.

Syntax: Dynamic segments are created by embedding placeholders within angle brackets (<>) directly into the URL path. For example, <string:name> captures a string value, while <int:id> is used to capture an integer.

@http.route('/product/<int:id>', type='http', auth='public', website=True)
def product(self, id):
    return "Product ID: %s" % id
  • This route will successfully match URLs such as /product/5 and pass the value 5 as the id argument to the product method.
  • Validation and Parsing: The framework automatically validates the parameter type (e.g., string or integer) based on the placeholder definition. If the captured value does not conform to the expected type, the system will generate a 404 error.

Multiple Routes in One Decorator

The @http.route decorator supports defining multiple URL patterns for the same controller method by accepting a list of URLs. This feature is particularly beneficial when you need several distinct URLs to trigger identical back-end logic or presentation.

Syntax: To define multiple routes, a list of strings can be provided to the decorator, as shown:

@http.route(['/shop', '/store'], type='http', auth='public', website=True)
def shop(self):
    return "Welcome to the shop!"
  • In this specific case, both the /shop and /store URLs will successfully trigger the execution of the shop method.
  • Use Cases: This approach is frequently employed to manage legacy URLs, provide more user-friendly URL alternatives, or support various naming conventions (e.g., directing both /cart and /basket to the same functional endpoint).

Working with Query Strings

Within this framework, it is possible to retrieve query string parameters directly from the URL, specifically the portion following the '?' symbol. These parameters offer a flexible way to modify your controller's behavior and response without altering the fundamental route pattern.

Query Parameter Access: Query parameters are readily accessible through the request.params dictionary, which contains key-value pairs of the parameters.

@http.route('/search', type='http', auth='public')
def search(self):
    query = request.params.get('q')
    return "Search results for: %s" % query
  • If the URL is /search?q=Odoo, the method will capture "Odoo" as the value of the query parameter and return the corresponding search results.
  • Pitfalls and Best Practices:
    • Always validate and sanitize query parameters to prevent security vulnerabilities such as SQL injection or the processing of improper data.
    • Implement default settings or robust error handling to gracefully manage situations where expected query parameters might be absent.

Route Prioritization and Order of Evaluation

When multiple routes potentially match an incoming request, the platform employs a specific order of registration to determine which route will be executed. Typically, more specialized routes are given precedence over more general ones, ensuring the most specific match is handled first.

Order of Definition: In cases where conflicts arise due to overlapping route patterns, the order in which your code defines and registers the routes dictates which one will be utilized by the system.

@http.route('/item/<int:id>', type='http', auth='public')
def item_by_id(self, id):
    return "Item with ID %s" % id

@http.route('/item/<string:name>', type='http', auth='public')
def item_by_name(self, name):
    return "Item with name %s" % name

For instance, if a request is made to /item/123, the system will prioritize the first route (/item/<int:id>) because the URL segment perfectly matches an integer type. Should the URL contain a string value instead, the system will then gracefully fall back to the second, more general route (/item/<string:name>).

Practical Examples of Routing

This section provides practical examples, starting with a basic illustration of how to create a route that returns simple HTML or plain text. This foundational example is often the initial step in grasping the concept of URL routing within the framework.

from odoo import http

class HelloWorld(http.Controller):
    @http.route('/hello', auth='public', methods=['GET'])
    def hello(self):
        return "Hello, world!"

Explanation: The route /hello is directly mapped to the hello method within the HelloWorld controller. When this route is accessed via a GET request, it returns a basic "Hello, world!" message as plain text.

Endpoint: Navigating to http://localhost:8069/hello in your browser will display "Hello, world!".

Handling Form Submission

The creation of web forms, the submission of user data, and the subsequent processing within a controller represent a common and essential web functionality. This example demonstrates how to construct a basic form and effectively manage its submitted data within the framework's routing mechanism.

class FormController(http.Controller):
    @http.route('/form', auth='public', methods=['GET'])
    def form(self, **kw):
        return """
            <form action="/form/submit" method="post">
                <input type="text" name="name" placeholder="Your name" required>
                <input type="submit" value="Submit">
            </form>
        """

    @http.route('/form/submit', auth='public', methods=['POST'])
    def form_submit(self, **kw):
        name = kw.get('name', '')
        return f"Hello, {name}!"

Explanation: The first route (/form) is responsible for rendering an HTML form that prompts the user for a name. When this form is submitted via a POST request, the form_submit method captures the input data and subsequently returns a personalized greeting.

Endpoint: The form can be viewed by going to http://localhost:8069/form. After submitting the form, you will be directed to a page that greets you with the name you entered.

Creating a JSON Endpoint

Returning data in JSON format is exceptionally useful for facilitating Asynchronous JavaScript and XML (AJAX) calls and for building robust Application Programming Interfaces (APIs). This example illustrates the process of creating a JSON response designed for consumption by modern web applications.

class JsonController(http.Controller):
    @http.route('/json_data', auth='public', methods=['GET'], type='json')
    def json_data(self):
        return {'message': 'This is a JSON response', 'status': 'success'}

Explanation: The /json_data route is configured to return a JSON response. By setting type='json' within the decorator, the framework automatically serializes the returned Python dictionary into the appropriate JSON format. This functionality is invaluable for API endpoints or AJAX calls that are designed to expect JSON data.

Endpoint: Visiting http://localhost:8069/json_data will display the following JSON response:

{
    "message": "This is a JSON response",
    "status": "success"
}

Dynamic Content Rendering with Templates

For rendering dynamic content, the framework utilizes the powerful QWeb templating engine. This allows developers to pass data to templates and render complex views directly from a defined route, creating highly interactive web pages.

class TemplateController(http.Controller):
    @http.route('/template', auth='public', methods=['GET'])
    def template(self, **kw):
        values = {'title': 'Welcome to Odoo', 'content': 'This is a dynamic page.'}
        return http.request.render('my_module.template_view', values)

Explanation: The template route is responsible for rendering a view using the QWeb template engine. The http.request.render() method is employed to process the template_view, passing a dictionary of values (containing title and content) to be used within the template.

Endpoint: When a user accesses http://localhost:8069/template, the framework will dynamically populate the `template_view` QWeb template with the provided values, rendering the content accordingly.

Testing and Debugging Routes

The following comprehensive steps will guide you through effectively testing and debugging your routes, ensuring that your web application functions reliably and as anticipated.

Utilizing Built-In Logging for Routes

Logging serves as an indispensable tool for troubleshooting and verifying that routes are correctly mapped and accessible within the web application. To effectively monitor route accesses and other critical operations, it is highly recommended to enable and configure the logging system.

Enabling Logging: The standard Python logging package can be leveraged to provide detailed logging for your defined routes. An illustrative example of how to log incoming requests to a specific route is provided below:

import logging
from odoo import http

_logger = logging.getLogger(__name__)

class MyController(http.Controller):
    @http.route('/log_example', auth='public', methods=['GET'])
    def log_example(self, **kw):
        _logger.info("Route /log_example was accessed.")
        return "For more information, see the log."

Understanding Log Levels

The logging system supports numerous log levels, including DEBUG, INFO, WARNING, ERROR, and CRITICAL. These levels should be utilized judiciously, corresponding to the severity and importance of the message being logged.

  • DEBUG: Reserved for fine-grained informational events, useful during development for detailed tracing.
  • INFO: Provides general information about the route being accessed or significant application events.
  • WARNING: Indicates potentially harmful situations that do not necessarily constitute an error but warrant attention.
  • ERROR: Signifies errors and issues that prevent a specific operation from completing successfully.
  • CRITICAL: Used for severe errors that may lead to application failure or data loss.

Practical Debugging Tips

  • Always log significant actions, such as the initiation and conclusion of requests, particularly for POST requests, to track their lifecycle.
  • Regularly inspect the application's logs, typically located in the logs directory of your installation, for any unexpected behavior or route-related errors.

Inspecting Route Definitions

To confirm that your custom routes are accurately defined and being correctly accessed, you can inspect the list of all registered routes within the framework.

Examining Routes: The framework provides internal tools that allow you to view a comprehensive list of all registered routes within the application.

from odoo import http

class RouteInspector(http.Controller):
    @http.route('/inspect_routes', auth='public')
    def inspect_routes(self):
        routes = http.request.env['ir.http'].search([])
        for route in routes:
            print(route.url)
        return "Routes have been printed to the console."

Command-Line or Interactive Debugging: To run your application instance in debug mode and observe logs and interactions directly in the console, utilize the primary command-line tool:

./odoo-bin --dev=all
  • The --dev=all flag enables detailed logging and comprehensive error reports, providing valuable insights into all route information and request handling within the logs.

Interactive Debugging with PDB: Python's built-in debugger, pdb, offers a powerful way to step through your code and examine variables in real-time. This is particularly effective when the application is running in a development environment, allowing for detailed code inspection.

Unit Testing Controllers and Routes

Unit testing is indispensable for ensuring that your controllers and routes function precisely as intended under various conditions. The framework provides a robust testing framework that you can leverage to create comprehensive tests for your custom controllers and their associated routes.

Writing Unit Tests: The testing infrastructure seamlessly integrates Python's standard unittest module, providing a familiar environment for test development.

from odoo.tests import TransactionCase

class TestMyController(TransactionCase):
    def test_hello_world_route(self):
        response = self.env['ir.http'].web_request('/hello')
        self.assertEqual(response, "Hello, world!")

Test Setup: To safeguard your production data, the testing framework automatically creates a dedicated test database and executes all tests within an isolated environment, preventing any unintended side effects.

Running Tests: Tests can be executed directly from the command line using the primary command-line utility:

./odoo-bin --test-enable --db-filter=my_database_name

Recommended Practices for Testing

  • Provide tests for both valid and invalid inputs to ensure your routes handle a wide range of edge cases gracefully.
  • Utilize the assertEqual function to confirm that the response from your routes precisely matches the anticipated output.
  • Isolate tests to specific routes and controllers to guarantee that every component of functionality is verified independently and thoroughly.