= Super-Entities = [[TOC]] == Introduction == 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): [[Image(super_entity.png)]] 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. == API == To simplify the handling of super-entities in the model, S3Model provides a unified API for super-entities: === Defining a Super-Entity === A super-entity can be defined using the {{{super_entity()}}} method of S3Model: {{{#!python class MyModel(S3Model): def model(self): # Define a dict with nice names for the instance types: my_instance_types = { "my_instance_tablename": current.T("Nice name for the instance type"), } # Define the super-entity tablename = "my_super_entity" table = self.super_entity(tablename, "my_super_id", # specify the super ID my_instance_types, # specify the instance types Field("start_date", "datetime"), # shared field location_id()) # shared field }}} Shared fields will mirror the values from the respective instance record, which allows easy access without need to join multiple tables. If the names of shared fields are different in certain instance tables, you can define a field mapping when you configure the instance table: {{{#!python # Define the instance table tablename = "my_instance_table" table = self.define_table(tablename, Field("first_date", "datetime"), Field("last_date", "datetime"), *s3_meta_fields()) # Configure the instance table self.configure("my_instance_table", # Configure the super entity super_entity = "my_super_entity", # Map the "first_date" field in my_instance_table to "start_date" in my_super_entity my_super_entity_fields = {"start_date": "first_date"}, ) }}} === Defining an Instance of a Super-Entity === To make a table an instance of a super-entity, you can use {{{s3db.configure()}}}: {{{ 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 ...) }}} Note: {{{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 [wiki:S3XRC/RESTfulAPI/s3_rest_controller 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: {{{ s3db.update_super(table, record) }}} where {{{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: {{{ s3db.delete_super(table, record) }}} before you delete the record, where {{{table}}} is the instance table and {{{record}}} the record to be deleted. ---- DeveloperGuidelines