@@ -40,13 +40,55 @@ def keygetter(
40
40
obj : "Mapping[str, t.Any]" ,
41
41
path : str ,
42
42
) -> t .Union [None , t .Any , str , t .List [str ], "Mapping[str, str]" ]:
43
- """obj, "foods__breakfast", obj['foods']['breakfast'] .
43
+ """Fetch values in objects and keys, supported nested data .
44
44
45
- >>> keygetter({ "foods": { "breakfast": "cereal" } }, "foods__breakfast")
46
- 'cereal'
47
- >>> keygetter({ "foods ": { "breakfast": "cereal" } }, "foods ")
45
+ **With dictionaries**:
46
+
47
+ >>> keygetter({ "food ": { "breakfast": "cereal" } }, "food ")
48
48
{'breakfast': 'cereal'}
49
49
50
+ >>> keygetter({ "food": { "breakfast": "cereal" } }, "food__breakfast")
51
+ 'cereal'
52
+
53
+ **With objects**:
54
+
55
+ >>> from typing import Optional
56
+ >>> from dataclasses import dataclass, field
57
+
58
+ >>> @dataclass()
59
+ ... class Food:
60
+ ... fruit: list[str] = field(default_factory=list)
61
+ ... breakfast: Optional[str] = None
62
+
63
+
64
+ >>> @dataclass()
65
+ ... class Restaurant:
66
+ ... place: str
67
+ ... city: str
68
+ ... state: str
69
+ ... food: Food = field(default_factory=Food)
70
+
71
+
72
+ >>> restaurant = Restaurant(
73
+ ... place="Largo",
74
+ ... city="Tampa",
75
+ ... state="Florida",
76
+ ... food=Food(
77
+ ... fruit=["banana", "orange"], breakfast="cereal"
78
+ ... )
79
+ ... )
80
+
81
+ >>> restaurant
82
+ Restaurant(place='Largo',
83
+ city='Tampa',
84
+ state='Florida',
85
+ food=Food(fruit=['banana', 'orange'], breakfast='cereal'))
86
+
87
+ >>> keygetter(restaurant, "food")
88
+ Food(fruit=['banana', 'orange'], breakfast='cereal')
89
+
90
+ >>> keygetter(restaurant, "food__breakfast")
91
+ 'cereal'
50
92
"""
51
93
try :
52
94
sub_fields = path .split ("__" )
@@ -74,10 +116,24 @@ def parse_lookup(
74
116
75
117
If comparator not used or value not found, return None.
76
118
77
- mykey__endswith("mykey") -> "mykey" else None
78
-
79
119
>>> parse_lookup({ "food": "red apple" }, "food__istartswith", "__istartswith")
80
120
'red apple'
121
+
122
+ It can also look up objects:
123
+
124
+ >>> from dataclasses import dataclass
125
+
126
+ >>> @dataclass()
127
+ ... class Inventory:
128
+ ... food: str
129
+
130
+ >>> item = Inventory(food="red apple")
131
+
132
+ >>> item
133
+ Inventory(food='red apple')
134
+
135
+ >>> parse_lookup(item, "food__istartswith", "__istartswith")
136
+ 'red apple'
81
137
"""
82
138
try :
83
139
if isinstance (path , str ) and isinstance (lookup , str ) and path .endswith (lookup ):
@@ -264,6 +320,8 @@ class QueryList(t.Generic[T], t.List[T]):
264
320
265
321
*Experimental, unstable*.
266
322
323
+ **With dictionaries**:
324
+
267
325
>>> query = QueryList(
268
326
... [
269
327
... {
@@ -280,6 +338,7 @@ class QueryList(t.Generic[T], t.List[T]):
280
338
... },
281
339
... ]
282
340
... )
341
+
283
342
>>> query.filter(place="Chicago suburbs")[0]['city']
284
343
'Elmhurst'
285
344
>>> query.filter(place__icontains="chicago")[0]['city']
@@ -290,8 +349,119 @@ class QueryList(t.Generic[T], t.List[T]):
290
349
'Elmhurst'
291
350
>>> query.filter(foods__fruit__in="orange")[0]['city']
292
351
'Tampa'
293
- >>> query.get(foods__fruit__in="orange")['city']
352
+
353
+ >>> query.filter(foods__fruit__in="apple")
354
+ [{'place': 'Chicago suburbs',
355
+ 'city': 'Elmhurst',
356
+ 'state': 'Illinois',
357
+ 'foods':
358
+ {'fruit': ['apple', 'cantelope'], 'breakfast': 'waffles'}}]
359
+
360
+ >>> query.filter(foods__fruit__in="non_existent")
361
+ []
362
+
363
+ **With objects**:
364
+
365
+ >>> from typing import Any, Dict
366
+ >>> from dataclasses import dataclass, field
367
+
368
+ >>> @dataclass()
369
+ ... class Restaurant:
370
+ ... place: str
371
+ ... city: str
372
+ ... state: str
373
+ ... foods: Dict[str, Any]
374
+
375
+ >>> restaurant = Restaurant(
376
+ ... place="Largo",
377
+ ... city="Tampa",
378
+ ... state="Florida",
379
+ ... foods={
380
+ ... "fruit": ["banana", "orange"], "breakfast": "cereal"
381
+ ... }
382
+ ... )
383
+
384
+ >>> restaurant
385
+ Restaurant(place='Largo',
386
+ city='Tampa',
387
+ state='Florida',
388
+ foods={'fruit': ['banana', 'orange'], 'breakfast': 'cereal'})
389
+
390
+ >>> query = QueryList([restaurant])
391
+
392
+ >>> query.filter(foods__fruit__in="banana")
393
+ [Restaurant(place='Largo',
394
+ city='Tampa',
395
+ state='Florida',
396
+ foods={'fruit': ['banana', 'orange'], 'breakfast': 'cereal'})]
397
+
398
+ >>> query.filter(foods__fruit__in="banana")[0].city
399
+ 'Tampa'
400
+
401
+ >>> query.get(foods__fruit__in="banana").city
402
+ 'Tampa'
403
+
404
+ **With objects (nested)**:
405
+
406
+ >>> from typing import List, Optional
407
+ >>> from dataclasses import dataclass, field
408
+
409
+ >>> @dataclass()
410
+ ... class Food:
411
+ ... fruit: List[str] = field(default_factory=list)
412
+ ... breakfast: Optional[str] = None
413
+
414
+
415
+ >>> @dataclass()
416
+ ... class Restaurant:
417
+ ... place: str
418
+ ... city: str
419
+ ... state: str
420
+ ... food: Food = field(default_factory=Food)
421
+
422
+
423
+ >>> query = QueryList([
424
+ ... Restaurant(
425
+ ... place="Largo",
426
+ ... city="Tampa",
427
+ ... state="Florida",
428
+ ... food=Food(
429
+ ... fruit=["banana", "orange"], breakfast="cereal"
430
+ ... )
431
+ ... ),
432
+ ... Restaurant(
433
+ ... place="Chicago suburbs",
434
+ ... city="Elmhurst",
435
+ ... state="Illinois",
436
+ ... food=Food(
437
+ ... fruit=["apple", "cantelope"], breakfast="waffles"
438
+ ... )
439
+ ... )
440
+ ... ])
441
+
442
+ >>> query.filter(food__fruit__in="banana")
443
+ [Restaurant(place='Largo',
444
+ city='Tampa',
445
+ state='Florida',
446
+ food=Food(fruit=['banana', 'orange'], breakfast='cereal'))]
447
+
448
+ >>> query.filter(food__fruit__in="banana")[0].city
449
+ 'Tampa'
450
+
451
+ >>> query.get(food__fruit__in="banana").city
294
452
'Tampa'
453
+
454
+ >>> query.filter(food__breakfast="waffles")
455
+ [Restaurant(place='Chicago suburbs',
456
+ city='Elmhurst',
457
+ state='Illinois',
458
+ food=Food(fruit=['apple', 'cantelope'], breakfast='waffles'))]
459
+
460
+ >>> query.filter(food__breakfast="waffles")[0].city
461
+ 'Elmhurst'
462
+
463
+ >>> query.filter(food__breakfast="non_existent")
464
+ []
295
465
"""
296
466
297
467
data : "Sequence[T]"
@@ -308,12 +478,6 @@ def items(self) -> t.List[t.Tuple[str, T]]:
308
478
def __eq__ (
309
479
self ,
310
480
other : object ,
311
- # other: t.Union[
312
- # "QueryList[T]",
313
- # t.List[Mapping[str, str]],
314
- # t.List[Mapping[str, int]],
315
- # t.List[Mapping[str, t.Union[str, Mapping[str, t.Union[List[str], str]]]]],
316
- # ],
317
481
) -> bool :
318
482
data = other
319
483
0 commit comments