Skip to main content
Class for creating multi-select parameter widgets that allow users to choose multiple options from a list. This class can be imported from the squirrels.parameters or the squirrels module.

Factory methods

Factory methods are class methods that create and configure parameter instances. These methods are typically used in the pyconfigs/parameters.py file to create the parameter configurations (which describes the “shape” of the parameter but does not include the realtime user selections).

create_simple()

Decorator for creating a simple parameter from a function that returns select options. The decorated function must return a list of SelectParameterOption objects.
@classmethod
def create_simple(
    cls, name: str, label: str, 
    *, description: str = "", show_select_all: bool = True, 
    order_matters: bool = False, none_is_all: bool = True
) -> Callable:

create_with_options()

Decorator for creating a parameter with options that can vary based on user attributes or parent parameter selections. The decorated function must return a list of SelectParameterOption objects. This is functionally equivalent to create_simple but with additional arguments available for user_attribute and parent_name.
@classmethod
def create_with_options(
    cls, name: str, label: str, 
    *, description: str = "", show_select_all: bool = True, 
    order_matters: bool = False, none_is_all: bool = True,
    user_attribute: str | None = None, parent_name: str | None = None
) -> Callable:

create_from_source()

Decorator for creating a parameter populated from a database table or query using a SelectDataSource. The decorated function must return a SelectDataSource object.
@classmethod
def create_from_source(
    cls, name: str, label: str, 
    *, description: str = "", show_select_all: bool = True, 
    order_matters: bool = False, none_is_all: bool = True,
    user_attribute: str | None = None, parent_name: str | None = None
) -> Callable:

Instance methods

Instance methods are available on parameter instances at query time (in context.py or data models) to retrieve selected values.

get_selected_list() -> Sequence[SelectParameterOption]

Gets the sequence of selected SelectParameterOption objects.
def get_selected_list(self) -> Sequence[SelectParameterOption]:
returns
Sequence[SelectParameterOption]
A sequence of selected SelectParameterOption objects.

get_selected_list() -> Sequence[Any]

Gets the sequence of selected custom fields.
def get_selected_list(
    self, field: str, 
    *, default_field: str | None = None, default: Any | None = None
) -> Sequence[Any]:
returns
Sequence[Any]
A sequence of custom field values, or an empty sequence if no options are selected (unless none_is_all is True, in which case all options are returned).

get_selected_ids_as_list()

Gets the sequence of ID(s) of the selected option(s).
def get_selected_ids_as_list(self) -> Sequence[str]:
returns
Sequence[str]
A sequence of ID strings of the selected options.

has_non_empty_selection()

Returns True if more than zero options were selected. False otherwise. Note that even when this returns False, all methods starting with “get_selected” would return the full list of options if none_is_all is set to True.
def has_non_empty_selection(self) -> bool:
returns
bool
True if at least one option is selected, False otherwise.

is_enabled()

Returns whether the parameter is enabled. A parameter is enabled if it has at least one selectable option.
def is_enabled(self) -> bool:
returns
bool
True if the parameter has at least one selectable option, False otherwise.

Examples for factory methods

All examples below are defined in the pyconfigs/parameters.py file.

Using create_simple for basic multi-select

For parameters that don’t need user-specific or parent-dependent options, you can use create_simple.
from squirrels import parameters as p, parameter_options as po

@p.MultiSelectParameter.create_simple(
    name="status_filters", 
    label="Order Status",
    description="Filter orders by status"
)
def status_filters_options() -> list[po.SelectParameterOption]:
    return [
        po.SelectParameterOption(id="pending", label="Pending"),
        po.SelectParameterOption(id="processing", label="Processing", is_default=True),
        po.SelectParameterOption(id="shipped", label="Shipped", is_default=True),
        po.SelectParameterOption(id="delivered", label="Delivered"),
        po.SelectParameterOption(id="cancelled", label="Cancelled"),
    ]

Cascading multi-select with parent dependency

This example shows how multi-select options can change based on a parent parameter selection.
from squirrels import parameters as p, parameter_options as po

# Parent parameter
@p.SingleSelectParameter.create_with_options(
    name="department", 
    label="Department",
    description="Select a department"
)
def department_options():
    return [
        po.SelectParameterOption(id="sales", label="Sales", is_default=True),
        po.SelectParameterOption(id="engineering", label="Engineering"),
    ]

# Child multi-select with cascading options
@p.MultiSelectParameter.create_with_options(
    name="team_members", 
    label="Team Members",
    description="Select team members from the chosen department",
    parent_name="department"
)
def team_members_options():
    return [
        po.SelectParameterOption(
            id="alice", 
            label="Alice Johnson",
            parent_option_ids="sales"
        ),
        po.SelectParameterOption(
            id="diana", 
            label="Diana Chen",
            parent_option_ids="engineering"
        ),
    ]

User-specific multi-select options

This example shows different options based on user access levels.
from squirrels import parameters as p, parameter_options as po

