|Version 25 (modified by 8 years ago) ( diff ),|
Table of Contents
Sometimes it is useful to link the same component to multiple master entities without introducing a separate foreign key constraint for each master.
Example: both warehouses and offices have contact persons. Instead of creating separate warehouse_contact and office_contact components, you can make use of the super-entity concept to link a single site_contact component to both entities.
To achieve this, S3 supports a special type of key tables called Super-Entities (referring to the fact that they constitue a generalization concept):
A super-entity stores (tablename, uuid) pairs which identify records across multiple tables, and the primary key of the super-entity (super-key) can then be used for foreign key constraints to link components. The primary key of the super-entity is usually referred to as super-key, and the respective foreign key constraints are called super-link.
The super-entity concept is implemented in S3Model, and supported by both S3CRUD and S3Import. Custom methods can make use of the current.s3db API to handle super-entities as described below.
To simplify the handling of super-entities in the model, S3Model provides a unified API for super-entities:
Defining a Super-Entity
To define a super-entity, you can use the
table = s3db.super_entity(tablename, "sit_id", situation_types, Field("datetime", "datetime"), # shared field location_id()) # shared field
You can define so-called shared fields in the super-entity, which are mirrored from the respective resource record. This allows to easily access these data from the component without the need to involve the primay instance table in the backward-join:
In case the names of the shared fields in your instance table differ from those of the super-entity, you can define a field mapping:
Defining an Instance of a Super-Entity
To make a table an instance of a super-entity, you can use
s3db.configure(table, super_entity = db.sit_situation)
By default, all fields that the table and the super-entity have in common will be mirrored in the super-entity ("shared fields"). You can override this by specifying a list of fields to be mirrored by the super-entity.
s3db.configure(table, super_entity = db.sit_situation, sit_situation_fields = ["datetime"])
In case your table uses different names for the shared fields, you can use a dict instead to specify a mapping:
s3db.configure(table, super_entity = db.sit_situation, sit_situation_fields = dict(datetime="timestmp", location_id="location_id"))
Linking to a Super-Entity
Both, instance tables as well as shared components need to be linked to the super-entity. This can be done by inserting a super-key field into the instance/component table which must have the same name as the primary key of the super-entity. You can use the
super_link() function as a DRY method for this:
tablename = "pr_presence" table = db.define_table(tablename, super_link("pe_id", "pr_pentity"), super_link("sit_id", "sit_situation"), # sit_id ...)
super_link generates a
Field instance - if you just need the name of the super-key field, you can use:
sk = s3db.super_key(db.pr_pentity) # returns the string "pe_id"
Updating a Super-Entity
If you use s3_rest_controller() for CRUD, it will automatically create, update and delete super-entities as necessary.
Otherwise, or if you manipulate records outside s3_rest_controller, you have to update all super-entities which an instance table implements whenever you create, update or delete a record in that table.
To do this, you can use:
table is the instance table and
record the newly created/updated record.
In case you're going to delete a record from an instance table, you may use:
before you delete the record, where
table is the instance table and
record the record to be deleted.