-
Notifications
You must be signed in to change notification settings - Fork 434
Support ReturnValuesOnConditionCheckFailure for non-transactional operation #1263
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
base: master
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||||
|---|---|---|---|---|---|---|
|
|
@@ -441,11 +441,22 @@ def update(self, actions: List[Action], condition: Optional[Condition] = None, * | |||||
| self.deserialize(item_data) | ||||||
| return data | ||||||
|
|
||||||
| def save(self, condition: Optional[Condition] = None, *, add_version_condition: bool = True) -> Dict[str, Any]: | ||||||
| def save(self, condition: Optional[Condition] = None, *, add_version_condition: bool = True, return_values_on_condition_failure: Optional[str] = None) -> Dict[str, Any]: | ||||||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This could be typed as:
Suggested change
|
||||||
| """ | ||||||
| Save this object to dynamodb | ||||||
|
|
||||||
| :param condition: an optional Condition on which to save | ||||||
| :param add_version_condition: For models which have a :class:`~pynamodb.attributes.VersionAttribute`, | ||||||
| specifies whether only to save if the version matches the model that is currently loaded. | ||||||
| Set to `False` for a 'last write wins' strategy. | ||||||
| Regardless, the version will always be incremented to prevent "rollbacks" by concurrent :meth:`update` calls. | ||||||
| :param return_values_on_condition_failure: If set, then this value will be returned in error if the condition is not met. | ||||||
| """ | ||||||
| args, kwargs = self._get_save_args(condition=condition, add_version_condition=add_version_condition) | ||||||
| args, kwargs = self._get_save_args( | ||||||
| condition=condition, | ||||||
| add_version_condition=add_version_condition, | ||||||
| return_values_on_condition_failure=return_values_on_condition_failure | ||||||
| ) | ||||||
| data = self._get_connection().put_item(*args, **kwargs) | ||||||
| self.update_local_version_attribute() | ||||||
| return data | ||||||
|
|
@@ -888,7 +899,7 @@ def _get_schema(cls) -> ModelSchema: | |||||
|
|
||||||
| return schema | ||||||
|
|
||||||
| def _get_save_args(self, condition: Optional[Condition] = None, *, add_version_condition: bool = True) -> Tuple[Iterable[Any], Dict[str, Any]]: | ||||||
| def _get_save_args(self, condition: Optional[Condition] = None, *, add_version_condition: bool = True, return_values_on_condition_failure: Optional[str] = None) -> Tuple[Iterable[Any], Dict[str, Any]]: | ||||||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This could be typed as:
Suggested change
|
||||||
| """ | ||||||
| Gets the proper *args, **kwargs for saving and retrieving this object | ||||||
|
|
||||||
|
|
@@ -898,6 +909,7 @@ def _get_save_args(self, condition: Optional[Condition] = None, *, add_version_c | |||||
| :param add_version_condition: For models which have a :class:`~pynamodb.attributes.VersionAttribute`, | ||||||
| specifies whether the item should only be saved if its current version matches the expected one. | ||||||
| Set to `False` for a 'last-write-wins' strategy. | ||||||
| :param return_values_on_condition_failure: If set, then this will return the values on condition failure | ||||||
| """ | ||||||
| attribute_values = self.serialize(null_check=True) | ||||||
| hash_key_attribute = self._hash_key_attribute() | ||||||
|
|
@@ -915,6 +927,8 @@ def _get_save_args(self, condition: Optional[Condition] = None, *, add_version_c | |||||
| condition &= version_condition | ||||||
| kwargs['attributes'] = attribute_values | ||||||
| kwargs['condition'] = condition | ||||||
| if return_values_on_condition_failure and return_values_on_condition_failure is not None: | ||||||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This could be simplified to:
Suggested change
|
||||||
| kwargs['return_values_on_condition_failure'] = return_values_on_condition_failure | ||||||
| return args, kwargs | ||||||
|
|
||||||
| def _get_hash_range_key_serialized_values(self) -> Tuple[Any, Optional[Any]]: | ||||||
|
|
||||||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -3265,6 +3265,36 @@ def test_model_version_attribute_save(add_version_condition: bool) -> None: | |
|
|
||
| assert args == params | ||
|
|
||
| @pytest.mark.parametrize('return_values_on_condition_failure', [None, 'ALL_OLD', 'all_old']) | ||
| def test_model_return_values_on_condition_failure(return_values_on_condition_failure: str | None) -> None: | ||
| item = VersionedModel('test_user_name', email='[email protected]') | ||
| with patch(PATCH_METHOD) as req: | ||
| req.return_value = {} | ||
| item.save(return_values_on_condition_failure=return_values_on_condition_failure) | ||
| args = req.call_args[0][1] | ||
| params = { | ||
| 'Item': { | ||
| 'name': { | ||
| 'S': 'test_user_name' | ||
| }, | ||
| 'email': { | ||
| 'S': '[email protected]' | ||
| }, | ||
| 'version': { | ||
| 'N': '1' | ||
| }, | ||
| }, | ||
| 'ReturnConsumedCapacity': 'TOTAL', | ||
| 'TableName': 'VersionedModel', | ||
| 'ConditionExpression': 'attribute_not_exists (#0)', | ||
| 'ExpressionAttributeNames': {'#0': 'version'} | ||
| } | ||
| if return_values_on_condition_failure is not None: | ||
| params.update({ | ||
| 'ReturnValuesOnConditionCheckFailure': return_values_on_condition_failure.upper(), | ||
| }) | ||
|
|
||
| assert args == params | ||
|
|
||
| @pytest.mark.parametrize('add_version_condition', [True, False]) | ||
| def test_version_attribute_increments_on_update(add_version_condition: bool) -> None: | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This could be typed as: