core.bones.relational

This module contains the RelationalBone to create and manage relationships between skeletons and enums to parameterize it.

Module Contents

Classes

RelationalConsistency

An enumeration representing the different consistency strategies for handling stale relations in

RelationalUpdateLevel

An enumeration representing the different update levels for the RelationalBone class.

RelationalBone

The base class for all relational bones in the ViUR framework.

class core.bones.relational.RelationalConsistency

Bases: enum.Enum

An enumeration representing the different consistency strategies for handling stale relations in the RelationalBone class.

Ignore = 1

Ignore stale relations, which represents the old behavior.

PreventDeletion = 2

Lock the target object so that it cannot be deleted.

SetNull = 3

Drop the relation if the target object is deleted.

CascadeDeletion = 4

Warning

Delete this object also if the referenced entry is deleted (Dangerous!)

class core.bones.relational.RelationalUpdateLevel

Bases: enum.Enum

An enumeration representing the different update levels for the RelationalBone class.

Always = 0

Always update the relational information, regardless of the context.

OnRebuildSearchIndex = 1

Update the relational information only when rebuilding the search index.

OnValueAssignment = 2

Update the relational information only when a new value is assigned to the bone.

class core.bones.relational.RelationalBone(*, consistency=RelationalConsistency.Ignore, format='$(dest.name)', kind=None, module=None, parentKeys={'name'}, refKeys={'name'}, updateLevel=RelationalUpdateLevel.Always, using=None, **kwargs)

Bases: viur.core.bones.base.BaseBone

The base class for all relational bones in the ViUR framework. RelationalBone is used to create and manage relationships between database entities. This class provides basic functionality and attributes that can be extended by other specialized relational bone classes, such as N1Relation, N2NRelation, and Hierarchy. This implementation prioritizes read efficiency and is suitable for situations where data is read more frequently than written. However, it comes with increased write operations when writing an entity to the database. The additional write operations depend on the type of relationship: multiple=True RelationalBones or 1:N relations.

The implementation does not instantly update relational information when a skeleton is updated; instead, it triggers a deferred task to update references. This may result in outdated data until the task is completed.

Note: Filtering a list by relational properties uses the outdated data.

Example: - Entity A references Entity B. - Both have a property “name.” - Entity B is updated (its name changes). - Entity A’s RelationalBone values still show Entity B’s old name.

It is not recommended for cases where data is read less frequently than written, as there is no write-efficient method available yet.

Parameters:
  • kind (str) – KindName of the referenced property.

  • module (Optional[str]) – Name of the module which should be used to select entities of kind “type”. If not set, the value of “type” will be used (the kindName must match the moduleName)

  • refKeys (Optional[Iterable[str]]) – A list of properties to include from the referenced property. These properties will be available in the template without having to fetch the referenced property. Filtering is also only possible by properties named here!

  • parentKeys (Optional[Iterable[str]]) – A list of properties from the current skeleton to include. If mixing filtering by relational properties and properties of the class itself, these must be named here.

  • multiple – If True, allow referencing multiple Elements of the given class. (Eg. n:n-relation). Otherwise its n:1, (you can only select exactly one). It’s possible to use a unique constraint on this bone, allowing for at-most-1:1 or at-most-1:n relations. Instead of true, it’s also possible to use a `class MultipleConstraints` instead.

  • format (str) –

    Hint for the frontend how to display such an relation. This is now a python expression evaluated by safeeval on the client side. The following values will be passed to the expression:

    • value

      The value to display. This will be always a dict (= a single value) - even if the relation is multiple (in which case the expression is evaluated once per referenced entity)

    • structure

      The structure of the skeleton this bone is part of as a dictionary as it’s transferred to the fronted by the admin/vi-render.

    • language

      The current language used by the frontend in ISO2 code (eg. “de”). This will be always set, even if the project did not enable the multi-language feature.

  • updateLevel (RelationalUpdateLevel) –

    Indicates how ViUR should keep the values copied from the referenced entity into our entity up to date. If this bone is indexed, it’s recommended to leave this set to RelationalUpdateLevel.Always, as filtering/sorting by this bone will produce stale results.

    param RelationalUpdateLevel.Always:

    always update refkeys (old behavior). If the referenced entity is edited, ViUR will update this entity also (after a small delay, as these updates happen deferred)

    param RelationalUpdateLevel.OnRebuildSearchIndex:

    update refKeys only on rebuildSearchIndex. If the referenced entity changes, this entity will remain unchanged (this RelationalBone will still have the old values), but it can be updated by either by editing this entity or running a rebuildSearchIndex over our kind.

    param RelationalUpdateLevel.OnValueAssignment:

    update only if explicitly set. A rebuildSearchIndex will not trigger an update, this bone has to be explicitly modified (in an edit) to have it’s values updated

  • consistency (RelationalConsistency) –

    Can be used to implement SQL-like constrains on this relation. Possible values are:
    • RelationalConsistency.Ignore

      If the referenced entity gets deleted, this bone will not change. It will still reflect the old values. This will be even be preserved over edits, however if that referenced value is once deleted by the user (assigning a different value to this bone or removing that value of the list of relations if we are multiple) there’s no way of restoring it

    • RelationalConsistency.PreventDeletion

      Will prevent deleting the referenced entity as long as it’s selected in this bone (calling skel.delete() on the referenced entity will raise errors.Locked). It’s still (technically) possible to remove the underlying datastore entity using db.Delete manually, but this must not be used on a skeleton object as it will leave a whole bunch of references in a stale state.

    • RelationalConsistency.SetNull

      Will set this bone to None (or remove the relation from the list in case we are multiple) when the referenced entity is deleted.

    • RelationalConsistency.CascadeDeletion:

      (Dangerous!) Will delete this entity when the referenced entity is deleted. Warning: Unlike relational updates this will cascade. If Entity A references B with CascadeDeletion set, and B references C also with CascadeDeletion; if C gets deleted, both B and A will be deleted as well.

  • using (Optional[viur.core.skeleton.RelSkel]) –

