-
Notifications
You must be signed in to change notification settings - Fork 3
fix: keep order/cart address copies live until frozen #196
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
sveneberth
merged 3 commits into
viur-framework:main
from
sveneberth:fix/live-address-relation-until-frozen
Jul 31, 2026
Merged
Changes from 1 commit
Commits
Show all changes
3 commits
Select commit
Hold shift + click to select a range
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,61 @@ | ||
| import typing as t | ||
|
|
||
| from viur.core.bones import RelationalBone, RelationalUpdateLevel | ||
| from viur.core.skeleton import SkeletonInstance | ||
|
|
||
| from ..globals import SHOP_LOGGER | ||
|
|
||
| logger = SHOP_LOGGER.getChild(__name__) | ||
|
|
||
|
|
||
| class SnapshotRelationalBone(RelationalBone): | ||
| """A :class:`RelationalBone` that keeps its cached ``refKeys`` copy in sync | ||
| while the owning skeleton is still editable, and freezes that copy once the | ||
| owning skeleton is frozen. | ||
|
|
||
| Background | ||
| ---------- | ||
| viur-core only offers a *static* :class:`RelationalUpdateLevel`, and neither | ||
| value fits an order/cart address: | ||
|
|
||
| - ``Always`` keeps the cached copy in sync with the referenced entity, but a | ||
| completed (frozen) order would then change retroactively whenever the | ||
| referenced address gets edited later. | ||
| - ``OnValueAssignment`` freezes the copy at assignment time, but then never | ||
| reflects edits made to the referenced entity during an ongoing checkout | ||
| (e.g. the customer adds a birthdate in a later step or corrects the | ||
| address) -- the stale copy is what gets persisted with the order. | ||
|
|
||
| This bone combines both: it is configured with ``updateLevel=Always`` so the | ||
| copy is kept in sync -- both by the ``update_relations`` task (triggered when | ||
| the referenced entity changes) and by explicit :meth:`refresh` calls -- as | ||
| long as the owning skeleton is *not* frozen, and turns :meth:`refresh` into a | ||
| no-op once it *is* frozen, preserving the snapshot captured at freeze time. | ||
|
|
||
| :param is_frozen: Callable deciding whether the owning skeleton is frozen and | ||
| its snapshot must be preserved. Defaults to reading the boolean | ||
| ``is_frozen`` bone. | ||
| """ | ||
|
|
||
| def __init__( | ||
| self, | ||
| *args: t.Any, | ||
| is_frozen: t.Callable[[SkeletonInstance], bool] = lambda skel: bool(skel["is_frozen"]), | ||
| **kwargs: t.Any, | ||
| ) -> None: | ||
| # The live-sync behaviour relies on updateLevel=Always: it keeps the | ||
| # relation in the update_relations query and prevents refresh() from | ||
| # short-circuiting. Freezing is handled by refresh() below instead, so | ||
| # any caller-provided updateLevel would break the contract and is | ||
| # therefore overridden here. | ||
| kwargs["updateLevel"] = RelationalUpdateLevel.Always | ||
| super().__init__(*args, **kwargs) | ||
| self.is_frozen = is_frozen | ||
|
|
||
| def refresh(self, skel: SkeletonInstance, name: str) -> None: | ||
| """Refresh the cached copy -- unless the owning skeleton is frozen, in | ||
| which case the snapshot captured at freeze time is preserved.""" | ||
| if self.is_frozen(skel): | ||
| logger.debug(f"Skipping refresh of frozen {name!r} on {skel['key']!r}") | ||
| return | ||
| super().refresh(skel, name) | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.