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
.
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.