Skip to content

Commit 01732f2

Browse files
Restrict match parameter to individual mode only, remove from batch mode
Co-authored-by: MarcusRisanger <69350948+MarcusRisanger@users.noreply.github.com>
1 parent 1491c17 commit 01732f2

4 files changed

Lines changed: 29 additions & 65 deletions

File tree

UPSERT_MATCH_EXAMPLE.md

Lines changed: 14 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@
22

33
The `match` parameter in the `upsert` method allows you to control whether the operation should only create new records or only update existing ones.
44

5+
**Note:** The `match` parameter is only supported for **individual mode**, not batch mode.
6+
57
## Usage
68

79
### Standard Upsert (Default Behavior)
@@ -14,25 +16,31 @@ entity.upsert(data, mode="individual")
1416
```python
1517
# Only updates existing records, will fail if record doesn't exist
1618
# Uses If-Match: * header
19+
# Only works with mode="individual"
1720
entity.upsert(data, mode="individual", match="prevent_create")
1821
```
1922

2023
### Prevent Update (Only Create)
2124
```python
2225
# Only creates new records, will fail if record already exists
2326
# Uses If-None-Match: * header
27+
# Only works with mode="individual"
2428
entity.upsert(data, mode="individual", match="prevent_update")
2529
```
2630

27-
## Batch Mode Support
28-
The `match` parameter works with both individual and batch modes:
31+
## Batch Mode
32+
The `match` parameter is **not supported** for batch mode operations. Attempting to use it will raise a `DataverseError`:
2933

3034
```python
31-
# Batch mode with prevent_create
32-
entity.upsert(data, mode="batch", match="prevent_create")
35+
# This will raise an error
36+
entity.upsert(data, mode="batch", match="prevent_create") # Error!
37+
```
3338

34-
# Batch mode with prevent_update
35-
entity.upsert(data, mode="batch", match="prevent_update")
39+
For batch operations, use standard upsert behavior without the `match` parameter:
40+
41+
```python
42+
# Standard batch upsert (create or update)
43+
entity.upsert(data, mode="batch")
3644
```
3745

3846
## Reference

dataverse_api/entity.py

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -749,7 +749,6 @@ def upsert(
749749
altkey_name: str | None = None,
750750
threading: bool = False,
751751
batch_size: int | None = None,
752-
match: Literal["prevent_create", "prevent_update"] | None = None,
753752
) -> list[requests.Response]: ...
754753

