|
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._meta.get_fields(): |
| 194 | + value = getattr(model, field.name) |
| 195 | + if isinstance(value, Model): |
| 196 | + _fetch_deferred_fields(value) |
0 commit comments