Initialize a new RelationalBone.

Parameters:
  • kind (str) – KindName of the referenced property.

  • module (Optional[str]) – Name of the module which should be used to select entities of kind “type”. If not set, the value of “type” will be used (the kindName must match the moduleName)

  • refKeys (Optional[Iterable[str]]) – An iterable of properties to include from the referenced property. These properties will be available in the template without having to fetch the referenced property. Filtering is also only possible by properties named here!

  • parentKeys (Optional[Iterable[str]]) – An iterable of properties from the current skeleton to include. If mixing filtering by relational properties and properties of the class itself, these must be named here.

  • multiple – If True, allow referencing multiple Elements of the given class. (Eg. n:n-relation). Otherwise its n:1, (you can only select exactly one). It’s possible to use a unique constraint on this bone, allowing for at-most-1:1 or at-most-1:n relations. Instead of true, it’s also possible to use a :class:MultipleConstraints instead.

  • format (str) –

    Hint for the frontend how to display such an relation. This is now a python expression evaluated by safeeval on the client side. The following values will be passed to the expression

    param value:

    The value to display. This will be always a dict (= a single value) - even if the relation is multiple (in which case the expression is evaluated once per referenced entity)

    param structure:

    The structure of the skeleton this bone is part of as a dictionary as it’s transferred to the fronted by the admin/vi-render.

    param language:

    The current language used by the frontend in ISO2 code (eg. “de”). This will be always set, even if the project did not enable the multi-language feature.

  • updateLevel (RelationalUpdateLevel) –

    Indicates how ViUR should keep the values copied from the referenced entity into our entity up to date. If this bone is indexed, it’s recommended to leave this set to RelationalUpdateLevel.Always, as filtering/sorting by this bone will produce stale results.

    param RelationalUpdateLevel.Always:

    always update refkeys (old behavior). If the referenced entity is edited, ViUR will update this entity also (after a small delay, as these updates happen deferred)

    param RelationalUpdateLevel.OnRebuildSearchIndex:

    update refKeys only on rebuildSearchIndex. If the referenced entity changes, this entity will remain unchanged (this RelationalBone will still have the old values), but it can be updated by either by editing this entity or running a rebuildSearchIndex over our kind.

    param RelationalUpdateLevel.OnValueAssignment:

    update only if explicitly set. A rebuildSearchIndex will not trigger an update, this bone has to be explicitly modified (in an edit) to have it’s values updated

  • consistency (RelationalConsistency) –

    Can be used to implement SQL-like constrains on this relation.

    param RelationalConsistency.Ignore:

    If the referenced entity gets deleted, this bone will not change. It will still reflect the old values. This will be even be preserved over edits, however if that referenced value is once deleted by the user (assigning a different value to this bone or removing that value of the list of relations if we are multiple) there’s no way of restoring it

    param RelationalConsistency.PreventDeletion:

    Will prevent deleting the referenced entity as long as it’s selected in this bone (calling skel.delete() on the referenced entity will raise errors.Locked). It’s still (technically) possible to remove the underlying datastore entity using db.Delete manually, but this must not be used on a skeleton object as it will leave a whole bunch of references in a stale state.

    param RelationalConsistency.SetNull:

    Will set this bone to None (or remove the relation from the list in case we are multiple) when the referenced entity is deleted.

    param RelationalConsistency.CascadeDeletion:

    (Dangerous!) Will delete this entity when the referenced entity is deleted. Warning: Unlike relational updates this will cascade. If Entity A references B with CascadeDeletion set, and B references C also with CascadeDeletion; if C gets deleted, both B and A will be deleted as well.

  • using (Optional[viur.core.skeleton.RelSkel]) –

