Skip to content
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

update-or-create method #113

Merged
merged 3 commits into from
Sep 27, 2021
Merged
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
26 changes: 25 additions & 1 deletion docs/making_queries.md
Original file line number Diff line number Diff line change
Expand Up @@ -210,8 +210,32 @@ To get an existing instance matching the query, or create a new one.
This will retuurn a tuple of `instance` and `created`.

```python
note, created = await Note.objects.get_or_create(text="Going to car wash")
note, created = await Note.objects.get_or_create(
text="Going to car wash", defaults={"completed": False}
)
```

This will query a `Note` with `text` as `"Going to car wash"`,
if it doesn't exist, it will use `defaults` argument to create the new instance.

!!! note
Since `get_or_create()` is doing a [get()](#get), it can raise `MultipleMatches` exception.


### .update_or_create()

To update an existing instance matching the query, or create a new one.
This will retuurn a tuple of `instance` and `created`.

```python
note, created = await Note.objects.update_or_create(
text="Going to car wash", defaults={"completed": True}
)
```

This will query a `Note` with `text` as `"Going to car wash"`,
if an instance is found, it will use the `defaults` argument to update the instance.
If it matches no records, it will use the comibnation of arguments to create the new instance.

!!! note
Since `update_or_create()` is doing a [get()](#get), it can raise `MultipleMatches` exception.
17 changes: 16 additions & 1 deletion orm/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -425,11 +425,26 @@ async def update(self, **kwargs) -> None:

await self.database.execute(expr)

async def get_or_create(self, **kwargs) -> typing.Tuple[typing.Any, bool]:
async def get_or_create(
self, defaults: typing.Dict[str, typing.Any], **kwargs
) -> typing.Tuple[typing.Any, bool]:
try:
instance = await self.get(**kwargs)
return instance, False
except NoMatch:
kwargs.update(defaults)
instance = await self.create(**kwargs)
return instance, True

async def update_or_create(
self, defaults: typing.Dict[str, typing.Any], **kwargs
) -> typing.Tuple[typing.Any, bool]:
try:
instance = await self.get(**kwargs)
await instance.update(**defaults)
return instance, False
except NoMatch:
kwargs.update(defaults)
instance = await self.create(**kwargs)
return instance, True

Expand Down
2 changes: 1 addition & 1 deletion setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ def get_packages(package):
packages=get_packages(PACKAGE),
package_data={PACKAGE: ["py.typed"]},
data_files=[("", ["LICENSE.md"])],
install_requires=["anyio>=3.0.0,<4", "databases>=0.5.0", "typesystem>=0.3.0"],
install_requires=["anyio>=3.0.0,<4", "databases~=0.5", "typesystem~=0.3"],
extras_require={
"postgresql": ["asyncpg"],
"mysql": ["aiomysql"],
Expand Down
30 changes: 26 additions & 4 deletions tests/test_models.py
Original file line number Diff line number Diff line change
Expand Up @@ -254,13 +254,19 @@ async def test_model_search():


async def test_model_get_or_create():
user, created = await User.objects.get_or_create(name="Tom")

user, created = await User.objects.get_or_create(
name="Tom", defaults={"language": "Spanish"}
)
assert created is True
assert await User.objects.get(pk=user.id) == user
assert user.name == "Tom"
assert user.language == "Spanish"

user, created = await User.objects.get_or_create(name="Tom")
user, created = await User.objects.get_or_create(
name="Tom", defaults={"language": "English"}
)
assert created is False
assert user.name == "Tom"
assert user.language == "Spanish"


async def test_queryset_delete():
Expand All @@ -287,3 +293,19 @@ async def test_queryset_update():
await Product.objects.update(rating=3)
tie = await Product.objects.get(pk=tie.id)
assert tie.rating == 3


async def test_model_update_or_create():
user, created = await User.objects.update_or_create(
name="Tom", language="English", defaults={"name": "Jane"}
)
assert created is True
assert user.name == "Jane"
assert user.language == "English"

user, created = await User.objects.update_or_create(
name="Jane", language="English", defaults={"name": "Tom"}
)
assert created is False
assert user.name == "Tom"
assert user.language == "English"