wiki:S3/FilterForms

S3 FilterForms

Filter forms allow the user to apply filters to interactive views (e.g. data tables, data lists, reports etc.).

Framework

The framework for filter forms consists of 4 core elements:

Client-side:

  • filter widgets to facilitate user input of filter criteria
  • a filter script to collect all values from filter widgets and convert them into a URL query

Server-side:

  • the REST interface to interpret and apply the URL query as resource filter
  • a filter form builder to render the filter widgets and populate them with the values from the URL query

Filter Forms Framework

URL Queries

Filter Widgets

General

All filter widgets are subclasses of the S3FilterWidget class. This base class only defines the API, i.e. it does not render a filter widget - it's only used here to describe the common API of all filter widget classes.

For the configuration, a widget instance is created like:

instance = S3FilterWidget(field=None, **attr)
  • field is the field selector (list of field selectors) for the field(s) to filter by (required)
  • attr are keyword attributes for the widget, where those starting with an underscore are the HTML attributes, while all others are widget options (see the particular widget for details)

The widget is then rendered by calling it:

widget = instance(resource, get_vars=None, alias=None)
  • resource is the resource to filter (required)
  • get_vars is the dict of GET vars from the previous request (used to populate the widget, required but can be empty)
  • alias is the component alias to use for the URL query (optional, needed if resource is not the master resource of the request)

The base class S3FilterWidget and all its subclasses are defined in modules/s3/s3filter.py.

S3TextFilter

S3TextFilter renders a text input field to filter text fields in the resource for strings:

Configuration:

instance = S3TextFilter(field, 
                        label=None,
                        _size=40,
                        _class="class",
                        _name="name", 
                        comment=None)

Parameters:

ParameterTypeExplanationDefault
fieldstring or list of stringsfield selector (or a list of field selectors) for the field(s) to filter by (required)-
labelstring or lazyTthe label for the filter widgetNone
_sizeintegerthe input field width in characters40
_classstringthe CSS class for the input fieldNone
_namestringname for the input fieldNone
commentstring or HTML widgethelp text or tooltip widget to render with the filter widget (only rendered by S3FilterForm)None

Note: if multiple strings are entered by the user, each of them (AND) must appear in any of the fields (OR) specified by the field selectors. All fields must be type "string" or "text".

S3OptionsFilter

S3OptionsFilter renders checkboxes to select pre-determined options for a field in the resource:

(Example with "multiselect" widget)

Configuration:

instance = S3OptionsFilter(field,
                           label=None,
                           options=None,
                           represent=None,
                           widget=None,
                           _class="class",
                           _name="name",
                           comment=None)

Parameters:

ParameterTypeExplanationDefault
fieldstringfield selector for the field to filter by (required)-
labelstring or lazyTthe label for the filter widgetNone
optionsdict or callabledict {value:label} of options for the widget, or a callable that returns such a dictNone
representcallablea callable to render a label for an optionNone
widgetstringselects the widget, either "groupedopts" (S3 grouped checkboxes) or "multiselect" (jQuery UI Multiselect-Dropdown by Eric Hynds) or "multiselect-bootstrap" (Bootstrap Multiselect-Dropdown)"groupedopts"
_classstringthe CSS class for the input fieldNone
_namestringname for the input fieldNone
commentstring or HTML widgethelp text or tooltip widget to render with the filter widget (only rendered by S3FilterForm)None

Note: if no options are specified, the widget will determine the options from the field (=all unique values for this field currently available in the DB) Note: if no represent is specified, the widget will use the labels specified in options, or otherwise use the field representation function Note: if the field is a foreign key, then represent can also be a string template for the referenced rows

S3HierarchyFilter

S3HierarchyFilter presents filter options as hierarchical widget:

Configuration:

instance = S3HierarchyFilter(field,
                             label=None,
                             lookup=tablename,
                             represent=represent,
                             multiple=True,
                             leafonly=True,
                             )

Parameters:

