:py:mod:`core.skeleton` ======================= .. py:module:: core.skeleton Module Contents --------------- Classes ~~~~~~~ .. autoapisummary:: core.skeleton.MetaBaseSkel core.skeleton.SkeletonInstance core.skeleton.BaseSkeleton core.skeleton.MetaSkel core.skeleton.CustomDatabaseAdapter core.skeleton.ViurTagsSearchAdapter core.skeleton.SeoKeyBone core.skeleton.Skeleton core.skeleton.RelSkel core.skeleton.RefSkel core.skeleton.SkelList core.skeleton.TaskUpdateSearchIndex core.skeleton.RebuildSearchIndex core.skeleton.TaskVacuumRelations Functions ~~~~~~~~~ .. autoapisummary:: core.skeleton.skeletonByKind core.skeleton.listKnownSkeletons core.skeleton.iterAllSkelClasses core.skeleton.processRemovedRelations core.skeleton.updateRelations core.skeleton.processVacuumRelationsChunk core.skeleton.__getattr__ Attributes ~~~~~~~~~~ .. autoapisummary:: core.skeleton._undefined core.skeleton.ABSTRACT_SKEL_CLS_SUFFIX core.skeleton.__DEPRECATED_NAMES .. py:data:: _undefined .. py:data:: ABSTRACT_SKEL_CLS_SUFFIX :value: 'AbstractSkel' .. py:class:: MetaBaseSkel(name, bases, dct, **kwargs) Bases: :py:obj:`type` This is the metaclass for Skeletons. It is used to enforce several restrictions on bone names, etc. .. py:attribute:: _skelCache .. py:attribute:: _allSkelClasses .. py:attribute:: __reserved_keywords .. py:attribute:: __allowed_chars .. py:method:: generate_bonemap(cls) :staticmethod: Recursively constructs a dict of bones from .. py:method:: __setattr__(key, value) Implement setattr(self, name, value). .. py:function:: skeletonByKind(kindName) Returns the Skeleton-Class for the given kindName. That skeleton must exist, otherwise an exception is raised. :param kindName: The kindname to retreive the skeleton for :return: The skeleton-class for that kind .. py:function:: listKnownSkeletons() :return: A list of all known kindnames (all kindnames for which a skeleton is defined) .. py:function:: iterAllSkelClasses() :return: An iterator that yields each Skeleton-Class once. (Only top-level skeletons are returned, so no RefSkel classes will be included) .. py:class:: SkeletonInstance(skelCls, subSkelNames=None, fullClone=False, clonedBoneMap=None) The actual wrapper around a Skeleton-Class. An object of this class is what's actually returned when you call a Skeleton-Class. With ViUR3, you don't get an instance of a Skeleton-Class any more - it's always this class. This is much faster as this is a small class. .. py:attribute:: __slots__ .. py:method:: items(yieldBoneValues = False) .. py:method:: keys() .. py:method:: values() .. py:method:: __iter__() .. py:method:: __contains__(item) .. py:method:: get(item, default=None) .. py:method:: __setitem__(key, value) .. py:method:: __getitem__(key) .. py:method:: __getattr__(item) Get a special attribute from the SkeletonInstance __getattr__ is called when an attribute access fails with an AttributeError. So we know that this is not a real attribute of the SkeletonInstance. But there are still a few special cases in which attributes are loaded from the skeleton class. .. py:method:: __delattr__(item) Implement delattr(self, name). .. py:method:: __setattr__(key, value) Implement setattr(self, name, value). .. py:method:: __repr__() Return repr(self). .. py:method:: __str__() Return str(self). .. py:method:: __len__() .. py:method:: clone() Clones a SkeletonInstance into a modificable, stand-alone instance. This will also allow to modify the underlying data model. .. py:method:: ensure_is_cloned() Ensured this SkeletonInstance is a stand-alone clone, which can be modified. Does nothing in case it was already cloned before. .. py:method:: setEntity(entity) .. py:method:: structure() .. py:method:: __deepcopy__(memodict) .. py:class:: BaseSkeleton Bases: :py:obj:`object` This is a container-object holding information about one database entity. It has to be sub-classed with individual information about the kindName of the entities and its specific data attributes, the so called bones. The Skeleton stores its bones in an :class:`OrderedDict`-Instance, so the definition order of the contained bones remains constant. :ivar key: This bone stores the current database key of this entity. Assigning to this bones value is dangerous and does *not* affect the actual key its stored in. :vartype key: server.bones.BaseBone :ivar creationdate: The date and time where this entity has been created. :vartype creationdate: server.bones.DateBone :ivar changedate: The date and time of the last change to this entity. :vartype changedate: server.bones.DateBone .. py:attribute:: __viurBaseSkeletonMarker__ :value: True .. py:attribute:: boneMap .. py:method:: subSkel(*name, fullClone = False, **kwargs) :classmethod: Creates a new sub-skeleton as part of the current skeleton. A sub-skeleton is a copy of the original skeleton, containing only a subset of its bones. To define sub-skeletons, use the subSkels property of the Skeleton object. By passing multiple sub-skeleton names to this function, a sub-skeleton with the union of all bones of the specified sub-skeletons is returned. If an entry called "*" exists in the subSkels-dictionary, the bones listed in this entry will always be part of the generated sub-skeleton. :param name: Name of the sub-skeleton (that's the key of the subSkels dictionary); Multiple names can be specified. :return: The sub-skeleton of the specified type. .. py:method:: setSystemInitialized() :classmethod: .. py:method:: setBoneValue(skelValues, boneName, value, append = False, language = None) :classmethod: Allows for setting a bones value without calling fromClient or assigning a value directly. Sanity-Checks are performed; if the value is invalid, that bone flips back to its original (default) value and false is returned. :param boneName: The name of the bone to be modified :param value: The value that should be assigned. It's type depends on the type of that bone :param append: If True, the given value is appended to the values of that bone instead of replacing it. Only supported on bones with multiple=True :param language: Language to set :return: Wherever that operation succeeded or not. .. py:method:: fromClient(skelValues, data, amend = False) :classmethod: Load supplied *data* into Skeleton. This function works similar to :func:`~viur.core.skeleton.Skeleton.setValues`, except that the values retrieved from *data* are checked against the bones and their validity checks. Even if this function returns False, all bones are guaranteed to be in a valid state. The ones which have been read correctly are set to their valid values; Bones with invalid values are set back to a safe default (None in most cases). So its possible to call :func:`~viur.core.skeleton.Skeleton.toDB` afterwards even if reading data with this function failed (through this might violates the assumed consistency-model). :param skel: The skeleton instance to be filled. :param data: Dictionary from which the data is read. :param amend: Defines whether content of data may be incomplete to amend the skel, which is useful for edit-actions. :returns: True if all data was successfully read and complete. False otherwise (e.g. some required fields where missing or where invalid). .. py:method:: refresh(skel) :classmethod: Refresh the bones current content. This function causes a refresh of all relational bones and their associated information. .. py:class:: MetaSkel(name, bases, dct, **kwargs) Bases: :py:obj:`MetaBaseSkel` This is the metaclass for Skeletons. It is used to enforce several restrictions on bone names, etc. .. py:class:: CustomDatabaseAdapter .. py:attribute:: providesFulltextSearch :type: bool :value: False .. py:attribute:: fulltextSearchGuaranteesQueryConstrains :value: False .. py:attribute:: providesCustomQueries :type: bool :value: False .. py:method:: preprocessEntry(entry, skel, changeList, isAdd) Can be overridden to add or alter the data of this entry before it's written to firestore. Will always be called inside an transaction. :param entry: The entry containing the serialized data of that skeleton :param skel: The (complete) skeleton this skel.toDB() runs for :param changeList: List of boneNames that are changed by this skel.toDB() call :param isAdd: Is this an update or an add? :return: The (maybe modified) entity .. py:method:: updateEntry(dbObj, skel, changeList, isAdd) Like `meth:preprocessEntry`, but runs after the transaction had completed. Changes made to dbObj will be ignored. :param entry: The entry containing the serialized data of that skeleton :param skel: The (complete) skeleton this skel.toDB() runs for :param changeList: List of boneNames that are changed by this skel.toDB() call :param isAdd: Is this an update or an add? .. py:method:: deleteEntry(entry, skel) Called, after an skeleton has been successfully deleted from firestore :param entry: The db.Entity object containing an snapshot of the data that has been deleted :param skel: The (complete) skeleton for which `meth:delete' had been called .. py:method:: fulltextSearch(queryString, databaseQuery) :abstractmethod: If this database supports fulltext searches, this method has to implement them. If it's a plain fulltext search engine, leave 'prop:fulltextSearchGuaranteesQueryConstrains' set to False, then the server will post-process the list of entries returned from this function and drop any entry that cannot be returned due to other constrains set in 'param:databaseQuery'. If you can obey *every* constrain set in that Query, we can skip this post-processing and save some CPU-cycles. :param queryString: the string as received from the user (no quotation or other safety checks applied!) :param databaseQuery: The query containing any constrains that returned entries must also match :return: .. py:class:: ViurTagsSearchAdapter(min_length = 2, max_length = 50, substring_matching = False) Bases: :py:obj:`CustomDatabaseAdapter` This Adapter implements a simple fulltext search on top of the datastore. On skel.toDB(), all words from String-/TextBones are collected with all *min_length* postfixes and dumped into the property `viurTags`. When queried, we'll run a prefix-match against this property - thus returning entities with either an exact match or a match within a word. Example: For the word "hello" we'll write "hello", "ello" and "llo" into viurTags. When queried with "hello" we'll have an exact match. When queried with "hel" we'll match the prefix for "hello" When queried with "ell" we'll prefix-match "ello" - this is only enabled when substring_matching is True. We'll automatically add this adapter if a skeleton has no other database adapter defined. .. py:attribute:: providesFulltextSearch :value: True .. py:attribute:: fulltextSearchGuaranteesQueryConstrains :value: True .. py:method:: _tagsFromString(value) Extract all words including all min_length postfixes from given string .. py:method:: preprocessEntry(entry, skel, changeList, isAdd) Collect searchTags from skeleton and build viurTags .. py:method:: fulltextSearch(queryString, databaseQuery) Run a fulltext search .. py:class:: SeoKeyBone(*, caseSensitive = True, max_length = 254, min_length = None, natural_sorting = False, **kwargs) Bases: :py:obj:`viur.core.bones.StringBone` Special kind of StringBone saving its contents as `viurCurrentSeoKeys` into the entity's `viur` dict. Initializes a new StringBone. :param caseSensitive: When filtering for values in this bone, should it be case-sensitive? :param max_length: The maximum length allowed for values of this bone. Set to None for no limitation. :param min_length: The minimum length allowed for values of this bone. Set to None for no limitation. :param natural_sorting: Allows a more natural sorting than the default sorting on the plain values. This uses the .sort_idx property. `True` enables sorting according to DIN 5007 Variant 2. With passing a `callable`, a custom transformer method can be set that creates the value for the index property. :param kwargs: Inherited arguments from the BaseBone. .. py:method:: unserialize(skel, name) Deserialize bone data from the datastore and populate the bone with the deserialized values. This function is the inverse of the serialize function. It converts data from the datastore into a format that can be used by the bones in the skeleton. :param skel: A SkeletonInstance object containing the values to be deserialized. :param name: The property name of the bone in its Skeleton (not the description). :returns: True if deserialization is successful, False otherwise. .. py:method:: serialize(skel, name, parentIndexed) Serializes this bone into a format that can be written into the datastore. :param skel: A SkeletonInstance object containing the values to be serialized. :param name: A string representing the property name of the bone in its Skeleton (not the description). :param parentIndexed: A boolean indicating whether the parent bone is indexed. :return: A boolean indicating whether the serialization was successful. .. py:class:: Skeleton(*args, **kwargs) Bases: :py:obj:`BaseSkeleton` This is a container-object holding information about one database entity. It has to be sub-classed with individual information about the kindName of the entities and its specific data attributes, the so called bones. The Skeleton stores its bones in an :class:`OrderedDict`-Instance, so the definition order of the contained bones remains constant. :ivar key: This bone stores the current database key of this entity. Assigning to this bones value is dangerous and does *not* affect the actual key its stored in. :vartype key: server.bones.BaseBone :ivar creationdate: The date and time where this entity has been created. :vartype creationdate: server.bones.DateBone :ivar changedate: The date and time of the last change to this entity. :vartype changedate: server.bones.DateBone .. py:attribute:: kindName :type: str .. py:attribute:: customDatabaseAdapter :type: CustomDatabaseAdapter | None .. py:attribute:: subSkels .. py:attribute:: interBoneValidations :type: list[Callable[[Skeleton], list[viur.core.bones.base.ReadFromClientError]]] :value: [] .. py:attribute:: __seo_key_trans .. py:attribute:: key .. py:attribute:: name .. py:attribute:: creationdate .. py:attribute:: changedate .. py:attribute:: viurCurrentSeoKeys .. py:method:: __repr__() Return repr(self). .. py:method:: __str__() Return str(self). .. py:method:: all(skelValues, **kwargs) :classmethod: Create a query with the current Skeletons kindName. :returns: A db.Query object which allows for entity filtering and sorting. .. py:method:: fromClient(skelValues, data, amend = False) :classmethod: This function works similar to :func:`~viur.core.skeleton.Skeleton.setValues`, except that the values retrieved from *data* are checked against the bones and their validity checks. Even if this function returns False, all bones are guaranteed to be in a valid state. The ones which have been read correctly are set to their valid values; Bones with invalid values are set back to a safe default (None in most cases). So its possible to call :func:`~viur.core.skeleton.Skeleton.toDB` afterwards even if reading data with this function failed (through this might violates the assumed consistency-model). :param skel: The skeleton instance to be filled. :param data: Dictionary from which the data is read. :param amend: Defines whether content of data may be incomplete to amend the skel, which is useful for edit-actions. :returns: True if all data was successfully read and complete. False otherwise (e.g. some required fields where missing or where invalid). .. py:method:: fromDB(skel, key) :classmethod: Load entity with *key* from the Datastore into the Skeleton. Reads all available data of entity kind *kindName* and the key *key* from the Datastore into the Skeleton structure's bones. Any previous data of the bones will discard. To store a Skeleton object to the Datastore, see :func:`~viur.core.skeleton.Skeleton.toDB`. :param key: A :class:`viur.core.DB.Key`, string, or int; from which the data shall be fetched. :returns: True on success; False if the given key could not be found or can not be parsed. .. py:method:: toDB(skel, update_relations = True, **kwargs) :classmethod: Store current Skeleton entity to the Datastore. Stores the current data of this instance into the database. If an *key* value is set to the object, this entity will ne updated; Otherwise a new entity will be created. To read a Skeleton object from the data store, see :func:`~viur.core.skeleton.Skeleton.fromDB`. :param update_relations: If False, this entity won't be marked dirty; This avoids from being fetched by the background task updating relations. :returns: The datastore key of the entity. .. py:method:: preProcessBlobLocks(skelValues, locks) :classmethod: Can be overridden to modify the list of blobs referenced by this skeleton .. py:method:: preProcessSerializedData(skelValues, entity) :classmethod: Can be overridden to modify the :class:`viur.core.db.Entity` before its actually written to the data store. .. py:method:: postSavedHandler(skelValues, key, dbObj) :classmethod: Can be overridden to perform further actions after the entity has been written to the data store. .. py:method:: postDeletedHandler(skelValues, key) :classmethod: Can be overridden to perform further actions after the entity has been deleted from the data store. .. py:method:: getCurrentSEOKeys(skelValues) :classmethod: Should be overridden to return a dictionary of language -> SEO-Friendly key this entry should be reachable under. How theses names are derived are entirely up to the application. If the name is already in use for this module, the server will automatically append some random string to make it unique. :return: .. py:method:: delete(skelValues) :classmethod: Deletes the entity associated with the current Skeleton from the data store. .. py:class:: RelSkel Bases: :py:obj:`BaseSkeleton` This is a Skeleton-like class that acts as a container for Skeletons used as a additional information data skeleton for :class:`~viur.core.bones.extendedRelationalBone.extendedRelationalBone`. It needs to be sub-classed where information about the kindName and its attributes (bones) are specified. The Skeleton stores its bones in an :class:`OrderedDict`-Instance, so the definition order of the contained bones remains constant. .. py:method:: serialize(parentIndexed) .. py:method:: unserialize(values) Loads 'values' into this skeleton. :param values: dict with values we'll assign to our bones .. py:class:: RefSkel Bases: :py:obj:`RelSkel` This is a Skeleton-like class that acts as a container for Skeletons used as a additional information data skeleton for :class:`~viur.core.bones.extendedRelationalBone.extendedRelationalBone`. It needs to be sub-classed where information about the kindName and its attributes (bones) are specified. The Skeleton stores its bones in an :class:`OrderedDict`-Instance, so the definition order of the contained bones remains constant. .. py:method:: fromSkel(kindName, *args) :classmethod: Creates a relSkel from a skeleton-class using only the bones explicitly named in \*args :param args: List of bone names we'll adapt :return: A new instance of RefSkel .. py:class:: SkelList(baseSkel=None) Bases: :py:obj:`list` This class is used to hold multiple skeletons together with other, commonly used information. SkelLists are returned by Skel().all()...fetch()-constructs and provide additional information about the data base query, for fetching additional entries. :ivar cursor: Holds the cursor within a query. :vartype cursor: str :param baseSkel: The baseclass for all entries in this list .. py:attribute:: __slots__ :value: ('baseSkel', 'customQueryInfo', 'getCursor', 'get_orders', 'renderPreparation') .. py:function:: processRemovedRelations(removedKey, cursor=None) .. py:function:: updateRelations(destKey, minChangeTime, changedBone, cursor = None) This function updates Entities, which may have a copy of values from another entity which has been recently edited (updated). In ViUR, relations are implemented by copying the values from the referenced entity into the entity that's referencing them. This allows ViUR to run queries over properties of referenced entities and prevents additional db.Get's to these referenced entities if the main entity is read. However, this forces us to track changes made to entities as we might have to update these mirrored values. This is the deferred call from meth:`viur.core.skeleton.Skeleton.toDB()` after an update (edit) on one Entity to do exactly that. :param destKey: The database-key of the entity that has been edited :param minChangeTime: The timestamp on which the edit occurred. As we run deferred, and the entity might have been edited multiple times before we get acutally called, we can ignore entities that have been updated in the meantime as they're already up2date :param changedBone: If set, we'll update only entites that have a copy of that bone. Relations mirror only key and name by default, so we don't have to update these if only another bone has been changed. :param cursor: The database cursor for the current request as we only process five entities at once and then defer again. .. py:class:: TaskUpdateSearchIndex Bases: :py:obj:`viur.core.tasks.CallableTaskBase` This tasks loads and saves *every* entity of the given module. This ensures an updated searchIndex and verifies consistency of this data. .. py:attribute:: key :value: 'rebuildSearchIndex' .. py:attribute:: name :value: 'Rebuild search index' .. py:attribute:: descr :value: 'This task can be called to update search indexes and relational information.' .. py:method:: canCall() Checks wherever the current user can execute this task .. py:method:: dataSkel() If additional data is needed, return a skeleton-instance here. These values are then passed to *execute*. .. py:method:: execute(module, *args, **kwargs) The actual code that should be run goes here. .. py:method:: _run(module, notify) :staticmethod: .. py:class:: RebuildSearchIndex Bases: :py:obj:`viur.core.tasks.QueryIter` BaseClass to run a database Query and process each entry matched. This will run each step deferred, so it is possible to process an arbitrary number of entries without being limited by time or memory. To use this class create a subclass, override the classmethods handleEntry and handleFinish and then call startIterOnQuery with an instance of a database Query (and possible some custom data to pass along) .. py:method:: handleEntry(skel, customData) :classmethod: Overridable hook to process one entry. "entry" will be either an db.Entity or an SkeletonInstance (if that query has been created by skel.all()) Warning: If your query has an sortOrder other than __key__ and you modify that property here it is possible to encounter that object later one *again* (as it may jump behind the current cursor). .. py:method:: handleFinish(totalCount, customData) :classmethod: Overridable hook that indicates the current run has been finished. .. py:class:: TaskVacuumRelations Bases: :py:obj:`TaskUpdateSearchIndex` Checks entries in viur-relations and verifies that the src-kind and it's RelationalBone still exists. .. py:attribute:: key :value: 'vacuumRelations' .. py:attribute:: name :value: 'Vacuum viur-relations (dangerous)' .. py:attribute:: descr :value: 'Drop stale inbound relations for the given kind' .. py:method:: execute(module, *args, **kwargs) The actual code that should be run goes here. .. py:function:: processVacuumRelationsChunk(module, cursor, count_total = 0, count_removed = 0, notify=None) Processes 25 Entries and calls the next batch .. py:data:: __DEPRECATED_NAMES .. py:function:: __getattr__(attr)