Skip to content

Commit a1c9c4b

Browse files
committed
refactor to remove query & mutation options
1 parent 7b03761 commit a1c9c4b

10 files changed

+49
-61
lines changed
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
from example.models import TodoItem
22
from reactpy import component
33
from reactpy_django.hooks import use_query
4-
from reactpy_django.types import QueryOptions
54
from reactpy_django.utils import django_query_postprocessor
65

76

@@ -11,13 +10,11 @@ def get_items():
1110

1211
@component
1312
def todo_list():
14-
# These `QueryOptions` are functionally equivalent to ReactPy-Django's default values
13+
# These postprocessor options are functionally equivalent to ReactPy-Django's default values
1514
item_query = use_query(
16-
QueryOptions(
17-
postprocessor=django_query_postprocessor,
18-
postprocessor_kwargs={"many_to_many": True, "many_to_one": True},
19-
),
2015
get_items,
16+
postprocessor=django_query_postprocessor,
17+
postprocessor_kwargs={"many_to_many": True, "many_to_one": True},
2118
)
2219

2320
return item_query.data

docs/examples/python/use-mutation-thread-sensitive.py

+1-2
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
from reactpy import component, html
22
from reactpy_django.hooks import use_mutation
3-
from reactpy_django.types import MutationOptions
43

54

65
def execute_thread_safe_mutation(text):
@@ -11,8 +10,8 @@ def execute_thread_safe_mutation(text):
1110
@component
1211
def my_component():
1312
item_mutation = use_mutation(
14-
MutationOptions(thread_sensitive=False),
1513
execute_thread_safe_mutation,
14+
thread_sensitive=False,
1615
)
1716

1817
def submit_event(event):

docs/examples/python/use-query-args.py

+2-7
Original file line numberDiff line numberDiff line change
@@ -2,16 +2,11 @@
22
from reactpy_django.hooks import use_query
33

44

5-
def example_query(value: int, other_value: bool = False):
6-
...
5+
def example_query(value: int, other_value: bool = False): ...
76

87

98
@component
109
def my_component():
11-
query = use_query(
12-
example_query,
13-
123,
14-
other_value=True,
15-
)
10+
query = use_query(example_query, kwargs={"value": 123, "other_value": True})
1611

1712
return str(query.data)

docs/examples/python/use-query-postprocessor-change.py

+2-5
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
from reactpy import component
22
from reactpy_django.hooks import use_query
3-
from reactpy_django.types import QueryOptions
43

54

65
def my_postprocessor(data, example_kwarg=True):
@@ -18,11 +17,9 @@ def execute_io_intensive_operation():
1817
@component
1918
def my_component():
2019
query = use_query(
21-
QueryOptions(
22-
postprocessor=my_postprocessor,
23-
postprocessor_kwargs={"example_kwarg": False},
24-
),
2520
execute_io_intensive_operation,
21+
postprocessor=my_postprocessor,
22+
postprocessor_kwargs={"example_kwarg": False},
2623
)
2724

2825
if query.loading or query.error:

docs/examples/python/use-query-postprocessor-disable.py

+1-2
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
from reactpy import component
22
from reactpy_django.hooks import use_query
3-
from reactpy_django.types import QueryOptions
43

54

65
def execute_io_intensive_operation():
@@ -11,8 +10,8 @@ def execute_io_intensive_operation():
1110
@component
1211
def my_component():
1312
query = use_query(
14-
QueryOptions(postprocessor=None),
1513
execute_io_intensive_operation,
14+
postprocessor=None,
1615
)
1716

1817
if query.loading or query.error:

docs/examples/python/use-query-postprocessor-kwargs.py

+1-4
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
from example.models import TodoItem
22
from reactpy import component
33
from reactpy_django.hooks import use_query
4-
from reactpy_django.types import QueryOptions
54

65

76
def get_model_with_relationships():
@@ -17,10 +16,8 @@ def get_model_with_relationships():
1716
@component
1817
def my_component():
1918
query = use_query(
20-
QueryOptions(
21-
postprocessor_kwargs={"many_to_many": False, "many_to_one": False}
22-
),
2319
get_model_with_relationships,
20+
postprocessor_kwargs={"many_to_many": False, "many_to_one": False},
2421
)
2522

2623
if query.loading or query.error or not query.data:

docs/examples/python/use-query-thread-sensitive.py

+1-5
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
from reactpy import component
22
from reactpy_django.hooks import use_query
3-
from reactpy_django.types import QueryOptions
43

54

65
def execute_thread_safe_operation():
@@ -10,10 +9,7 @@ def execute_thread_safe_operation():
109