ParameterTypeExplanationDefault
fieldstringfield selector for the field to filter by (required)-
labelstring or lazyTthe label for the fitler widgetNone
lookupstringname of the hierarchical lookup tableinferred from field
representcallablerepresentation function for the optionsinferred from field
multiplebooleanallow selection of multiple optionsTrue
leafonlybooleanonly leaf nodes in the hierarchy can be selectedTrue

S3DateFilter

S3DateFilter renders two input fields for a date/date+time range, each using a calendar widget for input:

Configuration:

instance = S3DateFilter(field,
                        label=None,
                        hide_time=False,
                        _class="class",
                        comment=None)

Parameters:

ParameterTypeExplanationDefault
fieldstringfield selector for the field to filter by (required)-
labelstring or lazyTthe label for the fitler widgetNone
hide_timebooleanhide the time slider (...and only choose a date even if it's a datetime field)False
_classstringthe CSS class for the input fieldNone
commentstring or HTML widgethelp text or tooltip widget to render with the filter widget (only rendered by S3FilterForm)None

Filter Forms

Configuration of Filter Forms in S3CRUD

Standard List Views

S3CRUD has filter forms integrated in the standard list view (=multi-record request w/o URL method).

The filter form can be suppressed by a keyword argument to s3_rest_controller():

output = s3_rest_controller(prefix, resourcename,
                            hide_filter=True        # hide filter form in list views
                           )

hide_filter can be overridden in the URL by:

  • /datatable resp. /datalist method: always renders the view without the filter form
  • /datatable_f resp. /datalist_f method: always render the view with the filter form

Configuration

For pages with filter forms, the filter widgets are configured per resource as a list of filter widget instances in the "filter_widgets"-setting like:

s3db.configure(tablename,
               filter_widgets=[
                 S3TextFilter(["name", "email", "comments"],
                              label=T("Search"),
                              comment=T("Search for office by text.")),
                 S3OptionsFilter("organisation_id",
                                 label=messages.ORGANISATION,
                                 comment=T("Search for office by organization."),
                                 represent="%(name)s",
                                 widget="multiselect"),
                 S3OptionsFilter("location_id$L1",
                                 location_level="L1",
                                 widget="multiselect")
               ])

The widgets appear in the form in list order.

Controller Arguments

Additionally, there are a number of controller options for the s3_rest_controller call:

output = s3_rest_controller(prefix, resourcename,
                            filter_submit_url = submit_url,
                            filter_ajax_url = ajax_url,
                            filter_formstyle = formstyle,
                            filter_submit = submit,
                            hide_filter = hide_filter,
                            )

Options:

OptionTypeFunctionDefault
filter_submit_urlURL stringwhere to request filtered data fromURL of the current call
filter_ajax_urlURL stringwhere to request updated filter options fromURL of the current call with /filter method
filter_formstyleFormstyle functionAlternative formstyle for the filter formbuilt-in formstyle
filter_submitStringLabel for the submit button or tuple of (label, class) to also specify a CSS class for the buttonT("Search")
hide_filterBooleanWhether to show filter_widgetsTrue for Master, False for Components

Note: if formstyle returns a TR, a list of TRs or a TAG[""] of TRs, then the filter form will be rendered as a TABLE - otherwise as DIV.

Default Filters

A FilterForm can be instructed to set it's status to default values for the convenience of interactive users. This allows their initial view to be filtered, yet allow them to unfilter to whatever their security permissions, or any other 'hard' filter allows.

To instruct a FilterForm to NOT implement the default filters, add ?default_filters=0 to the URL.

Example: Filter Staff by a User's (Branch) Organisation, add this to the controller:

def user_org_default_filter(selector, tablename=None):
    auth = current.auth
    user_org_id = auth.is_logged_in() and auth.user.organisation_id
    if user_org_id:
        return user_org_id
    else:
        # no default
        return {}

from s3 import s3_set_default_filter
s3_set_default_filter("~.organisation_id",
                      user_org_default_filter,
                      tablename = "hrm_human_resource")

Development

Components

  • tbw

Python Classes

  • tbw

JavaScript Methods

  • tbw

Implementing Filter Widgets

  • Widgets should ensure that any defaults are present in their displayed options, as otherwise it is confusing to have a filter which cannot be seen/removed.
  • tbw

Using Filter Forms in REST Method Handlers

Filter forms can be rendered by any method handler. Since filter forms will never be submitted, the method handler does not need to handle any subsequent POSTs related to the filter form, but just insert the filter form HTML into the page.

A filter form can be generated using the S3FilterForm class:

from s3filter import S3FilterForm
filter_form = S3FilterForm(widgets, **attr)

Parameters:

ParameterTypeExplanation
widgetslist of S3FilterWidget instancesthe widgets for the filter form (required)
attr Options and HTML attributes for the filter form

Options and HTML attributes:

ParameterTypeExplanationDefault
formstylefunctiona function to render a widget row, must accept (row_id, label, widget, comment) as parameter listS3FilterForm._formstyle()
submitboolean or the button label as string/lazyT or a tuple (label, HTML class)whether to render a submit button in the filter form and how, with False or None no submit button will be renderedFalse (if True, then the label defaults to T("Search"))
ajaxbooleanwhether to Ajax-update the target object (True, also requires the target to be specified in the html() call) or re-load the page (False)False
urlstringwhere to load the filtered data from, if ajax=True then this URL should link to the Ajax method of the target objectURL of the request
ajaxurlstringwhere to load option updates for filter widgets from, this URL must link to the S3Filter method handlerURL of the request + /filter.json method
_classstringthe CSS class for the filter form (HTML attribute)"filter-form"
_idstringthe HTML element ID for the filter form (HTML attribute)None

The filter form can then be rendered using its .html() method:

html_output = filter_form.html(resource, get_vars, target=None, alias=None)

Parameters:

ParameterTypeExplanation
resourceS3ResourceThe resource to render the filter form for. Important: do not just use the r.resource instance here (because it's coming with pre-filtered options), instead create a new instance using s3db.resource(r.resource.tablename) (required) -
get_varsdictthe GET vars (=URL query vars) to populate the filter widgets from (required) -
targetstringthe HTML element ID of the target object (e.g. a datalist or datatable) (required if the form is set to ajax=True), or a string with multiple target IDs separated by blanks (make sure all targets have their ids set, e.g. "list_id"-parameter for datatables/datalists)None
aliasstringthe table alias to use in URL queries (required if the filtered resource is not the master resource of the request URL, e.g. a component)None

Using Filter Forms in Custom Controllers

Whilst is is possible to have the Form control a list/table which uses the same custom controller to retrieve ajax data, it is generally easier to use the native controllers. Just be sure then to have the same configuration in the native controller as the custom one. This can be achieved by using customise_resource to do all the configuration & then call this and then read this configuration in the custom controller. If-required, can send an extra var from the custom controller to allow customise_ to differentiate.

When the Filter Form is used to control a dataList:

  1. Ensure that Filter options update when entries are added/modified by prefixing the form's HTML id by the datalist's list_id:
filter_form = S3FilterForm(...
                           _id = "%s-filter-form" % list_id,
                           )
  1. Specify the list_id for the data list and add it to the target of the filter form:
list_id = "my-datalist"

datalist, numrows, ids = resource.datalist(...,
                                           list_id = list_id,
                                           ...
                                           )
output["filter_form"] = filter_form.html(..., 
                                         target = list_id,
                                         ...
                                         )
  1. Make sure the datalist retains its HTML-id during ajax-refreshes (so that the filter form can still find it), by adding the list_id parameter to the ajaxurl too:
ajax_vars = {"list_id": list_id,
             }
data = datalist.html(ajaxurl = URL(c = module, 
                                   f = resource, 
                                   args = "datalist",
                                   vars = ajax_vars, 
                                   extension = "dl",
                                   ),
                     )

Last modified 4 years ago Last modified on 05/14/21 13:09:47

Attachments (5)

Download all attachments as: .zip

Note: See TracWiki for help on using the wiki.