type = 'relational'
kind
setSystemInitialized()

Set the system initialized for the current class and cache the RefSkel and SkeletonInstance.

This method calls the superclass’s setSystemInitialized method and initializes the RefSkel and SkeletonInstance classes. The RefSkel is created from the current kind and refKeys, while the SkeletonInstance class is stored as a reference.

Return type:

None

_getSkels()

Retrieve the reference skeleton and the ‘using’ skeleton for the current RelationalBone instance.

This method returns a tuple containing the reference skeleton (RefSkel) and the ‘using’ skeleton (UsingSkel) associated with the current RelationalBone instance. The ‘using’ skeleton is only retrieved if the ‘using’ attribute is defined.

Returns:

A tuple containing the reference skeleton and the ‘using’ skeleton.

Return type:

tuple

singleValueUnserialize(val)

Restore a value, including the Rel- and Using-Skeleton, from the serialized data read from the datastore.

This method takes a serialized value from the datastore, deserializes it, and returns the corresponding value with restored RelSkel and Using-Skel. It also handles ViUR 2 compatibility by handling string values.

Parameters:

val (str or dict) – A JSON-encoded datastore property.

Returns:

The deserialized value with restored RelSkel and Using-Skel.

Return type:

dict

Raises:

AssertionError – If the deserialized value is not a dictionary.

serialize(skel, name, parentIndexed)

Serialize the RelationalBone for the given skeleton, updating relational locks as necessary.

This method serializes the RelationalBone values for a given skeleton and stores the serialized values in the skeleton’s dbEntity. It also updates the relational locks, adding new locks and removing old ones as needed.

Parameters:
  • skel (SkeletonInstance) – The skeleton instance containing the values to be serialized.

  • name (str) – The name of the bone to be serialized.

  • parentIndexed (bool) – A flag indicating whether the parent bone is indexed.

Returns:

True if the serialization is successful, False otherwise.

Return type:

bool

Raises:

AssertionError – If a programming error is detected.

delete(skel, name)

Clear any outgoing relational locks when deleting a skeleton.

This method ensures that any outgoing relational locks are cleared when deleting a skeleton.

Parameters:
  • skel (SkeletonInstance) – The skeleton instance being deleted.

  • name (str) – The name of the bone being deleted.

Raises:

Warning – If a referenced entry is missing despite the lock.

postSavedHandler(skel, boneName, key)

Handle relational updates after a skeleton is saved.

This method updates, removes, or adds relations between the saved skeleton and the referenced entities. It also takes care of updating the relational properties and consistency levels.

Parameters:
  • skel (SkeletonInstance) – The saved skeleton instance.

  • boneName (str) – The name of the relational bone.

  • key (google.cloud.datastore.key.Key) – The key of the saved skeleton instance.

Raises:

Warning – If a relation entry is corrupt and cannot be updated.

postDeletedHandler(skel, boneName, key)

Handle relational updates after a skeleton is deleted.

This method deletes all relations associated with the deleted skeleton and the referenced entities for the given relational bone.

Parameters:
  • skel (SkeletonInstance) – The deleted skeleton instance.

  • boneName (str) – The name of the relational bone.

  • key (google.cloud.datastore.key.Key) – The key of the deleted skeleton instance.

isInvalid(key)

Check if the given key is invalid for this relational bone.