@p.MultiSelectParameter.create_with_options(
    name="report_sections", 
    label="Report Sections",
    description="Select which sections to include in the report",
    user_attribute="access_level"
)
def report_sections_options():
    return [
        po.SelectParameterOption(
            id="summary", 
            label="Executive Summary",
            is_default=True,
            user_groups=["admin", "member", "guest"]
        ),
        po.SelectParameterOption(
            id="metrics", 
            label="Key Metrics",
            user_groups=["admin", "member", "guest"]
        ),
        po.SelectParameterOption(
            id="financial", 
            label="Financial Details",
            user_groups=["admin", "member"]
        ),
    ]
If custom user fields are defined in pyconfigs/user.py, then they can be used to restrict visibility of parameter options as well. To do so, the user_attribute argument must be prefixed with custom_fields..
from squirrels import parameters as p, parameter_options as po

@p.MultiSelectParameter.create_with_options(
    name="report_sections", 
    label="Report Sections",
    description="Select which sections to include in the report",
    user_attribute="custom_fields.role"
)
def report_sections_options():
    return [
        po.SelectParameterOption(
            id="summary", 
            label="Executive Summary",
            user_groups=["manager", "staff"]
        ),
        po.SelectParameterOption(
            id="financial", 
            label="Financial Details",
            user_groups=["manager"]
        ),
    ]

Multi-select from database source

This example populates multi-select options from a database table.
from squirrels import parameters as p, data_sources as ds

@p.MultiSelectParameter.create_from_source(
    name="store_locations", 
    label="Store Locations",
    description="Select one or more store locations"
)
def store_locations_source() -> ds.SelectDataSource:
    return ds.SelectDataSource(
        table_or_query="""
            SELECT 
                store_id,
                store_name,
                is_flagship AS is_default
            FROM stores
            WHERE is_active = 1
            ORDER BY store_name
        """,
        id_col="store_id",
        options_col="store_name",
        is_default_col="is_default"
    )

Cascading multi-select from database

This example shows a multi-select that depends on a parent parameter, both populated from database sources.
from squirrels import parameters as p, data_sources as ds

# First define the parent parameter
@p.SingleSelectParameter.create_from_source(
    name="region", 
    label="Region",
    description="Select a region"
)
def region_source():
    return ds.SelectDataSource(
        table_or_query="regions",
        id_col="region_id",
        options_col="region_name"
    )

# Child multi-select that cascades based on region
@p.MultiSelectParameter.create_from_source(
    name="stores_in_region", 
    label="Stores in Region",
    description="Select stores within the chosen region",
    parent_name="region"
)
def stores_in_region_source():
    return ds.SelectDataSource(
        table_or_query="""
            SELECT 
                store_id,
                store_name,
                region_id
            FROM stores
            WHERE is_active = 1
            ORDER BY store_name
        """,
        id_col="store_id",
        options_col="store_name",
        parent_id_col="region_id"
    )

Examples for instance methods

Once parameters are configured, you can use instance methods in your models to access the selected values. The parameter instances are available through the context object (e.g., sqrl.prms).

Basic usage in context.py

from squirrels import ContextArgs

def main(ctx: dict[str, Any], sqrl: ContextArgs) -> None:
    if sqrl.param_exists("categories"):
        categories_param = sqrl.prms["categories"]
        assert isinstance(categories_param, p.MultiSelectParameter)
        ctx["category_ids"] = categories_param.get_selected_ids_as_list()
        ctx["category_filter"] = categories_param.get_selected_ids()

Basic usage in Jinja SQL models

The following example works but is not recommended. See tip below for why.
-- models/federates/filtered_products.sql
SELECT *
FROM products
WHERE category_id IN ({{ prms["categories"].get_selected_ids() | quote_and_join }})
To omit the WHERE clause entirely when no options are selected, you can do so as follows:
-- models/federates/filtered_products.sql
SELECT *
FROM products
{%- if prms["categories"].has_non_empty_selection() %}
WHERE category_id IN ({{ prms["categories"].get_selected_ids() | quote_and_join }})
{%- endif %}
It is generally better to only use the instance methods in context.py to transform parameter selections into context variables. Using the instance methods directly in the data models is not recommended.IDEs can provide code suggestions for the available instance methods in Python instead of having to memorize which method (such as get_selected_ids_as_list) is available to use for MultiSelectParameter objects.

Accessing custom fields

Suppose you define a multi-select parameter with custom fields as such:
# In pyconfigs/parameters.py
@p.MultiSelectParameter.create_with_options(
    name="metric_filters", 
    label="Metrics to Display"
)
def metric_filters_options():
    return [
        po.SelectParameterOption(
            id="revenue", 
            label="Revenue",
            column_name="total_revenue",
            aggregation="SUM"
        ),
        po.SelectParameterOption(
            id="orders", 
            label="Order Count",
            column_name="order_count",
            aggregation="COUNT"
        ),
    ]
Then you can access the custom fields in context.py as follows:
# In context.py
def main(ctx: dict[str, Any], sqrl: ContextArgs) -> None:
    if sqrl.param_exists("metric_filters"):
        metric_param = sqrl.prms["metric_filters"]
        assert isinstance(metric_param, p.MultiSelectParameter)

        columns = metric_param.get_selected_list("column_name")
        ctx["metric_columns"] = columns