|Version 27 (modified by 10 years ago) ( diff ),|
Component resources are an S3 framework concept to simplify the implementation of and access to aggregation models, which is the most common type of data models used in Sahana-Eden.
Typically, such an aggregation model has a master entity and a number of associated (sub-)entities ("have"-relationships):
- organisations = have => offices, projects, teams
- persons = have => addresses, identities
- groups = have => members, tasks
Each of these relationships can be represented by a join between the respective tables.
The "Component Resource" extension provides the functionality to pre-configure and re-use these joins as pseudo-attributes ("components") of the master entity, thereby replacing the joins by simple identifiers (aliases) which can be used to construct queries, e.g. in URLs:
# Join "office": org_organisation <-- id/organisation_id --> org_office /org/organisation/5/office
# Join "contact": pr_person <-- pe_id/pe_id --> pr_contact /pr/person?contact.contact_method=EMAILfirstname.lastname@example.org
The "Component Resource" extension is one of the fundamental RAD concepts of the S3 framework. Besides simplified construction of queries, views and self-processing forms it is also one of the fundamental premises for the implicit constraints in S3XML which simplify the mapping and transformation from and to other data models using XSLT.
The following diagram describes the joins which are currently supported:
Note that, for efficiency reasons, "components" can not be nested in queries.
Components can be bound to their master records via link-tables. In such cases, the foreign key constraints for the component link are all in a separate link-table, whereas both the master table and the component table are completely independent:
master (primary key) <==== (foreign key) link table (foreign key) ====> (primary key) component
Link-table component links have some advantages over simple foreign key constraints:
- they can carry attributes of their own (attributed link)
- they provide the option to bind the same component record to multiple master records (many-to-many)
- there are several different ways to actuate such links
- link-table links work both ways (i.e. with master/component exchanged, can be declared both ways at the same time)
However, they do have disadvantages too:
- overhead to maintain a separate database table (processing time, migration issues etc.)
- increased complexity to access and query resources (3 tables instead of 2)
- increased complexity to handle such links in CRUD and XML/XSLT
Link Actuation Options
The RESTful methods can handle the link-table in a number of different ways, depending on the CRUD method and the configured option:
- replace: hides the link table and always operates on the component table
- hide: hides the component table and always operates on the link table
- link: operates on the component table for single-record requests, and on the link table for summary requests (=without record ID) and delete
- embed: operates on the link table, embeds the component record in single-record requests
The following table gives an overview of link actuation in S3CRUD:
|CRUD Method||Link Actuation Option|
|create||create-form for component||create-form for link||create-form for link||create-form for link with component embedded|
|read||read-view of component||read-view of link||read-view of component||read-view of link (with component embedded2)|
|update||update-form for component||update-form for link||update-form for component||update-form for link with component embedded|
|delete||deletes both, component and link||deletes the link||deletes the link1||deletes the link1|
|list||list view of component||list view of link||list view of link||list view of link (with component embedded2)|
- 1 = deletes the component together with the last link if autodelete option is set
- 2 = not implemented yet
Other RESTful methods such as S3Search or S3Report may have their own definitions.
The basic syntax of a link-table component link declaration is:
s3db.add_component("my_component", # Tablename of the component my_master=dict( # Tablename of the master table name="alias", # Use this 'alias' as the component name link="my_linktable", # Tablename of the link table joinby="fieldname", # FK of the master table (=left key constraint) key="fieldname", # FK of the component table (=right key constraint) actuate="replace", # Actuation option (see above, optional, defaults to "link") autodelete=False # Delete the component record together with the last link (optional, default is False) widget=Widget, # Widget to use for embedding (optional, defaults to S3EmbedComponentWidget) autocomplete="fieldname", # Field in the component to use for autocomplete when embedding the component ))
If no field is defined for autocomplete, then no autocomplete-widget will be used, but a standard SELECT of options for key (default behavior).
Important: if you specify a widget for embedding (e.g. S3AddPersonWidget), then you must ensure that the foreign key in the link-table doesn't also use either this widget or any other widget validator!
name='alias' is specified, the component would not be addressed in the URL as
master/component but as
master/alias. This allows to link the same component table to the same master table more than once using different link tables.