This method always returns None, as the actual validation of the key is performed in other methods of the RelationalBone class.

Parameters:

key – The key to be checked for validity.

Returns:

None, as the actual validation is performed elsewhere.

parseSubfieldsFromClient()

Determine if the RelationalBone should parse subfields from the client.

This method returns True if the using attribute is not None, indicating that this RelationalBone has a using-skeleton, and its subfields should be parsed. Otherwise, it returns False.

Returns:

True if the using-skeleton is not None and subfields should be parsed, False otherwise.

Return type:

bool

singleValueFromClient(value, skel, bone_name, client_data)

Load a single value from a client

Parameters:
  • value – The single value which should be loaded.

  • skel – The SkeletonInstance where the value should be loaded into.

  • bone_name – The bone name of this bone in the SkeletonInstance.

  • client_data – The data taken from the client, a dictionary with usually bone names as key

Returns:

A tuple. If the value is valid, the first element is the parsed value and the second is None. If the value is invalid or not parseable, the first element is a empty value and the second a list of ReadFromClientError.

_rewriteQuery(name, skel, dbFilter, rawFilter)

Rewrites a datastore query to operate on “viur-relations” instead of the original kind.

This method is needed to perform relational queries on n:m relations. It takes the original datastore query and rewrites it to target the “viur-relations” kind. It also adjusts filters and sort orders accordingly.

Parameters:
  • name (str) – The name of the bone.

  • skel (SkeletonInstance) – The skeleton instance the bone is a part of.

  • dbFilter (viur.core.db.Query) – The original datastore query to be rewritten.

  • rawFilter (dict) – The raw filter applied to the original datastore query.

Returns:

A tuple containing the name, skeleton, rewritten query, and raw filter.

Return type:

Tuple[str, ‘viur.core.skeleton.SkeletonInstance’, ‘viur.core.db.Query’, dict]

Raises:
  • NotImplementedError – If the original query contains multiple filters with “IN” or “!=” operators.

  • RuntimeError – If the filtering is invalid, e.g., using multiple key filters or querying properties not in parentKeys.

buildDBFilter(name, skel, dbFilter, rawFilter, prefix=None)

Builds a datastore query by modifying the given filter based on the RelationalBone’s properties.

This method takes a datastore query and modifies it according to the relational bone properties. It also merges any related filters based on the ‘refKeys’ and ‘using’ attributes of the bone.

Parameters:
  • name (str) – The name of the bone.

  • skel (SkeletonInstance) – The skeleton instance the bone is a part of.

  • dbFilter (db.Query) – The original datastore query to be modified.

  • rawFilter (dict) – The raw filter applied to the original datastore query.

  • prefix (str) – Optional prefix to be applied to filter keys.

Returns:

The modified datastore query.

Return type:

db.Query

Raises:

RuntimeError – If the filtering is invalid, e.g., querying properties not in ‘refKeys’ or not a bone in ‘using’.

buildDBSort(name, skel, dbFilter, rawFilter)

Builds a datastore query by modifying the given filter based on the RelationalBone’s properties for sorting.

This method takes a datastore query and modifies its sorting behavior according to the relational bone properties. It also checks if the sorting is valid based on the ‘refKeys’ and ‘using’ attributes of the bone.

Parameters:
  • name (str) – The name of the bone.

  • skel (SkeletonInstance) – The skeleton instance the bone is a part of.

  • dbFilter (db.Query) – The original datastore query to be modified.

  • rawFilter (dict) – The raw filter applied to the original datastore query.

Returns:

The modified datastore query with updated sorting behavior.

Return type:

t.Optional[db.Query]

Raises:

RuntimeError – If the sorting is invalid, e.g., using properties not in ‘refKeys’ or not a bone in ‘using’.

filterHook(name, query, param, value)

Hook installed by buildDbFilter that rewrites filters added to the query to match the layout of the viur-relations index and performs sanity checks on the query.

This method rewrites and validates filters added to a datastore query after the buildDbFilter method has been executed. It ensures that the filters are compatible with the structure of the viur-relations index and checks if the query is possible.

Parameters:
  • name (str) – The name of the bone.

  • query (db.Query) – The datastore query to be modified.

  • param (str) – The filter parameter to be checked and potentially modified.

  • value – The value associated with the filter parameter.

Returns:

A tuple containing the modified filter parameter and its associated value, or None if the filter parameter is a key special property.

