How can I create a unique constraint on a skeleton over multiple bones?
Opened this issue · 1 comments
sveneberth commented
I have these bones in a Skeleton:
name = StringBone(
)
module = SelectBone(
values=lambda: {name: name for name in ModuleConf.MODULES},
)
Now I want to ensure there's at least one skeleton per permutation of the values:
for example
{name: "foo", module: "file", other_bone:"abc"} # id: 1
{name: "bar", module: "file", other_bone:"456"} # id: 2
{name: "foo", module: "user", other_bone:"abc"} # id: 3
{name: "bar", module: "user", other_bone:"abc"} # id: 4
can exist together.
But you should not able to add
{name: "foo", module: "file", other_bone: "def"} # id: 5
because there's already an entry (ìd: 1
) with {name: "foo", module: "file"}
.
I tried to create a composition of these bones with a compute bone.
unique_lock = StringBone(
compute=Compute(lambda skel: f'{skel["module"]}_{skel["name"]}',
interval=ComputeInterval(ComputeMethod.OnWrite)),
visible=False,
unique=UniqueValue(UniqueLockMethod.SameValue, False, "Value already taken"),
)
But this doesn't cause a client-error in fromClient
and fails only in the skeleton toDB transaction:
File "/.../lib/python3.11/site-packages/viur/core/skeleton.py", line 1029, in __txn_update
raise ValueError(
ValueError: The unique value 'file_foo' of bone 'unique_lock' has been recently claimed!
Does anyone has an idea how I can simplify this / enforce a client error?
ArneGudermann commented
Maybe we can check this in getUniquePropertyIndexValues
like:
def getUniquePropertyIndexValues(self, skel: 'viur.core.skeleton.SkeletonInstance', name: str) -> list[str]:
val = skel[name]
if self.compute:
match self.compute.interval.method:
case ComputeMethod.OnWrite:
val = self._compute(skel, name)
case ComputeMethod.Lifetime:
now = utils.utcNow()
last_update = \
skel.accessedValues.get(f"_viur_compute_{name}_") \
or skel.dbEntity.get(f"_viur_compute_{name}_")
if not last_update or last_update + self.compute.interval.lifetime < now:
val = self._compute(skel, name)
case ComputeMethod.Once:
if name not in skel.dbEntity:
val = self._compute(skel, name)
if val is None:
return []
return self._hashValueForUniquePropertyIndex(val)