Selectors¶
Protocols¶
Each Protocol is parameterised on input / instance / result types only;
**extras is typed Any. Strict-typed extras live on the user's function
signature via **extras: Unpack[YourKw] — see
Typing services and selectors for the full pattern.
Selector ¶
Bases: Protocol
Any callable returning a queryset, a list or one object.
The loosest description of a SelectorSpec's selector: dispatch binds
it from the keywords it declares, drawn from the keyword pool and the reads
the spec declares, so no signature is imposed and conforming is optional.
For a typed result, use
ListSelector
or RetrieveSelector.
Runtime-checkable, and an isinstance check asks only whether the object
is callable: an async def passes it too, so it cannot tell the two
kinds apart.
AsyncSelector ¶
Bases: Protocol
The async def sibling of Selector.
A SelectorSpec's selector may be async def, and
adispatch awaits it.
To a type checker the two Protocols differ in the return, an awaitable
here. An isinstance check cannot tell them apart, since both ask only
whether the object is callable;
is_async is the run-time
question.
ListSelector ¶
Bases: Protocol[ResultT]
The structural shape of the selector of a LIST SelectorSpec.
It returns the rows, a queryset or any other iterable; a queryset is what
the spec's shaping (extend_queryset and its peers) can act on. Every
keyword arrives through **extras.
See CreateService
for **extras and strictly typed extras.
RetrieveSelector ¶
Bases: Protocol[ResultT]
The structural shape of the selector of a RETRIEVE SelectorSpec.
It returns the one row, or None for a row that is not there, which
dispatch answers as not-found unless the spec declares allow_none. A
selector returning a queryset, which dispatch collapses to its one row,
binds ResultT to the queryset's type. The lookup the spec reads -
typically pk - arrives through **extras with every other keyword.
See CreateService
for **extras and strictly typed extras.
Helpers¶
call_selector¶
call_selector ¶
Invoke selector with the framework's kwargs pool.
request is required; user is derived from request.user, and
view is the view DRF recorded on the request it built (a plain
HttpRequest records none). extras are spread last, so a key in them
replaces either seed; call_service drops such a key instead.
Async selectors are bridged via async_to_sync.
acall_selector¶
acall_selector
async
¶
acall_selector(
selector: Callable[..., ResultT] | Callable[..., Awaitable[ResultT]],
*,
request: Request,
**extras: Any,
) -> ResultT
Invoke selector from async code with the framework's kwargs pool.
Async selectors are awaited; sync selectors are called inline.
Dispatch¶
utils ¶
Internal selector dispatch helpers (sync + async).
apply_queryset_shaping ¶
apply_queryset_shaping(
qs: Any,
view: Any,
request: Request,
*,
select_related: Any,
prefetch_related: Any,
annotations: Any,
extend_queryset: Any,
filter_set: Any = None,
filter_data: Any = None,
source_label: str,
) -> Any
Apply the five shaping fields to qs, through the kernel's shape_queryset.
drfs' signature over django-service-specs' shaping, for a caller holding a
view and a request rather than a spec: the fields become a kernel
SelectorSpec (with a stand-in selector it never runs), and view,
request and the filter's arguments its pool. The order is the kernel's:
the declarative fields first (in declaration order), then
extend_queryset, so the user callable always sees the fully
statically-shaped queryset, and finally filter_set, so filtering
composes with shaping and runs before the retrieve .first()
materialization the caller does next.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
extend_queryset
|
Any
|
Called as |
required |
filter_set
|
Any
|
A django-filter |
None
|
filter_data
|
Any
|
The mapping the filter's arguments are read from, each of
its filters' names and nothing else. |
None
|
source_label
|
str
|
Named in the misconfiguration error to point at the
offending callable ( |
required |
Returns:
| Type | Description |
|---|---|
Any
|
The shaped queryset, or |
Raises:
| Type | Description |
|---|---|
ImproperlyConfigured
|
Shaping is configured but |
InvalidArguments
|
The kernel's, where |
check_view_object_permissions ¶
TargetGuard running
DRF's object-permission check for an HTTP view.
The HTTP counterpart of
enforce_permissions:
off HTTP a transport enforces spec.permission_classes itself, while a DRF view
has already instantiated them and exposes check_object_permissions. Both plug
into the same on_target_resolved seam, so the core stays authz-agnostic on every
transport.
Gated on Model, like enforce_permissions: the core fires this hook on
the LIST branch too, with the resolved queryset, and object permissions
are per-row. None — a create, or a bulk list payload — is skipped too.
dispatch_selector_for_spec ¶
End-to-end dispatch for one SelectorSpec call from a DRF view.
Resolves the view's get_selector_kwargs / get_<action>_selector_kwargs chain
into a ViewHooks carrier,
hands off to the one
dispatch_spec
core, and translates the neutral
DispatchResult
into the view-layer contract: a RETRIEVE that resolved nothing raises NotFound,
unless the spec sets allow_none=True (then None, and the retrieve views
render 200 + JSON null). SelectorKind.LIST returns the shaped queryset.
The request is no argument mapping. Off HTTP the flat params
mapping is the argument channel; over HTTP it is empty, and a selector's
arguments come from what the spec declares it reads: a route capture of
one of its reads or a declared UrlKwarg from view.kwargs, a
declared QueryParam from the query string, and the hook chain's values.
The query string reaches the filter_set as filter_data, restricted
to the names the filter reads, so it feeds the filter without widening the
argument channel.
There is deliberately no extra_url_kwargs parameter: the core reads
view.kwargs itself, for the names the spec reads and the ones its
callables mark NotClientInput, and a capture named like a seed of the
call never replaces the seed, so a nested route like
/users/<user>/posts/ cannot let the captured value shadow the
authenticated user (test_url_kwarg_cannot_shadow_the_authenticated_user_over_http).
Taking the mapping from a caller would reopen that.
The caller must check spec.selector is not None first and fall back to
vanilla DRF otherwise.