Field Selectors
Table of Contents
Field selectors are a string syntax to specify fields in an S3Resource.
Basic Syntax
The fundamental syntax of a field selector is:
<path><fieldname>
The path specifies the table that contains the field.
Fields in the Master Table
In the simplest case, the path is just the master table of the resource which is represented by the tilde followed by a period:
~.<fieldname>
The master table path is mandatory only in URLs but can otherwise be omitted, i.e. the simplest field selector is simply the field name.
It is also possible to use the name of the master table without module prefix (e.g. "organisation" for org_organisation) instead of the tilde to refer to the master table.
Fields in Components
If the field is in a component resource, then the path is the alias of that component followed by a period:
<alias>.<fieldname>
By default, the alias is the name of the component table without its module prefix (e.g. "contact" for pr_contact).
The alias can also be set explicitly in add_component (e.g. for filtered components).
If the alias can not be resolved, an AttributeError will be raised.
Fields in Referenced Tables
If the field is in a table that is referenced by a foreign key in the master table, then this can be expressed like:
~.<foreign_key>$<fieldname>
The $ sign indicates that the field is in the table referenced by this foreign key field.
The foreign key field must have a real foreign key constraint, i.e. it must not be a list:reference or an integer pseudo-reference.
Similar, if the foreign key is in a component:
<alias>.<foreign_key>$<fieldname>
Fields in Linked Tables
If the field is in a table that references the master table, but is not a component of it, this can be expressed like:
~.<foreign_key>:<linked_table>.<field>
In this case, the <foreign_key> is the field in <linked_table> that references the master table (i.e. the so-called "left key").
Similar, if the linked table references a component instead of the master table:
<alias>.<foreign_key>:<linked_table>.<field>
Combinations
It is possible to chain path expressions. Every part of the path is relative to the table specified by any previous parts:
~.<foreign_key>$<alias>.<fieldname>
...specifies that the field is in a component <alias> of the table referenced by <foreign_key>
<alias>.<foreign_key1>$<foreign_key2>$<fieldname>
...specifies a field in table that is referenced by <foreign_key2> in a table that is referenced by <foreign_key1> in a component <alias> of the master resource.
One should though keep in mind that using multi-table paths requires multi-table joins or multiple sub-queries when filtering data. It should therefore be avoided (e.g. through better modelling) where possible, especially where performance is critical.
Context Paths
Context paths allow to use the same filter expression for multiple resources even if they have different relationships to the target field.
Consider the following case:
For project_project, the location reference is in a component project_location:
location.location_id$name
For org_office, the location reference is in the master table:
~.location_id$name
Obviously, the field specified by both selectors is the same - just the path to the field is specific for each resource. Due to these different paths, though, filter queries with either selector can not be re-used for the other resource:
# Filter query specific for project_project query = S3FieldSelector("location.location_id$name") == "Example" # Filter query specific for org_office query = S3FieldSelector("~.location_id$name") == "Example"
To overcome this, we can configure the resource-specific part of the path as "context" path, using s3db.configure:
# Configure the "location" context for project_project: s3db.configure("project_project", context={"location": "location.location_id"}, ) # Configure the "location" context for org_office: s3db.configure("org_office", context={"location": "~.location_id"}, )
Now we can use this "location" context to replace the resource-specific part of the path:
(location)$name
Through this, we can re-use the filter query for both resources:
# Filter expression valid for both project_project and org_office: query = S3FieldSelector("(location)$name") == "Example"
Only when the field selector gets resolved against each resource (e.g. during add_filter), the "(location)" part is translated according to the respective "context" configuration.
It is possible to include the target field in the context expression:
E.g. in this example, if we wanted to filter by location_id, then "(location)" would be a valid field selector for both resources:
# Filter expression valid for both project_project and org_office: query = S3FieldSelector("(location)") == 43L
Context paths can be used for various purposes, e.g.
- profile pages which show multiple resources all in the "context" of the same primary record, e.g. all offices, projects, etc related to the same location (=primary record)
- filter forms across multiple resources (dashboards, not implemented yet)
- global filters for all pages (s3db.context)