Skip to content
This repository was archived by the owner on Apr 15, 2025. It is now read-only.

Commit a326cb7

Browse files
committed
feat(client): add support for create_many_and_return
1 parent 78f0395 commit a326cb7

File tree

9 files changed

+2393
-536
lines changed

9 files changed

+2393
-536
lines changed

databases/constants.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,8 @@ def _fromdir(path: str) -> list[str]:
6868
unsupported_features={
6969
'arrays',
7070
'case_sensitivity',
71+
'create_many_and_return',
72+
'create_many_and_return_skip_duplicates',
7173
},
7274
),
7375
'mariadb': DatabaseConfig(
@@ -81,6 +83,8 @@ def _fromdir(path: str) -> list[str]:
8183
'arrays',
8284
'case_sensitivity',
8385
'full_text_search',
86+
'create_many_and_return',
87+
'create_many_and_return_skip_duplicates',
8488
},
8589
),
8690
}
Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
import pytest
2+
3+
from prisma import Prisma
4+
5+
6+
@pytest.mark.asyncio
7+
async def test_create_many_and_return(client: Prisma) -> None:
8+
"""Standard usage"""
9+
records = await client.user.create_many_and_return([{'name': 'Robert'}, {'name': 'Tegan'}])
10+
assert len(records) == 2
11+
assert records[0].name == 'Robert'
12+
assert records[1].name == 'Tegan'
13+
14+
user = await client.user.find_first(where={'name': 'Robert'})
15+
assert user is not None
16+
assert user.name == 'Robert'
17+
18+
assert await client.user.count() == 2
19+
20+
21+
@pytest.mark.asyncio
22+
async def test_required_relation_key_field(client: Prisma) -> None:
23+
"""Explicitly passing a field used as a foreign key connects the relations"""
24+
user = await client.user.create(
25+
data={
26+
'name': 'Robert',
27+
},
28+
)
29+
user2 = await client.user.create(
30+
data={
31+
'name': 'Robert',
32+
},
33+
)
34+
records = await client.profile.create_many_and_return(
35+
data=[
36+
{'user_id': user.id, 'description': 'Foo', 'country': 'Scotland'},
37+
{
38+
'user_id': user2.id,
39+
'description': 'Foo 2',
40+
'country': 'Scotland',
41+
},
42+
],
43+
)
44+
assert len(records) == 2
45+
assert records[0].description == 'Foo'
46+
assert records[1].description == 'Foo 2'
47+
48+
found = await client.user.find_unique(
49+
where={
50+
'id': user.id,
51+
},
52+
include={
53+
'profile': True,
54+
},
55+
)
56+
assert found is not None
57+
assert found.profile is not None
58+
assert found.profile.description == 'Foo'
59+
60+
found = await client.user.find_unique(
61+
where={
62+
'id': user2.id,
63+
},
64+
include={
65+
'profile': True,
66+
},
67+
)
68+
assert found is not None
69+
assert found.profile is not None
70+
assert found.profile.description == 'Foo 2'
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
import pytest
2+
3+
import prisma
4+
from prisma import Prisma
5+
6+
7+
@pytest.mark.asyncio
8+
async def test_create_many_and_return_skip_duplicates(client: Prisma) -> None:
9+
"""Skipping duplcates ignores unique constraint errors"""
10+
user = await client.user.create({'name': 'Robert'})
11+
12+
with pytest.raises(prisma.errors.UniqueViolationError) as exc:
13+
await client.user.create_many_and_return([{'id': user.id, 'name': 'Robert 2'}])
14+
15+
assert exc.match(r'Unique constraint failed')
16+
17+
records = await client.user.create_many_and_return(
18+
[{'id': user.id, 'name': 'Robert 2'}, {'name': 'Tegan'}],
19+
skip_duplicates=True,
20+
)
21+
assert len(count) == 1
22+
assert records[0].name == 'Tegan'

