|
11 | 11 | from typing import Any, Callable, Optional, Protocol, TypeVar, Union |
12 | 12 |
|
13 | 13 | T = TypeVar("T", Any, Any) |
| 14 | +no_arg = object() |
14 | 15 |
|
15 | 16 |
|
16 | 17 | def keygetter( |
@@ -370,6 +371,9 @@ class QueryList(list[T]): |
370 | 371 | >>> query.filter(foods__fruit__in="banana")[0].city |
371 | 372 | 'Tampa' |
372 | 373 |
|
| 374 | + >>> query.get(foods__fruit__in="banana").city |
| 375 | + 'Tampa' |
| 376 | +
|
373 | 377 | **With objects (nested)**: |
374 | 378 |
|
375 | 379 | >>> from typing import Optional |
@@ -417,6 +421,9 @@ class QueryList(list[T]): |
417 | 421 | >>> query.filter(food__fruit__in="banana")[0].city |
418 | 422 | 'Tampa' |
419 | 423 |
|
| 424 | + >>> query.get(food__fruit__in="banana").city |
| 425 | + 'Tampa' |
| 426 | +
|
420 | 427 | >>> query.filter(food__breakfast="waffles") |
421 | 428 | [Restaurant(place='Chicago suburbs', |
422 | 429 | city='Elmhurst', |
@@ -506,3 +513,18 @@ def val_match(obj: Union[str, list[Any]]) -> bool: |
506 | 513 | _filter = filter_lookup |
507 | 514 |
|
508 | 515 | return self.__class__(k for k in self if _filter(k)) |
| 516 | + |
| 517 | + def get( |
| 518 | + self, |
| 519 | + matcher: Optional[Union[Callable[[T], bool], T]] = None, |
| 520 | + default: Optional[Any] = no_arg, |
| 521 | + **kwargs: Any, |
| 522 | + ) -> Optional[T]: |
| 523 | + objs = self.filter(matcher=matcher, **kwargs) |
| 524 | + if len(objs) > 1: |
| 525 | + raise Exception("Multiple objects returned") |
| 526 | + elif len(objs) == 0: |
| 527 | + if default == no_arg: |
| 528 | + raise Exception("No objects found") |
| 529 | + return default |
| 530 | + return objs[0] |
0 commit comments