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

Commit dc93180

Browse files
authored
queryset-level update method (#112)
1 parent 843e503 commit dc93180

File tree

4 files changed

+54
-11
lines changed

4 files changed

+54
-11
lines changed

docs/making_queries.md

Lines changed: 12 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -138,7 +138,7 @@ It's not very common, but to delete all rows in a table:
138138
await Note.objects.delete()
139139
```
140140

141-
You can also call delete on a queried instance:
141+
You can also call `.delete()` on a queried instance:
142142

143143
```python
144144
note = await Note.objects.first()
@@ -182,16 +182,23 @@ note = await Note.objects.get(id=1)
182182

183183
### .update()
184184

185-
`.update()` method is defined on model instances.
186-
You need to query to get a `Note` instance first:
185+
You can update instances by calling `.update()` on a queryset:
187186

188187
```python
189-
note = await Note.objects.first()
188+
await Note.objects.filter(completed=True).update(completed=False)
190189
```
191190

192-
Then update the field(s):
191+
It's not very common, but to update all rows in a table:
193192

194193
```python
194+
await Note.objects.update(completed=False)
195+
```
196+
197+
You can also call `.update()` on a queried instance:
198+
199+
```python
200+
note = await Note.objects.first()
201+
195202
await note.update(completed=True)
196203
```
197204

orm/models.py

Lines changed: 18 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -407,7 +407,23 @@ async def delete(self) -> None:
407407
for filter_clause in self.filter_clauses:
408408
expr = expr.where(filter_clause)
409409

410-
return await self.database.execute(expr)
410+
await self.database.execute(expr)
411+
412+
async def update(self, **kwargs) -> None:
413+
fields = {
414+
key: field.validator
415+
for key, field in self.model_cls.fields.items()
416+
if key in kwargs
417+
}
418+
validator = typesystem.Schema(fields=fields)
419+
kwargs = validator.validate(kwargs)
420+
421+
expr = self.table.update().values(**kwargs)
422+
423+
for filter_clause in self.filter_clauses:
424+
expr = expr.where(filter_clause)
425+
426+
await self.database.execute(expr)
411427

412428
async def get_or_create(self, **kwargs) -> typing.Tuple[typing.Any, bool]:
413429
try:
@@ -459,19 +475,15 @@ def table(self) -> sqlalchemy.Table:
459475
return self.__class__.table
460476

461477
async def update(self, **kwargs):
462-
# Validate the keyword arguments.
463478
fields = {
464479
key: field.validator for key, field in self.fields.items() if key in kwargs
465480
}
466481
validator = typesystem.Schema(fields=fields)
467482
kwargs = validator.validate(kwargs)
468483

469-
# Build the update expression.
470484
pk_column = getattr(self.table.c, self.pkname)
471-
expr = self.table.update()
472-
expr = expr.values(**kwargs).where(pk_column == self.pk)
485+
expr = self.table.update().values(**kwargs).where(pk_column == self.pk)
473486

474-
# Perform the update.
475487
await self.database.execute(expr)
476488

477489
# Update the model instance.

tests/test_foreignkey.py

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -178,3 +178,13 @@ async def test_queryset_delete_with_fk():
178178
await Track.objects.filter(album=malibu).delete()
179179
assert await Track.objects.filter(album=malibu).count() == 0
180180
assert await Track.objects.filter(album=wall).count() == 1
181+
182+
183+
async def test_queryset_update_with_fk():
184+
malibu = await Album.objects.create(name="Malibu")
185+
wall = await Album.objects.create(name="The Wall")
186+
await Track.objects.create(album=malibu, title="The Bird", position=1)
187+
188+
await Track.objects.filter(album=malibu).update(album=wall)
189+
assert await Track.objects.filter(album=malibu).count() == 0
190+
assert await Track.objects.filter(album=wall).count() == 1

tests/test_models.py

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -273,3 +273,17 @@ async def test_queryset_delete():
273273

274274
await Product.objects.delete()
275275
assert await Product.objects.count() == 0
276+
277+
278+
async def test_queryset_update():
279+
shirt = await Product.objects.create(name="Shirt", rating=5)
280+
tie = await Product.objects.create(name="Tie", rating=5)
281+
282+
await Product.objects.filter(pk=shirt.id).update(rating=3)
283+
shirt = await Product.objects.get(pk=shirt.id)
284+
assert shirt.rating == 3
285+
assert await Product.objects.get(pk=tie.id) == tie
286+
287+
await Product.objects.update(rating=3)
288+
tie = await Product.objects.get(pk=tie.id)
289+
assert tie.rating == 3

0 commit comments

Comments
 (0)