githubEdit

Prefetching Strategy

This document explains the prefetching methods used to optimize cost calculations and view performance in Gyrinx.

Overview

The cost system relies on proper prefetching to:

  1. Enable the facts system - The can_use_facts property requires with_latest_actions()

  2. Avoid N+1 queries - List and detail views need related data prefetched

  3. Optimize display methods - cost_display(), rating_display() use cached facts when available

QuerySet Methods

List.objects.with_latest_actions()

Lightweight prefetch that enables the facts system:

def with_latest_actions(self):
    """
    Prefetch the latest action for each list.

    This enables the facts system by populating the `latest_actions` attribute,
    which is checked by the `can_use_facts` property.
    """
    return self.prefetch_related(
        Prefetch(
            "actions",
            queryset=ListAction.objects.order_by(
                "list_id", "-created", "-id"
            ).distinct("list_id"),
            to_attr="latest_actions",
        ),
    )

When to use: Any view that displays list costs (campaigns, homepage, list index).

What it enables:

  • list.can_use_facts returns True

  • list.latest_action returns the most recent action

  • list.facts() can return cached values

Full optimization for list detail pages:

When to use: List detail views, edit views.

Parameters:

  • with_fighters=False (default): Just list-level data

  • with_fighters=True: Also prefetch all fighters and their equipment

List.objects.with_fighter_data()

Prefetch fighters with their full related data:

When to use: Combined with with_related_data(with_fighters=True).

Fighter-level optimization:

When to use: Fighter detail views, lists of fighters.

Equipment assignment optimization:

When to use: Equipment lists, assignment detail views.

The can_use_facts Property

The facts system is gated by can_use_facts:

Important: If you skip the prefetch, can_use_facts returns False and display methods fall back to direct calculation.

View Patterns

Multi-List Views (Campaigns, Homepage)

Use with_latest_actions() for fast cost display:

List Detail Views

Use with_related_data() for full data:

Fighter Detail Views

Use fighter-level prefetch:

The _prefetched_objects_cache Check

The facts_from_db() method checks for prefetched data to avoid redundant queries:

This means:

  • If you've already prefetched fighters, facts_from_db() reuses them

  • If you haven't, it fetches them with appropriate optimization

Performance Impact

Scenario
Without Prefetch
With Prefetch

Campaign page (10 lists)

30+ queries

3 queries

List detail page

50+ queries

5-10 queries

Fighter detail page

20+ queries

3-5 queries

Common Mistakes

1. Forgetting with_latest_actions()

2. Not using with_fighters for detail views

3. Double prefetching

See Also

Last updated