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

Commit aba7907

Browse files
authored
update-or-create method (#113)
1 parent dc93180 commit aba7907

File tree

4 files changed

+68
-7
lines changed

4 files changed

+68
-7
lines changed

docs/making_queries.md

Lines changed: 25 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -210,8 +210,32 @@ To get an existing instance matching the query, or create a new one.
210210
This will retuurn a tuple of `instance` and `created`.
211211

212212
```python
213-
note, created = await Note.objects.get_or_create(text="Going to car wash")
213+
note, created = await Note.objects.get_or_create(
214+
text="Going to car wash", defaults={"completed": False}
215+
)
214216
```
215217

218+
This will query a `Note` with `text` as `"Going to car wash"`,
219+
if it doesn't exist, it will use `defaults` argument to create the new instance.
220+
216221
!!! note
217222
Since `get_or_create()` is doing a [get()](#get), it can raise `MultipleMatches` exception.
223+
224+
225+
### .update_or_create()
226+
227+
To update an existing instance matching the query, or create a new one.
228+
This will retuurn a tuple of `instance` and `created`.
229+
230+
```python
231+
note, created = await Note.objects.update_or_create(
232+
text="Going to car wash", defaults={"completed": True}
233+
)
234+
```
235+
236+
This will query a `Note` with `text` as `"Going to car wash"`,
237+
if an instance is found, it will use the `defaults` argument to update the instance.
238+
If it matches no records, it will use the comibnation of arguments to create the new instance.
239+
240+
!!! note
241+
Since `update_or_create()` is doing a [get()](#get), it can raise `MultipleMatches` exception.

orm/models.py

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -425,11 +425,26 @@ async def update(self, **kwargs) -> None:
425425

426426
await self.database.execute(expr)
427427

428-
async def get_or_create(self, **kwargs) -> typing.Tuple[typing.Any, bool]:
428+
async def get_or_create(
429+
self, defaults: typing.Dict[str, typing.Any], **kwargs
430+
) -> typing.Tuple[typing.Any, bool]:
429431
try:
430432
instance = await self.get(**kwargs)
431433
return instance, False
432434
except NoMatch:
435+
kwargs.update(defaults)
436+
instance = await self.create(**kwargs)
437+
return instance, True
438+
439+
async def update_or_create(
440+
self, defaults: typing.Dict[str, typing.Any], **kwargs
441+
) -> typing.Tuple[typing.Any, bool]:
442+
try:
443+
instance = await self.get(**kwargs)
444+
await instance.update(**defaults)
445+
return instance, False
446+
except NoMatch:
447+
kwargs.update(defaults)
433448
instance = await self.create(**kwargs)
434449
return instance, True
435450

setup.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,7 @@ def get_packages(package):
5151
packages=get_packages(PACKAGE),
5252
package_data={PACKAGE: ["py.typed"]},
5353
data_files=[("", ["LICENSE.md"])],
54-
install_requires=["anyio>=3.0.0,<4", "databases>=0.5.0", "typesystem>=0.3.0"],
54+
install_requires=["anyio>=3.0.0,<4", "databases~=0.5", "typesystem~=0.3"],
5555
extras_require={
5656
"postgresql": ["asyncpg"],
5757
"mysql": ["aiomysql"],

tests/test_models.py

Lines changed: 26 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -254,13 +254,19 @@ async def test_model_search():
254254

255255

256256
async def test_model_get_or_create():
257-
user, created = await User.objects.get_or_create(name="Tom")
258-
257+
user, created = await User.objects.get_or_create(
258+
name="Tom", defaults={"language": "Spanish"}
259+
)
259260
assert created is True
260-
assert await User.objects.get(pk=user.id) == user
261+
assert user.name == "Tom"
262+
assert user.language == "Spanish"
261263

262-
user, created = await User.objects.get_or_create(name="Tom")
264+
user, created = await User.objects.get_or_create(
265+
name="Tom", defaults={"language": "English"}
266+
)
263267
assert created is False
268+
assert user.name == "Tom"
269+
assert user.language == "Spanish"
264270

265271

266272
async def test_queryset_delete():
@@ -287,3 +293,19 @@ async def test_queryset_update():
287293
await Product.objects.update(rating=3)
288294
tie = await Product.objects.get(pk=tie.id)
289295
assert tie.rating == 3
296+
297+
298+
async def test_model_update_or_create():
299+
user, created = await User.objects.update_or_create(
300+
name="Tom", language="English", defaults={"name": "Jane"}
301+
)
302+
assert created is True
303+
assert user.name == "Jane"
304+
assert user.language == "English"
305+
306+
user, created = await User.objects.update_or_create(
307+
name="Jane", language="English", defaults={"name": "Tom"}
308+
)
309+
assert created is False
310+
assert user.name == "Tom"
311+
assert user.language == "English"

0 commit comments

Comments
 (0)