Skip to content
This repository was archived by the owner on Apr 15, 2025. It is now read-only.
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions databases/constants.py
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@ def _fromdir(path: str) -> list[str]:
'date',
'arrays',
'create_many_skip_duplicates',
'create_many_and_return_skip_duplicates',
'case_sensitivity',
'full_text_search',
},
Expand All @@ -68,6 +69,8 @@ def _fromdir(path: str) -> list[str]:
unsupported_features={
'arrays',
'case_sensitivity',
'create_many_and_return',
'create_many_and_return_skip_duplicates',
},
),
'mariadb': DatabaseConfig(
Expand All @@ -81,6 +84,8 @@ def _fromdir(path: str) -> list[str]:
'arrays',
'case_sensitivity',
'full_text_search',
'create_many_and_return',
'create_many_and_return_skip_duplicates',
},
),
}
Expand All @@ -107,6 +112,8 @@ def _fromdir(path: str) -> list[str]:
# not yet implemented
'date': [],
'create_many_skip_duplicates': ['test_create_many_skip_duplicates.py'],
'create_many_and_return': ['test_create_many_and_return.py'],
'create_many_and_return_skip_duplicates': ['test_create_many_and_return_skip_duplicates.py'],
'raw_queries': ['test_raw_queries.py', *_fromdir('types/raw_queries')],
'case_sensitivity': ['test_case_sensitivity.py'],
'full_text_search': ['test_full_text_search.py'],
Expand Down
71 changes: 71 additions & 0 deletions databases/tests/test_create_many_and_return.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
import pytest

from prisma import Prisma


@pytest.mark.asyncio
@pytest.mark.skip()
async def test_create_many_and_return(client: Prisma) -> None:
"""Standard usage"""
records = await client.user.create_many_and_return([{'name': 'Robert'}, {'name': 'Tegan'}])
assert len(records) == 2
assert records[0].name == 'Robert'
assert records[1].name == 'Tegan'

user = await client.user.find_first(where={'name': 'Robert'})
assert user is not None
assert user.name == 'Robert'

assert await client.user.count() == 2


@pytest.mark.asyncio
async def test_required_relation_key_field(client: Prisma) -> None:
"""Explicitly passing a field used as a foreign key connects the relations"""
user = await client.user.create(
data={
'name': 'Robert',
},
)
user2 = await client.user.create(
data={
'name': 'Robert',
},
)
records = await client.profile.create_many_and_return(
data=[
{'user_id': user.id, 'description': 'Foo', 'country': 'Scotland'},
{
'user_id': user2.id,
'description': 'Foo 2',
'country': 'Scotland',
},
],
)
assert len(records) == 2
assert records[0].description == 'Foo'
assert records[1].description == 'Foo 2'

found = await client.user.find_unique(
where={
'id': user.id,
},
include={
'profile': True,
},
)
assert found is not None
assert found.profile is not None
assert found.profile.description == 'Foo'

found = await client.user.find_unique(
where={
'id': user2.id,
},
include={
'profile': True,
},
)
assert found is not None
assert found.profile is not None
assert found.profile.description == 'Foo 2'
22 changes: 22 additions & 0 deletions databases/tests/test_create_many_and_return_skip_duplicates.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import pytest

import prisma
from prisma import Prisma


@pytest.mark.asyncio
async def test_create_many_and_return_skip_duplicates(client: Prisma) -> None:
"""Skipping duplcates ignores unique constraint errors"""
user = await client.user.create({'name': 'Robert'})

with pytest.raises(prisma.errors.UniqueViolationError) as exc:
await client.user.create_many_and_return([{'id': user.id, 'name': 'Robert 2'}])

assert exc.match(r'Unique constraint failed')

