Skip to content

Commit 35e2d3f

Browse files
committed
postprocessor options as kwargs
1 parent 31c1998 commit 35e2d3f

File tree

2 files changed

+47
-15
lines changed

2 files changed

+47
-15
lines changed

src/django_idom/hooks.py

Lines changed: 22 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -159,11 +159,11 @@ def execute_query() -> None:
159159

160160
# Use a custom postprocessor, if provided
161161
if query_options.postprocessor:
162-
query_options.postprocessor(data, query_options)
162+
query_options.postprocessor(data, **query_options.postprocessor_options)
163163

164164
# Use the default postprocessor
165165
else:
166-
_postprocess_django_query(data, query_options.postprocessor_options)
166+
_postprocess_django_query(data, **query_options.postprocessor_options)
167167
except Exception as e:
168168
set_data(None)
169169
set_loading(False)
@@ -235,7 +235,9 @@ def reset() -> None:
235235
return Mutation(call, loading, error, reset)
236236

237237

238-
def _postprocess_django_query(data: QuerySet | Model, options: dict[str, Any]) -> None:
238+
def _postprocess_django_query(
239+
data: QuerySet | Model, /, many_to_many: bool = False, many_to_one: bool = False
240+
) -> None:
239241
"""Recursively fetch all fields within a `Model` or `QuerySet` to ensure they are not performed lazily.
240242
241243
Some behaviors can be modified through `query_options` attributes."""
@@ -244,7 +246,11 @@ def _postprocess_django_query(data: QuerySet | Model, options: dict[str, Any]) -
244246
# https://github.com/typeddjango/django-stubs/issues/704
245247
if isinstance(data, QuerySet): # type: ignore[misc]
246248
for model in data:
247-
_postprocess_django_query(model, options)
249+
_postprocess_django_query(
250+
model,
251+
many_to_many=many_to_many,
252+
many_to_one=many_to_one,
253+
)
248254

249255
# `Model` instances
250256
elif isinstance(data, Model):
@@ -255,20 +261,26 @@ def _postprocess_django_query(data: QuerySet | Model, options: dict[str, Any]) -
255261
with contextlib.suppress(AttributeError):
256262
getattr(data, field.name)
257263

258-
if options.get("many_to_one", False) and type(field) == ManyToOneRel:
264+
if many_to_one and type(field) == ManyToOneRel:
259265
prefetch_fields.append(f"{field.name}_set")
260266

261-
elif options.get("many_to_many", False) and isinstance(
262-
field, ManyToManyField
263-
):
267+
elif many_to_many and isinstance(field, ManyToManyField):
264268
prefetch_fields.append(field.name)
265269
_postprocess_django_query(
266-
getattr(data, field.name).get_queryset(), options
270+
getattr(data, field.name).get_queryset(),
271+
many_to_many=many_to_many,
272+
many_to_one=many_to_one,
267273
)
268274

269275
if prefetch_fields:
270276
prefetch_related_objects([data], *prefetch_fields)
271277

272278
# Unrecognized type
273279
else:
274-
raise ValueError(f"Expected a Model or QuerySet, got {data!r}")
280+
raise TypeError(
281+
f"Django query postprocessor expected a Model or QuerySet, got {data!r}.\n"
282+
"One of the following may have occurred:\n"
283+
" - You are using a non-Django ORM.\n"
284+
" - You are attempting to use `use_query` to fetch a non-ORM data.\n\n"
285+
"If these situations seem correct, you may want to consider disabling the postprocessor via `QueryOptions`."
286+
)

src/django_idom/types.py

Lines changed: 25 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,17 @@
11
from __future__ import annotations
22

33
from dataclasses import dataclass, field
4-
from typing import Any, Awaitable, Callable, Generic, Optional, Sequence, TypeVar, Union
4+
from typing import (
5+
Any,
6+
Awaitable,
7+
Callable,
8+
Generic,
9+
Optional,
10+
Protocol,
11+
Sequence,
12+
TypeVar,
13+
Union,
14+
)
515

616
from django.db.models.base import Model
717
from django.db.models.query import QuerySet
@@ -53,15 +63,25 @@ class ViewComponentIframe:
5363
kwargs: dict
5464

5565

66+
class Postprocessor(Protocol):
67+
def __call__(self, data: Any, **kwargs: Any) -> None:
68+
...
69+
70+
5671
@dataclass
5772
class QueryOptions:
5873
"""Configuration options that can be provided to `use_query`."""
5974

6075
postprocessor_options: dict[str, Any] = field(default_factory=lambda: {})
6176
"""Configuration values usable by the `postprocessor`."""
6277

63-
postprocessor: Callable[[_Data, QueryOptions], None] | None = None
64-
"""A post processing callable that can read/modify the query `data` and the `QueryOptions` object.
78+
postprocessor: Postprocessor | None = None
79+
"""A post processing callable that can read/modify the query `data`.
80+
81+
`postprocessor_options` are provided to this `postprocessor` as keyword arguments.
82+
83+
If `None`, the default postprocessor is used.
6584
66-
If unset, the default handler is used. This handler can be configured via `postprocessor_options`
67-
to recursively fetch all fields to ensure queries are not performed lazily."""
85+
This default Django query postprocessor prevents Django's lazy query execution, and
86+
additionally can be configured via `postprocessor_options` to recursively fetch
87+
`many_to_many` and `many_to_one` fields."""

0 commit comments

Comments
 (0)