|
1 | 1 | from __future__ import annotations
|
2 | 2 |
|
3 |
| -from dataclasses import dataclass |
4 | 3 | from threading import Thread
|
5 | 4 | from types import FunctionType
|
6 | 5 | from typing import (
|
7 |
| - Dict, |
8 | 6 | Type,
|
9 | 7 | Union,
|
10 | 8 | Any,
|
11 |
| - Awaitable, |
12 | 9 | Callable,
|
13 | 10 | DefaultDict,
|
14 |
| - Optional, |
15 | 11 | Sequence,
|
16 | 12 | Type,
|
17 | 13 | Union,
|
|
20 | 16 | NamedTuple,
|
21 | 17 | )
|
22 | 18 |
|
| 19 | +from django.db.models.base import Model |
| 20 | +from django.db.models.query import QuerySet |
23 | 21 | from typing_extensions import ParamSpec
|
24 | 22 | from idom import use_callback
|
25 | 23 |
|
@@ -68,6 +66,7 @@ def use_websocket() -> IdomWebsocket:
|
68 | 66 | def use_query(
|
69 | 67 | query: Callable[_Params, _Data],
|
70 | 68 | *args: _Params.args,
|
| 69 | + fetch_deferred_fields: bool = True, |
71 | 70 | **kwargs: _Params.kwargs,
|
72 | 71 | ) -> Query[_Data]:
|
73 | 72 | given_query = query
|
@@ -99,15 +98,33 @@ def execute_query():
|
99 | 98 |
|
100 | 99 | def thread_target():
|
101 | 100 | try:
|
102 |
| - returned = query(*args, **kwargs) |
| 101 | + query_result = query(*args, **kwargs) |
103 | 102 | except Exception as e:
|
104 | 103 | set_data(UNDEFINED)
|
105 | 104 | set_loading(False)
|
106 | 105 | set_error(e)
|
107 |
| - else: |
108 |
| - set_data(returned) |
109 |
| - set_loading(False) |
110 |
| - set_error(None) |
| 106 | + return |
| 107 | + |
| 108 | + if isinstance(query_result, QuerySet): |
| 109 | + if fetch_deferred_fields: |
| 110 | + for model in query_result: |
| 111 | + _fetch_deferred_fields(model) |
| 112 | + else: |
| 113 | + # still force query set to execute |
| 114 | + for _ in query_result: |
| 115 | + pass |
| 116 | + elif isinstance(query_result, Model): |
| 117 | + if fetch_deferred_fields: |
| 118 | + _fetch_deferred_fields(query_result) |
| 119 | + elif fetch_deferred_fields: |
| 120 | + raise ValueError( |
| 121 | + f"Expected {query} to return Model or Query because " |
| 122 | + f"{fetch_deferred_fields=}, got {query_result!r}" |
| 123 | + ) |
| 124 | + |
| 125 | + set_data(query_result) |
| 126 | + set_loading(False) |
| 127 | + set_error(None) |
111 | 128 |
|
112 | 129 | # We need to run this in a thread so we don't prevent rendering when loading.
|
113 | 130 | # I'm also hoping that Django is ok with this since this thread won't have an
|
@@ -167,3 +184,13 @@ class Mutation(NamedTuple, Generic[_Params]):
|
167 | 184 | loading: bool
|
168 | 185 | error: Exception | None
|
169 | 186 | reset: Callable[[], None]
|
| 187 | + |
| 188 | + |
| 189 | +_Model = TypeVar("_Model", bound=Model) |
| 190 | + |
| 191 | + |
| 192 | +def _fetch_deferred_fields(model: _Model) -> _Model: |
| 193 | + for field in model.get_deferred_fields(): |
| 194 | + value = getattr(model, field) |
| 195 | + if isinstance(value, Model): |
| 196 | + _fetch_deferred_fields(value) |
0 commit comments