databases/utils.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,8 @@
2121
'json_arrays',
2222
'raw_queries',
2323
'create_many_skip_duplicates',
24+
'create_many_and_return',
25+
'create_many_and_return_skip_duplicates',
2426
'transactions',
2527
'case_sensitivity',
2628
'full_text_search',

src/prisma/_builder.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@
4141
'query_raw': 'mutation',
4242
'query_first': 'mutation',
4343
'create_many': 'mutation',
44+
'create_many_and_return': 'mutation',
4445
'execute_raw': 'mutation',
4546
'delete_many': 'mutation',
4647
'update_many': 'mutation',
@@ -61,6 +62,7 @@
6162
'query_raw': 'queryRaw',
6263
'query_first': 'queryRaw',
6364
'create_many': 'createMany{model}',
65+
'create_many_and_return': 'createMany{model}AndReturn',
6466
'execute_raw': 'executeRaw',
6567
'delete_many': 'deleteMany{model}',
6668
'update_many': 'updateMany{model}',

src/prisma/_types.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,7 @@ class _GenericAlias(Protocol):
4444
'update',
4545
'upsert',
4646
'create_many',
47+
'create_many_and_return',
4748
'delete_many',
4849
'update_many',
4950
# read queries

src/prisma/generator/templates/actions.py.jinja

Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -247,6 +247,70 @@ class {{ model.name }}Actions(Generic[{{ ModelType }}]):
247247
)
248248
return int(resp['data']['result']['count'])
249249

250+
{{ maybe_async_def }}create_many_and_return(
251+
self,
252+
data: List[types.{{ model.name }}CreateWithoutRelationsInput],
253+
*,
254+
skip_duplicates: Optional[bool] = None,
255+
) -> List[{{ ModelType }}]:
256+
"""Create multiple {{ model.name }} records at once.
257+
258+
The `skip_duplicates` argument is not supported when using SQLite, MongoDB or SQLServer
259+
260+
Parameters
261+
----------
262+
data
263+
List of {{ model.name }} record data
264+
skip_duplicates
265+
Boolean flag for ignoring unique constraint errors
266+
267+
Returns
268+
-------
269+
List[{{ RawModelType }}]
270+
The list of all {{ model.name }} records that could be found
271+
272+
Raises
273+
------
274+
prisma.errors.UnsupportedDatabaseError
275+
Attempting to query when using SQLite
276+
prisma.errors.UniqueViolationError
277+
A unique constraint check has failed, these can be ignored with the `skip_duplicates` argument
278+
{{ query_error_doc }}
279+
{{ base_error_doc }}
280+
281+
Example
282+
-------
283+
```py
284+
records = {{ maybe_await }}{{ model.name }}.prisma().create_many_and_return(
285+
data=[
286+
{% for _ in range(2) %}
287+
{
288+
# data to create a {{ model.name }} record
289+
{% for field in model.scalar_fields %}
290+
{% if field.required_on_create %}
291+
'{{ field.name }}': {{ field.get_sample_data() }},
292+
{% endif %}
293+
{% endfor %}
294+
},
295+
{% endfor %}
296+
],
297+
skip_duplicates=True,
298+
)
299+
```
300+
"""
301+
if skip_duplicates and self._client._active_provider in CREATE_MANY_SKIP_DUPLICATES_UNSUPPORTED:
302+
raise errors.UnsupportedDatabaseError(self._client._active_provider, 'create_many_skip_duplicates')
303+
304+
resp = {{ maybe_await }}self._client._execute(
305+
method='create_many_and_return',
306+
model=self._model,
307+
arguments={
308+
'data': data,
309+
'skipDuplicates': skip_duplicates,
310+
},
311+
)
312+
return [model_parse(self._model, r) for r in resp['data']['result']]
313+
250314
{{ maybe_async_def }}delete(
251315
self,
252316
where: types.{{ model.name }}WhereUniqueInput,

0 commit comments

Comments
 (0)