Skip to content

Latest commit

 

History

History
73 lines (54 loc) · 3.28 KB

File metadata and controls

73 lines (54 loc) · 3.28 KB

Use of Loader instance

Normally loader instance is instantiated and maintained internally by Resolver.

If you already have a loader, and the loader has added data through the prime method, you can use the loader_instance parameter to pass in the Resolver internally to skip the initialization process and use it directly. The loader instance passed in.

Taking UserLoader as an example, you can use a real and available loader src.service.user.loader:user_batch_loader, or you can use generate_single_empty_loader to generate a Loader class.

The difference between the two is that if the data passed in by loader.load(key) is not in the loader cache, batch_load_fn will be triggered to query, while generate_single_empty_loader will do nothing, if it does not exist, return None

generate_list_empty_loader returns [] by default

schema

UserLoader = generate_single_empty_loader('UserLoader')

class Sample7TaskDetail(ts.Task):
    user: Optional[us.User] = None
    def resolve_user(self, loader=LoaderDepend(UserLoader)):
        return loader.load(self.owner_id)

In the router, add_single_to_loader is used to handle the prime logic. It simulates pre-fetching user information, adds it to the loader, and then provides it to Sample7TaskDetail.

If you comment the add_single_to_loader method, you will find that all users are None

def add_single_to_loader(loader, items, get_key):
    _map = {}
    for item in items:
        _map[get_key(item)] = item
    for k, v in _map.items():
        loader.prime(k, v)

@route.get('/tasks', response_model=list[Sample7TaskDetail])
async def get_tasks(session: AsyncSession = Depends(db.get_session)):
    users = await uq.get_users(session)
    user_loader = UserLoader()
    add_single_to_loader(user_loader, users, lambda u: u.id)

    tasks = await tskq.get_tasks(session)
    tasks = [Sample7TaskDetail.model_validate(t) for t in tasks]
    tasks = await Resolver(loader_instances={UserLoader: user_loader}).resolve(tasks)
    return tasks

In the slightly more complex second example, we start from user[1] and traverse through each layer to find the stories owned by the user, the sprints to which each story belongs, and the teams to which each sprint belongs. The display starts from the Teams level and continues to unfold layer by layer.

@route.get('/user/stat', response_model=list[Sample7TeamDetail])
async def get_user_stat(session: AsyncSession = Depends(db.get_session)):
    sprint_to_story_loader = SprintToStoryLoader()
    team_to_sprint_loader = TeamToSprintLoader()

    users = await uq.get_user_by_ids([1], session)
    stories = await sq.get_stories_by_owner_ids([u.id for u in users], session)
    add_to_loader(sprint_to_story_loader, stories, lambda s: s.sprint_id)

    sprint_ids = list({s.sprint_id for s in stories})
    sprints = await spq.get_sprints_by_ids(sprint_ids, session)
    add_to_loader(team_to_sprint_loader, sprints, lambda s: s.team_id)

    team_ids = list({s.team_id for s in sprints})

    teams = await tq.get_team_by_ids(team_ids, session)
    teams = [Sample7TeamDetail.model_validate(t) for t in teams]
    teams = await Resolver(loader_instances={
        SprintToStoryLoader: sprint_to_story_loader,
        TeamToSprintLoader: team_to_sprint_loader
    }).resolve(teams)
    return teams