1110
@component
1211
def my_component():
13-
query = use_query(
14-
QueryOptions(thread_sensitive=False),
15-
execute_thread_safe_operation,
16-
)
12+
query = use_query(execute_thread_safe_operation, thread_sensitive=False)
1713

1814
if query.loading or query.error:
1915
return None

src/reactpy_django/hooks.py

+28-18
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,6 @@
1111
Sequence,
1212
Union,
1313
cast,
14-
overload,
1514
)
1615
from uuid import uuid4
1716

@@ -34,13 +33,11 @@
3433
FuncParams,
3534
Inferred,
3635
Mutation,
37-
MutationOptions,
3836
Query,
39-
QueryOptions,
4037
SyncPostprocessor,
4138
UserData,
4239
)
43-
from reactpy_django.utils import generate_obj_name, get_pk
40+
from reactpy_django.utils import django_query_postprocessor, generate_obj_name, get_pk
4441

4542
if TYPE_CHECKING:
4643
from channels_redis.core import RedisChannelLayer
@@ -106,19 +103,19 @@ def use_connection() -> ConnectionType:
106103

107104
def use_query(
108105
query: Callable[FuncParams, Awaitable[Inferred]] | Callable[FuncParams, Inferred],
109-
/,
110-
*args: FuncParams.args,
106+
*,
107+
kwargs: dict[str, Any] | None = None,
111108
thread_sensitive: bool = True,
112-
postprocessor: AsyncPostprocessor | SyncPostprocessor | None = None,
109+
postprocessor: (
110+
AsyncPostprocessor | SyncPostprocessor | None
111+
) = django_query_postprocessor,
113112
postprocessor_kwargs: dict[str, Any] | None = None,
114-
**kwargs: FuncParams.kwargs,
115113
) -> Query[Inferred]:
116114
"""This hook is used to execute functions in the background and return the result, \
117115
typically to read data the Django ORM.
118116
119117
Args:
120118
query: A callable that returns a Django `Model` or `QuerySet`.
121-
args: Positional arguments to passed into the `query` function.
122119
kwargs: Keyword arguments to passed into the `query` function.
123120
thread_sensitive: Whether to run the query in thread-sensitive mode. \
124121
This setting only applies to sync query functions.
@@ -131,13 +128,20 @@ def use_query(
131128
is used to prevent Django's lazy query execution and supports `many_to_many` \
132129
and `many_to_one` as `postprocessor_kwargs`.
133130
postprocessor_kwargs: Keyworded arguments passed into the `postprocessor` function.
131+
132+
Returns:
133+
A `Query` object containing the query result, loading state, error state, and a `refetch` \
134+
function to re-execute the query.
134135
"""
135136

136137
should_execute, set_should_execute = use_state(True)
137138
data, set_data = use_state(cast(Inferred, None))
138139
loading, set_loading = use_state(True)
139140
error, set_error = use_state(cast(Union[Exception, None], None))
140141
query_ref = use_ref(query)
142+
kwargs = kwargs or {}
143+
postprocessor_kwargs = postprocessor_kwargs or {}
144+
141145
if query_ref.current is not query:
142146
raise ValueError(f"Query function changed from {query_ref.current} to {query}.")
143147

@@ -146,12 +150,12 @@ async def execute_query() -> None:
146150
try:
147151
# Run the query
148152
if asyncio.iscoroutinefunction(query):
149-
new_data = await query(*args, **kwargs)
153+
new_data = await query(**kwargs)
150154
else:
151155
new_data = await database_sync_to_async(
152156
query,
153157
thread_sensitive=thread_sensitive,
154-
)(*args, **kwargs)
158+
)(**kwargs)
155159

156160
# Run the postprocessor
157161
if postprocessor:
@@ -202,15 +206,15 @@ def register_refetch_callback() -> Callable[[], None]:
202206
_REFETCH_CALLBACKS[query].add(refetch)
203207
return lambda: _REFETCH_CALLBACKS[query].remove(refetch)
204208

205-
# The query's user API
209+
# Return Query user API
206210
return Query(data, loading, error, refetch)
207211

208212

