1
+ from asyncio import get_event_loop
2
+
1
3
import aiodataloader
2
4
import sqlalchemy
3
5
from sqlalchemy .orm import Session , strategies
4
6
from sqlalchemy .orm .query import QueryContext
5
7
6
8
from .utils import is_sqlalchemy_version_less_than
7
9
10
+ # Cache this across `batch_load_fn` calls
11
+ # This is so SQL string generation is cached under-the-hood via `bakery`
12
+ # Caching the relationship loader for each relationship prop.
13
+ RELATIONSHIP_LOADERS_CACHE = {}
8
14
9
- def get_batch_resolver (relationship_prop ):
10
15
11
- # Cache this across `batch_load_fn` calls
12
- # This is so SQL string generation is cached under-the-hood via `bakery`
13
- selectin_loader = strategies .SelectInLoader (relationship_prop , (('lazy' , 'selectin' ),))
16
+ def get_batch_resolver (relationship_prop ):
14
17
15
18
class RelationshipLoader (aiodataloader .DataLoader ):
16
19
cache = False
17
20
21
+ def __init__ (self , relationship_prop , selectin_loader ):
22
+ super ().__init__ ()
23
+ self .relationship_prop = relationship_prop
24
+ self .selectin_loader = selectin_loader
25
+
18
26
async def batch_load_fn (self , parents ):
19
27
"""
20
28
Batch loads the relationships of all the parents as one SQL statement.
@@ -38,8 +46,8 @@ async def batch_load_fn(self, parents):
38
46
SQLAlchemy's main maitainer suggestion.
39
47
See https://git.io/JewQ7
40
48
"""
41
- child_mapper = relationship_prop .mapper
42
- parent_mapper = relationship_prop .parent
49
+ child_mapper = self . relationship_prop .mapper
50
+ parent_mapper = self . relationship_prop .parent
43
51
session = Session .object_session (parents [0 ])
44
52
45
53
# These issues are very unlikely to happen in practice...
@@ -62,26 +70,42 @@ async def batch_load_fn(self, parents):
62
70
query_context = parent_mapper_query ._compile_context ()
63
71
64
72
if is_sqlalchemy_version_less_than ('1.4' ):
65
- selectin_loader ._load_for_path (
73
+ self . selectin_loader ._load_for_path (
66
74
query_context ,
67
75
parent_mapper ._path_registry ,
68
76
states ,
69
77
None ,
70
78
child_mapper
71
79
)
72
80
else :
73
- selectin_loader ._load_for_path (
81
+ self . selectin_loader ._load_for_path (
74
82
query_context ,
75
83
parent_mapper ._path_registry ,
76
84
states ,
77
85
None ,
78
86
child_mapper ,
79
87
None
80
88
)
81
-
82
- return [getattr (parent , relationship_prop .key ) for parent in parents ]
83
-
84
- loader = RelationshipLoader ()
89
+ return [getattr (parent , self .relationship_prop .key ) for parent in parents ]
90
+
91
+ def _get_loader (relationship_prop ):
92
+ """Retrieve the cached loader of the given relationship."""
93
+ loader = RELATIONSHIP_LOADERS_CACHE .get (relationship_prop , None )
94
+ if loader is None :
95
+ selectin_loader = strategies .SelectInLoader (
96
+ relationship_prop ,
97
+ (('lazy' , 'selectin' ),)
98
+ )
99
+ loader = RelationshipLoader (
100
+ relationship_prop = relationship_prop ,
101
+ selectin_loader = selectin_loader
102
+ )
103
+ RELATIONSHIP_LOADERS_CACHE [relationship_prop ] = loader
104
+ else :
105
+ loader .loop = get_event_loop ()
106
+ return loader
107
+
108
+ loader = _get_loader (relationship_prop )
85
109
86
110
async def resolve (root , info , ** args ):
87
111
return await loader .load (root )
0 commit comments