Return type:

Tuple[str, Any] or None

Raises:

RuntimeError – If the filtering is invalid, e.g., using properties not in ‘refKeys’ or ‘parentKeys’.

orderHook(name, query, orderings)

Hook installed by buildDbFilter that rewrites orderings added to the query to match the layout of the viur-relations index and performs sanity checks on the query.

This method rewrites and validates orderings added to a datastore query after the buildDbFilter method has been executed. It ensures that the orderings are compatible with the structure of the viur-relations index and checks if the query is possible.

Parameters:
  • name (str) – The name of the bone.

  • query (db.Query) – The datastore query to be modified.

  • orderings (List[Union[str, Tuple[str, db.SortOrder]]] or Tuple[Union[str, Tuple[str, db.SortOrder]]]) – A list or tuple of orderings to be checked and potentially modified.

Returns:

A list of modified orderings that are compatible with the viur-relations index.

Return type:

List[Union[str, Tuple[str, db.SortOrder]]]

Raises:

RuntimeError – If the ordering is invalid, e.g., using properties not in ‘refKeys’ or ‘parentKeys’.

refresh(skel, boneName)

Refreshes all values that might be cached from other entities in the provided skeleton.

This method updates the cached values for relational bones in the provided skeleton, which correspond to other entities. It fetches the updated values for the relational bone’s reference keys and replaces the cached values in the skeleton with the fetched values.

Parameters:
  • skel (SkeletonInstance) – The skeleton containing the bone to be refreshed.

  • boneName (str) – The name of the bone to be refreshed.

getSearchTags(skel, name)

Retrieves the search tags for the given RelationalBone in the provided skeleton.

This method iterates over the values of the relational bone and gathers search tags from the reference and using skeletons. It combines all the tags into a set to avoid duplicates.

Parameters:
  • skel (SkeletonInstance) – The skeleton containing the bone for which search tags are to be retrieved.

  • name (str) – The name of the bone for which search tags are to be retrieved.

Returns:

A set of search tags for the specified relational bone.

Return type:

Set[str]

createRelSkelFromKey(key, rel=None)

Creates a relSkel instance valid for this bone from the given database key.

This method retrieves the entity corresponding to the provided key from the database, unserializes it into a reference skeleton, and returns a dictionary containing the reference skeleton and optional relation data.

Parameters:
  • key (Union[str, viur.core.db.Key]) – The database key of the entity for which a relSkel instance is to be created.

  • None]rel (Union[dict,) – Optional relation data to be included in the resulting dictionary. Default is None.

  • key

  • rel (dict | None) –

Returns:

A dictionary containing a reference skeleton and optional relation data.

Return type:

dict

setBoneValue(skel, boneName, value, append, language=None)

Sets the value of the specified bone in the given skeleton. Sanity checks are performed to ensure the value is valid. If the value is invalid, no modifications are made.

Parameters:
  • skel (SkeletonInstance) – Dictionary with the current values from the skeleton we belong to.

  • boneName (str) – The name of the bone to be modified.

  • value (Any) – The value to be assigned. The type depends on the bone type.

  • append (bool) – If true, the given value is appended to the values of the bone instead of replacing it. Only supported on bones with multiple=True.

  • language (None | str) – Set/append for a specific language (optional). Required if the bone supports languages.

  • language

Returns:

True if the operation succeeded, False otherwise.

Return type:

bool

getReferencedBlobs(skel, name)

Retrieves the set of referenced blobs from the specified bone in the given skeleton instance.

Parameters:
  • skel (SkeletonInstance) – The skeleton instance to extract the referenced blobs from.

  • name (str) – The name of the bone to retrieve the referenced blobs from.

Returns:

A set containing the unique blob keys referenced by the specified bone.

Return type:

Set[str]

getUniquePropertyIndexValues(valuesCache, name)

Generates unique property index values for the RelationalBone based on the referenced keys. Can be overridden if different behavior is required (e.g., examining values from prop:usingSkel).

Parameters:
  • valuesCache (dict) – The cache containing the current values of the bone.

  • name (str) – The name of the bone for which to generate unique property index values.

Returns:

A list containing the unique property index values for the specified bone.

Return type:

List[str]

structure()

Describes the bone and its settings as an JSON-serializable dict. This function has to be implemented for subsequent, specialized bone types.

Return type:

dict