records = await client.user.create_many_and_return(
[{'id': user.id, 'name': 'Robert 2'}, {'name': 'Tegan'}],
skip_duplicates=True,
)
assert len(records) == 1
assert records[0].name == 'Tegan'
2 changes: 2 additions & 0 deletions databases/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,8 @@
'json_arrays',
'raw_queries',
'create_many_skip_duplicates',
'create_many_and_return',
'create_many_and_return_skip_duplicates',
'transactions',
'case_sensitivity',
'full_text_search',
Expand Down
2 changes: 2 additions & 0 deletions src/prisma/_builder.py
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@
'query_raw': 'mutation',
'query_first': 'mutation',
'create_many': 'mutation',
'create_many_and_return': 'mutation',
'execute_raw': 'mutation',
'delete_many': 'mutation',
'update_many': 'mutation',
Expand All @@ -61,6 +62,7 @@
'query_raw': 'queryRaw',
'query_first': 'queryRaw',
'create_many': 'createMany{model}',
'create_many_and_return': 'createMany{model}AndReturn',
'execute_raw': 'executeRaw',
'delete_many': 'deleteMany{model}',
'update_many': 'updateMany{model}',
Expand Down
1 change: 1 addition & 0 deletions src/prisma/_types.py
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ class _GenericAlias(Protocol):
'update',
'upsert',
'create_many',
'create_many_and_return',
'delete_many',
'update_many',
# read queries
Expand Down
5 changes: 2 additions & 3 deletions src/prisma/cli/commands/dev.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,13 +29,12 @@ def playground(schema: Optional[str], skip_generate: bool) -> None:
else:
generate_client(schema=schema, reload=True)

# TODO: this assumes we are generating to the same location that we are being invoked from
from ... import Prisma
from ...engine import QueryEngine
from ...engine import QueryEngine, BaseQueryEngine

client = Prisma()
engine_class = client._engine_class
if engine_class.__name__ == 'QueryEngine':
if issubclass(engine_class, BaseQueryEngine):
with temp_env_update({'__PRISMA_PY_PLAYGROUND': '1'}):
maybe_async_run(client.connect)

Expand Down
1 change: 1 addition & 0 deletions src/prisma/engine/__init__.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
from ._query import (
BaseQueryEngine as BaseQueryEngine,
SyncQueryEngine as SyncQueryEngine,
AsyncQueryEngine as AsyncQueryEngine,
)
Expand Down
68 changes: 67 additions & 1 deletion src/prisma/generator/templates/actions.py.jinja
Original file line number Diff line number Diff line change
Expand Up @@ -190,7 +190,7 @@ class {{ model.name }}Actions(Generic[{{ ModelType }}]):
) -> int:
"""Create multiple {{ model.name }} records at once.

This function is *not* available when using SQLite.
The `skip_duplicates` argument is not supported when using SQLite, MongoDB or SQLServer

Parameters
----------
Expand Down Expand Up @@ -247,6 +247,72 @@ class {{ model.name }}Actions(Generic[{{ ModelType }}]):
)
return int(resp['data']['result']['count'])

{{ maybe_async_def }}create_many_and_return(
self,
data: List[types.{{ model.name }}CreateWithoutRelationsInput],
*,
skip_duplicates: Optional[bool] = None,
) -> List[{{ ModelType }}]:
"""Create multiple {{ model.name }} records at once.

This method is **not supported** on MariaDB or MySQL.

The `skip_duplicates` argument is **not supported** on SQLite.

Parameters
----------
data
List of {{ model.name }} record data
skip_duplicates
Boolean flag for ignoring unique constraint errors

Returns
-------
List[{{ RawModelType }}]
The list of all {{ model.name }} records that could be found

Raises
------
prisma.errors.UnsupportedDatabaseError
Attempting to query when using SQLite
prisma.errors.UniqueViolationError
A unique constraint check has failed, these can be ignored with the `skip_duplicates` argument
{{ query_error_doc }}
{{ base_error_doc }}

Example
-------
```py
records = {{ maybe_await }}{{ model.name }}.prisma().create_many_and_return(
data=[
{% for _ in range(2) %}
{
# data to create a {{ model.name }} record
{% for field in model.scalar_fields %}
{% if field.required_on_create %}
'{{ field.name }}': {{ field.get_sample_data() }},
{% endif %}
{% endfor %}
},
{% endfor %}
],
skip_duplicates=True,
)
```
"""
if skip_duplicates and self._client._active_provider in CREATE_MANY_SKIP_DUPLICATES_UNSUPPORTED:
raise errors.UnsupportedDatabaseError(self._client._active_provider, 'create_many_skip_duplicates')

resp = {{ maybe_await }}self._client._execute(
method='create_many_and_return',
model=self._model,
arguments={
'data': data,
'skipDuplicates': skip_duplicates,
},
)
return [model_parse(self._model, r) for r in resp['data']['result']]

{{ maybe_async_def }}delete(
self,
where: types.{{ model.name }}WhereUniqueInput,
Expand Down
Loading