Coverage for /home/runner/work/viur-core/viur-core/viur/src/viur/core/skeleton/relskel.py: 24%
40 statements
« prev ^ index » next coverage.py v7.10.6, created at 2025-09-13 11:04 +0000
« prev ^ index » next coverage.py v7.10.6, created at 2025-09-13 11:04 +0000
1from __future__ import annotations # noqa: required for pre-defined annotations
3import typing as t
4import fnmatch
6from .. import db, utils
7from .meta import BaseSkeleton
8from .utils import skeletonByKind
11class RelSkel(BaseSkeleton):
12 """
13 This is a Skeleton-like class that acts as a container for Skeletons used as a
14 additional information data skeleton for :class:`~viur.core.bones.relational.RelationalBone`.
16 It needs to be sub-classed where information about the kindName and its attributes
17 (bones) are specified.
19 The Skeleton stores its bones in an :class:`OrderedDict`-Instance, so the definition order of the
20 contained bones remains constant.
21 """
23 def serialize(self, parentIndexed):
24 if self.dbEntity is None:
25 self.dbEntity = db.Entity()
26 for key, _bone in self.items():
27 # if key in self.accessedValues:
28 _bone.serialize(self, key, parentIndexed)
29 # if "key" in self: # Write the key seperatly, as the base-bone doesn't store it
30 # dbObj["key"] = self["key"]
31 # FIXME: is this a good idea? Any other way to ensure only bones present in refKeys are serialized?
32 return self.dbEntity
34 def unserialize(self, values: db.Entity | dict):
35 """
36 Loads 'values' into this skeleton.
38 :param values: dict with values we'll assign to our bones
39 """
40 if not isinstance(values, db.Entity):
41 self.dbEntity = db.Entity()
43 if values:
44 self.dbEntity.update(values)
45 else:
46 self.dbEntity = values
48 self.accessedValues = {}
49 self.renderAccessedValues = {}
52class RefSkel(RelSkel):
53 @classmethod
54 def fromSkel(cls, kindName: str, *args: list[str]) -> t.Type[RefSkel]:
55 """
56 Creates a ``RefSkel`` from a skeleton-class using only the bones explicitly named in ``args``.
58 :param args: List of bone names we'll adapt
59 :return: A new instance of RefSkel
60 """
61 newClass = type("RefSkelFor" + kindName, (RefSkel,), {})
62 fromSkel = skeletonByKind(kindName)
63 newClass.kindName = kindName
64 bone_map = {}
65 for arg in args:
66 bone_map |= {k: fromSkel.__boneMap__[k] for k in fnmatch.filter(fromSkel.__boneMap__.keys(), arg)}
67 newClass.__boneMap__ = bone_map
68 return newClass
70 def read(
71 self,
72 key: t.Optional[db.Key | str | int] = None,
73 *,
74 subskel: t.Iterable[str] = (),
75 bones: t.Iterable[str] = (),
76 ) -> "SkeletonInstance":
77 """
78 Read full skeleton instance referenced by the RefSkel from the database.
80 Can be used for reading the full Skeleton from a RefSkel.
81 The `key` parameter also allows to read another, given key from the related kind.
83 :param key: Can be used to overwrite the key; Ohterwise, the RefSkel's key-property will be used.
84 :param subskel: Optionally form skel from subskels
85 :param bones: Optionally create skeleton only from the specified bones
87 :raise ValueError: If the entry is no longer in the database.
88 """
89 skel_cls = skeletonByKind(self.kindName)
91 if subskel or bones:
92 skel = skel_cls.subskel(*utils.ensure_iterable(subskel), bones=utils.ensure_iterable(bones))
93 else:
94 skel = skel_cls()
96 if not skel.read(key or self["key"]):
97 raise ValueError(f"""The key {key or self["key"]!r} seems to be gone""")
99 return skel