Parameters

The path that is being routed to can include parameters for the linked Form to use upon initialization, allowing differentiation of different pages based on the same Form.

For example, you could have a Route with a user-id parameter. The parameter would be used to populate the Profile form with the data associated to the requested user ID. With two users registered, User 1 and User 2, you would have two pages associated with this route, each with their own URL:

  • /my-app/profiles/user-1
  • /my-app/profiles/user-2

We can define parameters for a Route in its path argument. Each parameter in the path is denoted using the : character.

class RouteWithParameters(Route):
    path = '/route-with-parameters/:my-parameter'
    form = 'FormWithParameters'

In this instance, navigating to the /route-with-parameters/123 path will match this route, supplying 123 as a parameter under the key my-parameter.

Do keep in mind that the order in which you define your Routes is especially important when using parameters. More information can be found here.

When opening a Form via router navigation, Forms are given a RoutingContext object as the routing_context keyword argument of the Form’s __init__ method. This RoutingContext object holds data and events relevant to the current navigation request.

Any parameters included in the URL are stored in the RoutingContext object, under its params attribute. This means we can easily access any parameters in a Form’s __init__ method, for immediate usage or storage.

class FormWithParameters(FormWithParametersTemplate):

    def __init__(self, routing_context=None, **properties):
        self.routing_context = routing_context
        print(routing_context.params['my-parameter'])

Navigating to the /route-with-parameters/123 URL will open the FormWithParameters Form and print 123 in the app console.

Query

Another way to communicate with Forms through a URL path are query parameters. They can be entered in a URL following the ? character, like this:

/route-with-query-parameters?my-query=123

Routes don’t need to be configured to use query parameters, and any queries submitted through the URL can be accessed through the RoutingContext of a Form.

class FormWithQueryParameters(FormWithQueryParametersTemplate):

    def __init__(self, routing_context=None, **properties):
        self.routing_context = routing_context
        print(routing_context.query.get('my-query', None))

Unlike regular parameters, the entry of query parameters is entirely optional, and their values can be changed during runtime without pointing to a new route instance.

Query parameter changes in the URL can be tracked using the query_change event. On Form initialization, we can add a handler for this event, which can then be raised whenever a query parameter is changed, allowing us to update the Form.

class FormWithQueryParameters(FormWithQueryParametersTemplate):

    def __init__(self, routing_context=None, **properties):
        self.routing_context = routing_context

        routing_context.add_event_handler('query_changed', self.on_query_change)
        routing_context.raise_init_events() # raises the query_changed event

    def on_query_change(self, **event_args):
        query = self.routing_context.query
        print(query.get('my-query', None))

By default, a change in query parameters will prompt the router to navigate to the route again. The reason for this is that, by default, a route’s query parameters are part of cache dependencies.

Cache dependencies

Cache dependencies are a dictionary that constitutes part of the unique identity of a Route instance. Their contents determine whether two Route instances are considered two states of the same page, or two entirely different pages.

This distinction is important for caching; two different pages need to be cached in separate locations, while two states of the same page refer to the same location in cache. Cache dependencies are therefore part of a page’s caching key.

Cache dependencies are generated by the cache_deps method of Route objects, and can be found in RoutingContext objects under their deps property. By default, they are the query dictionary supplied with the navigation request.

This means that, by default, two paths that point towards the same Route but with different query parameters would actually point toward two different pages.

Let’s look at an example.

from routing.router import Route
import anvil.server

class Profile(Route):
    cache_form = True
    cache_data = True
    path = "/my-app/profiles/:user-id
    form = "Profile"

    def load_data(self, **loader_args):
        return anvil.server.call(
            'get_user_data',
            loader_args['routing_context'].params['user-id']
        )

Here, /my-app/profiles/user-1?show-optional-panel=true would point to a different location in cache than /my-app/profiles/user-1?show-optional-panel=false, leading to the creation of two separate instances of our Profile route.

In this specific case, that might not necessarily be what we want; our query parameter here only represents the state of our page, it does not denote a fully separate page.

By overriding our route’s cache_deps method, we can make our route’s caching keys exclude query parameters, be it completely or only keeping a subset of them, thus maintaining the page’s identity no matter the queries.

from routing.router import Route
import anvil.server

class Profile(Route):
    cache_form = True
    cache_data = True
    path = "/my-app/profiles/:user-id
    form = "Profile"

    def load_data(self, **loader_args):
        return anvil.server.call(
            'get_user_data',
            loader_args['routing_context'].params['user-id']
        )

    def cache_deps(self, **loader_args):
        # Do not return any cache dependencies.
        # This means that the identity of a page will only be tied to its path and parameters.
        return None

With this code, navigating to both /my-app/profiles/user-1?show-optional-panel=true and /my-app/profiles/user-1?show-optional-panel=false generates the same caching key:

/my-app/profiles/user-1:None

This means that all changes to the page brought by the query get cached in the same location.


Do you still have questions?

Our Community Forum is full of helpful information and Anvil experts.