209213
def use_mutation(
210214
mutation: (
211215
Callable[FuncParams, bool | None] | Callable[FuncParams, Awaitable[bool | None]]
212216
),
213-
/,
217+
*,
214218
thread_sensitive: bool = True,
215219
refetch: Callable[..., Any] | Sequence[Callable[..., Any]] | None = None,
216220
) -> Mutation[FuncParams]:
@@ -230,6 +234,10 @@ def use_mutation(
230234
hook) or a sequence of query functions that need a `refetch` if the \
231235
mutation succeeds. This is useful for refreshing data after a mutation \
232236
has been performed.
237+
238+
Returns:
239+
A `Mutation` object that can be called to execute the mutation. This object \
240+
also contains loading state, error state, and a `reset` function.
233241
"""
234242

235243
loading, set_loading = use_state(False)
@@ -287,7 +295,7 @@ def reset() -> None:
287295
set_loading(False)
288296
set_error(None)
289297

290-
# The mutation's user API
298+
# Return mutation user API
291299
return Mutation(schedule_mutation, loading, error, reset)
292300

293301

@@ -331,11 +339,13 @@ async def _set_user_data(data: dict):
331339
await model.asave()
332340

333341
query: Query[dict | None] = use_query(
334-
QueryOptions(postprocessor=None),
335342
_get_user_data,
336-
user=user,
337-
default_data=default_data,
338-
save_default_data=save_default_data,
343+
kwargs={
344+
"user": user,
345+
"default_data": default_data,
346+
"save_default_data": save_default_data,
347+
},
348+
postprocessor=None,
339349
)
340350
mutation = use_mutation(_set_user_data, refetch=_get_user_data)
341351

src/reactpy_django/utils.py

+3-4
Original file line numberDiff line numberDiff line change
@@ -262,7 +262,7 @@ def django_query_postprocessor(
262262
) -> QuerySet | Model:
263263
"""Recursively fetch all fields within a `Model` or `QuerySet` to ensure they are not performed lazily.
264264
265-
Behaviors can be modified through `QueryOptions` within your `use_query` hook.
265+
Behavior can be modified through `postprocessor_kwargs` within your `use_query` hook.
266266
267267
Args:
268268
data: The `Model` or `QuerySet` to recursively fetch fields from.
@@ -275,8 +275,7 @@ def django_query_postprocessor(
275275
The `Model` or `QuerySet` with all fields fetched.
276276
"""
277277

278-
# `QuerySet`, which is an iterable of `Model`/`QuerySet` instances
279-
# https://github.com/typeddjango/django-stubs/issues/704
278+
# `QuerySet`, which is an iterable containing `Model`/`QuerySet` objects.
280279
if isinstance(data, QuerySet):
281280
for model in data:
282281
django_query_postprocessor(
@@ -314,7 +313,7 @@ def django_query_postprocessor(
314313
"One of the following may have occurred:\n"
315314
" - You are using a non-Django ORM.\n"
316315
" - You are attempting to use `use_query` to fetch non-ORM data.\n\n"
317-
"If these situations seem correct, you may want to consider disabling the postprocessor via `QueryOptions`."
316+
"If these situations seem correct, you may want to consider disabling the postprocessor."
318317
)
319318

320319
return data

tests/test_app/components.py

+7-8
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,6 @@
1010
from django.shortcuts import render
1111
from reactpy import component, hooks, html, web
1212
from reactpy_django.components import view_to_component, view_to_iframe
13-
from reactpy_django.types import QueryOptions
1413

1514
from test_app.models import (
1615
AsyncForiegnChild,
@@ -659,7 +658,7 @@ def custom_host(number=0):
659658
@component
660659
def broken_postprocessor_query():
661660
relational_parent = reactpy_django.hooks.use_query(
662-
QueryOptions(postprocessor=None), get_relational_parent_query
661+
get_relational_parent_query, postprocessor=None
663662
)
664663

665664
if not relational_parent.data:
@@ -720,9 +719,9 @@ async def on_submit(event):
720719
"data-fetch-error": bool(user_data_query.error),
721720
"data-mutation-error": bool(user_data_mutation.error),
722721
"data-loading": user_data_query.loading or user_data_mutation.loading,
723-
"data-username": "AnonymousUser"
724-
if current_user.is_anonymous
725-
else current_user.username,
722+
"data-username": (
723+
"AnonymousUser" if current_user.is_anonymous else current_user.username
724+
),
726725
},
727726
html.div("use_user_data"),
728727
html.button({"class": "login-1", "on_click": login_user1}, "Login 1"),
@@ -788,9 +787,9 @@ async def on_submit(event):
788787
"data-fetch-error": bool(user_data_query.error),
789788
"data-mutation-error": bool(user_data_mutation.error),
790789
"data-loading": user_data_query.loading or user_data_mutation.loading,
791-
"data-username": "AnonymousUser"
792-
if current_user.is_anonymous
793-
else current_user.username,
790+
"data-username": (
791+
"AnonymousUser" if current_user.is_anonymous else current_user.username
792+
),
794793
},
795794
html.div("use_user_data_with_default"),
796795
html.button({"class": "login-3", "on_click": login_user3}, "Login 3"),

0 commit comments

Comments
 (0)