755754
def upsert(
@@ -780,11 +779,15 @@ def upsert(
780779
Optional override if batch mode is specified, useful for tuning workloads
781780
if 429s or timeouts occur.
782781
match : Literal["prevent_create", "prevent_update"] | None
783-
Controls upsert behavior using If-Match headers:
782+
Controls upsert behavior using If-Match headers.
783+
Only supported for individual mode, not batch mode.
784784
- None (default): Standard upsert behavior (create or update)
785785
- "prevent_create": Only update existing records (If-Match: *)
786786
- "prevent_update": Only create new records (If-None-Match: *)
787787
"""
788+
if match is not None and mode == "batch":
789+
raise DataverseError("The 'match' parameter is only supported for individual mode, not batch mode.")
790+
788791
if altkey_name is not None:
789792
try:
790793
key_columns = self.alternate_keys[altkey_name]
@@ -813,7 +816,6 @@ def upsert(
813816
data=data,
814817
keys=key_columns,
815818
is_primary_id=is_primary_id,
816-
match=match,
817819
)
818820
return self._batch_api_call(
819821
batch_commands=batch_commands,

dataverse_api/utils/batching.py

Lines changed: 1 addition & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
from dataclasses import dataclass, field
55
from enum import StrEnum
66
from textwrap import dedent
7-
from typing import Any, Collection, Generator, Literal, Mapping, MutableMapping, TypeVar
7+
from typing import Any, Collection, Generator, Mapping, MutableMapping, TypeVar
88
from urllib.parse import urljoin
99

1010
from dataverse_api.errors import DataverseError
@@ -209,7 +209,6 @@ def transform_to_batch_for_upsert(
209209
data: Collection[MutableMapping[str, Any]],
210210
keys: Iterable[str],
211211
is_primary_id: bool = False,
212-
match: Literal["prevent_create", "prevent_update"] | None = None,
213212
) -> list[BatchCommand]:
214213
"""
215214
Transform data payload to upsert batch data.
@@ -224,27 +223,15 @@ def transform_to_batch_for_upsert(
224223
The keys used to identify unique rows in the dataset.
225224
is_id : bool
226225
Whether the supplied singular key is the Entity primary ID attribute.
227-
match : Literal["prevent_create", "prevent_update"] | None
228-
Controls upsert behavior using If-Match headers:
229-
- None (default): Standard upsert behavior (create or update)
230-
- "prevent_create": Only update existing records (If-Match: *)
231-
- "prevent_update": Only create new records (If-None-Match: *)
232226
"""
233227
check_altkey_support(keys=keys, data=data)
234-
headers: dict[str, str] | None = None
235-
if match == "prevent_create":
236-
headers = {"If-Match": "*"}
237-
elif match == "prevent_update":
238-
headers = {"If-None-Match": "*"}
239-
240228
commands = []
241229
for keys, payload in transform_upsert_data(data, keys, is_primary_id):
242230
commands.append(
243231
BatchCommand(
244232
url=f"{url}({keys})",
245233
method=RequestMethod.PATCH,
246234
data=payload,
247-
headers=headers,
248235
)
249236
)
250237

tests/test_entity.py

Lines changed: 9 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -766,48 +766,15 @@ def test_entity_upsert_individual_prevent_update(
766766
assert row.status_code == 204
767767

768768

769-
def test_entity_upsert_batch_prevent_create(
770-
entity: DataverseEntity,
771-
primary_id: str,
772-
mocked_responses: responses.RequestsMock,
773-
):
774-
"""Test batch upsert with prevent_create (If-Match: *) - only update existing records."""
775-
# Setup
776-
data = [{primary_id: str(uuid4()), "test_val": random.randint(1, 10)} for _ in range(4)]
777-
778-
mocked_responses.post(url=f"{entity._endpoint}$batch")
779-
780-
resp = entity.upsert(data=data, mode="batch", match="prevent_create")
781-
782-
assert isinstance(resp[0].request.body, str) # type checking
783-
elements = resp[0].request.body.split("--batch")[1:-1]
784-
785-
for out, expected in zip(elements, data):
786-
assert f"{entity.entity_set_name}({expected.pop(primary_id)})" in out
787-
assert "If-Match: *" in out
788-
assert serialize_json(expected) in out
789-
790-
791-
def test_entity_upsert_batch_prevent_update(
792-
entity: DataverseEntity,
793-
primary_id: str,
794-
mocked_responses: responses.RequestsMock,
795-
):
796-
"""Test batch upsert with prevent_update (If-None-Match: *) - only create new records."""
797-
# Setup
798-
data = [{primary_id: str(uuid4()), "test_val": random.randint(1, 10)} for _ in range(4)]
799-
800-
mocked_responses.post(url=f"{entity._endpoint}$batch")
801-
802-
resp = entity.upsert(data=data, mode="batch", match="prevent_update")
803-
804-
assert isinstance(resp[0].request.body, str) # type checking
805-
elements = resp[0].request.body.split("--batch")[1:-1]
806-
807-
for out, expected in zip(elements, data):
808-
assert f"{entity.entity_set_name}({expected.pop(primary_id)})" in out
809-
assert "If-None-Match: *" in out
810-
assert serialize_json(expected) in out
769+
def test_entity_upsert_batch_match_not_supported(entity: DataverseEntity, primary_id: str):
770+
"""Test that using match parameter with batch mode raises an error."""
771+
data = [{primary_id: str(uuid4()), "test_val": 1}]
772+
773+
with pytest.raises(DataverseError, match=r".*match.*only supported for individual mode.*"):
774+
entity.upsert(data=data, mode="batch", match="prevent_create") # type: ignore
775+
776+
with pytest.raises(DataverseError, match=r".*match.*only supported for individual mode.*"):
777+
entity.upsert(data=data, mode="batch", match="prevent_update") # type: ignore
811778

812779

813780
def test_entity_upsert_pandas_dataframe(

0 commit comments

Comments
 (0)