Skip to content

Commit 99ac352

Browse files
committed
use_query options via type hints
1 parent 50de9c4 commit 99ac352

File tree

5 files changed

+47
-52
lines changed

5 files changed

+47
-52
lines changed

docs/src/features/components.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@ Convert any Django view into a IDOM component by using this decorator. Compatibl
3939

4040
| Type | Description |
4141
| --- | --- |
42-
| `_ViewComponentConstructor` | A function that takes `request: HttpRequest | None, *args: Any, key: Key | None, **kwargs: Any` and returns an IDOM component. |
42+
| `_ViewComponentConstructor` | A function that takes `request, *args, key, **kwargs` and returns an IDOM component. All parameters are directly provided to your view, besides `key` which is used by IDOM. |
4343

4444
??? Warning "Potential information exposure when using `compatibility = True`"
4545

src/django_idom/hooks.py

Lines changed: 41 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,16 @@
33
import asyncio
44
import contextlib
55
import logging
6-
from typing import Any, Awaitable, Callable, DefaultDict, Sequence, Union, cast
6+
from typing import (
7+
Any,
8+
Awaitable,
9+
Callable,
10+
DefaultDict,
11+
Sequence,
12+
Union,
13+
cast,
14+
overload,
15+
)
716

817
from channels.db import database_sync_to_async as _database_sync_to_async
918
from django.db.models import ManyToManyField, prefetch_related_objects
@@ -74,10 +83,30 @@ def use_websocket() -> IdomWebsocket:
7483
return websocket
7584

7685

86+
@overload
87+
def use_query(
88+
query: Callable[_Params, _Result | None],
89+
options: QueryOptions,
90+
/,
91+
*args: _Params.args,
92+
**kwargs: _Params.kwargs,
93+
) -> Query[_Result | None]:
94+
...
95+
96+
97+
@overload
7798
def use_query(
7899
query: Callable[_Params, _Result | None],
100+
/,
79101
*args: _Params.args,
80102
**kwargs: _Params.kwargs,
103+
) -> Query[_Result | None]:
104+
...
105+
106+
107+
def use_query(
108+
*args: Any,
109+
**kwargs: Any,
81110
) -> Query[_Result | None]:
82111
"""Hook to fetch a Django ORM query.
83112
@@ -87,6 +116,14 @@ def use_query(
87116
88117
Keyword Args:
89118
**kwargs: Keyword arguments to pass into `query`."""
119+
query = args[0]
120+
if len(args) > 1 and isinstance(args[1], QueryOptions):
121+
query_options = args[1]
122+
args = args[2:]
123+
else:
124+
query_options = QueryOptions()
125+
args = args[1:]
126+
90127
query_ref = use_ref(query)
91128
if query_ref.current is not query:
92129
raise ValueError(f"Query function changed from {query_ref.current} to {query}.")
@@ -95,9 +132,6 @@ def use_query(
95132
data, set_data = use_state(cast(Union[_Result, None], None))
96133
loading, set_loading = use_state(True)
97134
error, set_error = use_state(cast(Union[Exception, None], None))
98-
query_options = (
99-
query if isinstance(query, QueryOptions) else QueryOptions(func=query)
100-
)
101135

102136
@use_callback
103137
def refetch() -> None:
@@ -109,8 +143,8 @@ def refetch() -> None:
109143
def add_refetch_callback() -> Callable[[], None]:
110144
# By tracking callbacks globally, any usage of the query function will be re-run
111145
# if the user has told a mutation to refetch it.
112-
_REFETCH_CALLBACKS[query_options.func].add(refetch)
113-
return lambda: _REFETCH_CALLBACKS[query_options.func].remove(refetch)
146+
_REFETCH_CALLBACKS[query].add(refetch)
147+
return lambda: _REFETCH_CALLBACKS[query].remove(refetch)
114148

115149
@use_effect(dependencies=None)
116150
@database_sync_to_async
@@ -120,7 +154,7 @@ def execute_query() -> None:
120154

121155
try:
122156
# Run the initial query
123-
data = query_options.func(*args, **kwargs)
157+
data = query(*args, **kwargs)
124158

125159
# Use a custom postprocessor, if provided
126160
if query_options.postprocessor:

src/django_idom/types.py

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -57,12 +57,6 @@ class ViewComponentIframe:
5757
class QueryOptions:
5858
"""A Django ORM query function, alongside some configuration values."""
5959

60-
def __call__(self, *args, **kwargs):
61-
return self.func(*args, **kwargs)
62-
63-
func: Callable
64-
"""Callable that fetches ORM object(s)."""
65-
6660
postprocessor_options: dict[str, Any] = field(
6761
default_factory=lambda: {"many_to_many": False, "many_to_one": False}
6862
)

src/django_idom/utils.py

Lines changed: 0 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -200,38 +200,3 @@ def _generate_obj_name(object: Any) -> str | None:
200200
if hasattr(object, "__class__"):
201201
return f"{object.__module__}.{object.__class__.__name__}"
202202
return None
203-
204-
205-
@overload
206-
def query_options(
207-
query_func: None = ...,
208-
evaluator: Callable[[QueryOptions], None] | None = None,
209-
**options,
210-
) -> Callable[[Callable[_Params, _Result]], QueryOptions]:
211-
...
212-
213-
214-
@overload
215-
def query_options(
216-
query_func: Callable[_Params, _Result],
217-
evaluator: Callable[[QueryOptions], None] | None = None,
218-
**options,
219-
) -> QueryOptions:
220-
...
221-
222-
223-
def query_options(
224-
query_func: Callable[_Params, _Result] | None = None,
225-
evaluator: Callable[[QueryOptions], None] | None = None,
226-
**options,
227-
) -> QueryOptions | Callable[[Callable[_Params, _Result]], QueryOptions]:
228-
def decorator(query_func: Callable[_Params, _Result]):
229-
if not query_func:
230-
raise ValueError("A query function must be provided to `query_options`")
231-
232-
query_options = QueryOptions(func=query_func, postprocessor=evaluator)
233-
if options:
234-
query_options.postprocessor_options.update(options)
235-
return query_options
236-
237-
return decorator(query_func) if query_func else decorator

tests/test_app/components.py

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
import django_idom
99
from django_idom.components import view_to_component
1010
from django_idom.hooks import use_mutation, use_query
11-
from django_idom.utils import query_options
11+
from django_idom.types import QueryOptions
1212

1313
from . import views
1414

@@ -166,7 +166,6 @@ def create_relational_parent() -> RelationalParent:
166166
return parent
167167

168168

169-
@query_options(many_to_many=True, many_to_one=True)
170169
def get_relational_parent_query():
171170
return RelationalParent.objects.first() or create_relational_parent()
172171

@@ -184,7 +183,10 @@ def get_foriegn_child_query():
184183

185184
@component
186185
def relational_query():
187-
relational_parent = use_query(get_relational_parent_query)
186+
relational_parent = use_query(
187+
get_relational_parent_query,
188+
QueryOptions(postprocessor_options={"many_to_many": True, "many_to_one": True}),
189+
)
188190
foriegn_child = use_query(get_foriegn_child_query)
189191

190192
if not relational_parent.data or not foriegn_child.data:

0 commit comments

Comments
 (0)