Views¶
Mutation views¶
ServiceCreateView ¶
Bases: MutationFlowMixin, GenericAPIView
POST endpoint that runs a service callable to create a resource.
Configure by setting spec to a :class:ServiceSpec. The spec's
success_status defaults to 201 Created when unset.
ServiceUpdateView ¶
Bases: MutationFlowMixin, GenericAPIView
PUT / PATCH endpoint that runs a service callable.
The instance to update is resolved via spec.instance_selector_spec
when set — no queryset / lookup_field required on the subclass —
falling back to DRF's get_object() (set queryset and
lookup_field, or override get_object()).
Configure by setting spec to a :class:ServiceSpec. The spec's
success_status defaults to 200 OK when unset. Both verbs share
the one spec, so a forced spec.partial applies to PUT and PATCH —
set http_method_names = ["patch"] for a PATCH-only endpoint.
ServiceDeleteView ¶
Bases: MutationFlowMixin, GenericAPIView
DELETE endpoint that runs a service callable.
The instance to delete is resolved via spec.instance_selector_spec
when set — no queryset / lookup_field required on the subclass —
falling back to DRF's get_object().
Configure by setting spec to a :class:ServiceSpec. The spec's
input_serializer is optional (for delete-with-payload patterns
such as a deletion reason); success_status defaults to
204 No Content; set output_selector_spec with an
output_serializer on the spec to render a body instead.
Selector views¶
SelectorListView ¶
Bases: ListModelMixin, GenericAPIView
GET endpoint that delegates to a selector or to get_queryset().
Set spec to a :class:SelectorSpec to configure the selector and/or
the output serializer. Both fields are optional:
spec.selectoroverridesget_queryset();Nonefalls back to the inheritedquerysetattribute.spec.output_serializeroverridesget_serializer_class();Nonefalls back to DRF's standardserializer_classattribute.
spec = None (the default) keeps both as vanilla DRF.
The rest of the list flow — filter backends, pagination, response
rendering — is the standard DRF ListModelMixin.
get_selector_kwargs ¶
Hook for additional kwargs available to the selector signature.
SelectorRetrieveView ¶
Bases: RetrieveModelMixin, GenericAPIView
GET endpoint that returns a single object.
Set spec to a :class:SelectorSpec to configure the selector and/or
the output serializer. Both fields are optional:
spec.selectoroverridesget_object();Nonefalls back toself.get_object()— standard DRF lookup usingquerysetandlookup_field. ReturningNoneor raisingModel.DoesNotExistresults in a 404 — or, when the spec setsallow_none=True, a200with a JSONnullbody (the nullable-resource contract; the output serializer is skipped).spec.output_serializeroverridesget_serializer_class();Nonefalls back to DRF's standardserializer_classattribute.
spec = None (the default) keeps both as vanilla DRF.
Mutation flow mixin¶
MutationFlowMixin ¶
Provides _run_mutation for service-backed views and viewset mixins.
The actual flow lives in :func:dispatch_mutation_for_spec (so
@service_action can reach it without being a class). This mixin
is the OO entry point: the per-action mixins (ServiceCreateMixin
etc.) and the standalone single-purpose views compose it and call
self._run_mutation(...) after resolving their per-action spec.
Three layers contribute extra kwargs to every service call (most specific wins):
get_service_kwargs(self)— global fallback on the view.get_<action>_service_kwargs(self)— per-action override (viewsets only;self.actionmust be set).ServiceSpec.kwargs— per-spec callable, co-located with the service it feeds.
A symmetrical three-layer chain feeds the serializer's input dict
(merged on top of request.data before validation):
get_input_data(self, request)— global fallback on the view.get_<action>_input_data(self, request)— per-action override.ServiceSpec.input_data— per-spec callable.
Two further three-layer chains feed the context= argument passed to
the input serializer (during validation) and the output serializer
(during response rendering):
get_serializer_context(self)— DRF's default (request, view, format).get_input_serializer_context(self)/get_output_serializer_context(self)— directional fallback; defaults toself.get_serializer_context()so a singleget_serializer_contextoverride flows into both directions.get_<action>_input_serializer_context(self)/get_<action>_output_serializer_context(self)— per-action override (viewsets only;self.actionmust be set).
Each layer's result is merged with dict.update so the more specific
hook wins on overlapping keys.
get_service_kwargs ¶
Hook for additional kwargs available to every mutation service.
get_input_data ¶
Hook for extras merged on top of request.data before validation.
get_input_serializer_context ¶
Hook for the context= dict passed to the input serializer.
Defaults to :meth:get_serializer_context so overriding the
DRF-standard hook flows into the input-validation path automatically.
Override here to inject keys visible only during input validation.
get_output_serializer_context ¶
Hook for the context= dict passed to the output serializer.
Defaults to :meth:get_serializer_context so overriding the
DRF-standard hook flows into the response-rendering path
automatically. Override here to inject keys visible only during
response rendering.
get_permissions ¶
Honor spec.permission_classes on standalone mutation views.
Standalone Service*View subclasses carry spec as a class
attribute. When the spec sets permission_classes it wins over
the view's class-level permission_classes; None (the default)
falls through.
ServiceView Protocol¶
ServiceView ¶
Bases: Protocol
Minimal structural shape of a view as exposed to a kwargs provider.
Per-spec kwargs providers (ServiceSpec.kwargs / SelectorSpec.kwargs)
receive the calling view typed as :class:ServiceView. The Protocol pins
only the attributes a provider can rely on across both the standalone
Service*View classes and the viewset mixins:
request— the current DRF :class:~rest_framework.request.Request.kwargs— the URL kwargs dict resolved by the URLconf (e.g.{"pk": 7}). Empty dict on routes without captured groups.action— the viewset action name ("create","list", etc.) on viewsets;Noneon standalone single-purpose views.
Keep the surface narrow on purpose: providers should translate view state into typed kwargs for the service, not reach for view internals.
Kwarg resolution¶
utils ¶
Cross-cutting view helpers used by both mutation and query views.
resolve_extra_kwargs ¶
resolve_extra_kwargs(
view: Any,
request: Request,
*,
spec_kwargs: Callable[..., dict[str, Any]] | None,
action_hook: str | None,
catch_all_hook: str,
) -> dict[str, Any]
Collect the extras that should be merged into a service/selector pool.
Three layers, applied in order so that the more specific override the more general:
view.<catch_all_hook>()— global fallback declared on the view (get_service_kwargs/get_selector_kwargs). No-op when the method is not present.view.<action_hook>()— per-action method on the view, e.g.get_create_service_kwargs/get_list_selector_kwargs. Skipped whenaction_hookisNone(e.g. on standalone single-purpose views) or the method is absent.spec_kwargs(view, request)— per-spec callable from :attr:ServiceSpec.kwargs/ :attr:SelectorSpec.kwargs.
Each layer's result is merged with dict.update, so the spec-level
provider has the final say on any overlapping keys.
resolve_input_extras ¶
resolve_input_extras(
view: Any,
request: Request,
*,
spec_input_data: Callable[..., Mapping[str, Any]] | None,
action_hook: str | None,
catch_all_hook: str,
extras: Mapping[str, Any] | None = None,
) -> dict[str, Any]
Collect the extras to merge into the serializer input dict.
Mirrors :func:resolve_extra_kwargs but for the
input_serializer-bound data, not the service-call pool. Layers,
applied in order of increasing specificity (later wins on overlap):
view.<catch_all_hook>(request)— global fallback (get_input_data); typically returns{}.view.<action_hook>(request)— per-action method on the view (get_<action>_input_data). Skipped whenaction_hookisNone(standalone single-purpose views) or the method is absent.spec_input_data(view, request)— per-spec callable from :attr:ServiceSpec.input_data.
Each layer's result is merged with dict.update so the spec-level
provider has the final say on overlapping keys.
extras carries the resolved data available before validation —
currently the mutation target instance (None on create). Each
provider receives only the extras it declares by name (see
:func:_invoke_with_extras); legacy providers are unaffected.
layer_serializer_context ¶
layer_serializer_context(
base: Mapping[str, Any],
view: Any,
request: Request,
*,
direction_hook: str | None,
action_hook: str | None,
spec_provider: Callable[..., Mapping[str, Any]] | None = None,
extras: Mapping[str, Any] | None = None,
) -> dict[str, Any]
Layer the directional, action, and spec context hooks onto base.
Same precedence rules as :func:resolve_serializer_context, but takes
the layer-1 dict explicitly instead of calling view.get_serializer_context().
Used by get_serializer_context() overrides that need to extend
super().get_serializer_context() without recursing.
direction_hook=None skips the directional layer entirely. The
canonical use of this is _ActionSpecsMixin.get_serializer_context,
which can't safely call get_output_serializer_context because the
default implementation on :class:MutationFlowMixin would recurse
back into get_serializer_context.
extras carries the resolved data about to be serialized (the
result of a mutation, the retrieved instance, or the list
page). Each provider receives only the extras it declares by name
(see :func:_invoke_with_extras); legacy (view, request) providers
are unaffected. None is treated as an empty mapping.
resolve_serializer_context ¶
resolve_serializer_context(
view: Any,
request: Request,
*,
direction_hook: str,
action_hook: str | None,
spec_provider: Callable[..., Mapping[str, Any]] | None = None,
extras: Mapping[str, Any] | None = None,
) -> dict[str, Any]
Build the serializer context dict for one direction (input or output).
Four layers, applied in order so that the more specific override the more general:
view.get_serializer_context()— DRF's default, available on every :class:~rest_framework.generics.GenericAPIView(returnsrequest,view,format).view.<direction_hook>()— library directional fallback (get_input_serializer_context/get_output_serializer_context). Skipped when the method is absent, so plain DRF viewsets work unchanged.view.<action_hook>()— per-action override on the view, e.g.get_create_input_serializer_context/get_list_output_serializer_context. Skipped whenaction_hookisNone(standalone single-purpose views) or the method is absent.spec_provider(view, request)— per-spec callable from :attr:ServiceSpec.input_serializer_context/ :attr:ServiceSpec.output_serializer_context/ :attr:SelectorSpec.output_serializer_context. Skipped whenNone.
Each layer's result is merged with dict.update, so the spec-level
provider has the final say on overlapping keys.
extras (the resolved data — result / instance / page)
is offered to the directional, action, and spec providers by keyword,
each receiving only the names it declares. See
:func:layer_serializer_context.
get_class_attr ¶
Return the named class attribute without instance binding.
Functions stored as plain class attributes (e.g. service = my_fn)
would otherwise be wrapped in a bound method when accessed via self.
Use this helper to retrieve them as the original callable.
resolve_callable_kwargs ¶
Pick the subset of pool matching fn's declared parameters.
If fn declares **kwargs, the entire pool is passed.
Otherwise only parameters present in the signature are forwarded.
Spec validation¶
spec_validation ¶
Fail-fast validation of service / selector signatures at view setup time.
The framework already filters its kwargs pool through
:func:resolve_callable_kwargs at request time, which means a service that
declares a required kw-only parameter the framework cannot provide fails
late, deep in the dispatch path with a generic
TypeError: missing required keyword-only argument message. The helpers
here surface those errors at as_view() time with a precise diagnostic so
misconfigurations turn up at module import / URL wiring instead of at the
first request.
The validator is intentionally lenient on extras: when a callable could be
fed by ServiceSpec.kwargs / SelectorSpec.kwargs or by an overridden
get_*_kwargs method, the validator assumes those overrides supply
whatever the signature needs and only fails on the unambiguous misuses.
validate_callable_signature ¶
validate_callable_signature(
fn: Callable[..., Any],
*,
spec_label: str,
has_data: bool,
has_instance: bool,
has_result: bool,
spec_kwargs: Callable[..., Any] | None,
permissive_extras: bool,
extra_known_keys: Iterable[str] = (),
) -> None
Raise :exc:ImproperlyConfigured on a misconfigured service / selector.
has_data / has_instance / has_result describe whether those
framework-injected keys will be present for this call site. Mismatches
on those (e.g. a service requiring data when input_serializer is
unset) always fail — they cannot be papered over by the user.
For other required kw-only parameters the validator is lenient: if
permissive_extras is True (because the view overrides
get_*_kwargs or spec_kwargs is supplied) it assumes those
overrides contribute the missing keys and skips the check. Otherwise it
raises with a clear hint.
extra_known_keys lets callers extend the always-allowed set
(e.g. selectors include the URL kwargs they expect to see).
is_overridden ¶
Return True if view_cls overrides base_cls's method_name.
Used to decide whether the framework should assume an get_*_kwargs
override is contributing extras (and therefore relax signature
validation for that callable).
validate_service_spec ¶
validate_service_spec(
spec: ServiceSpec[Any, Any, Any],
*,
label: str,
has_instance: bool,
permissive_extras: bool,
) -> None
Validate a :class:ServiceSpec's service and nested selector specs.
Shared between standalone mutation views, viewset mixins, and
@service_action. has_instance is fixed by the action context
(False for create, True for update / destroy / detail actions).
validate_selector_spec ¶
validate_selector_spec(
spec: SelectorSpec[Any, Any],
*,
label: str,
expected_kind: SelectorKind | None = None,
) -> None
Validate a :class:SelectorSpec's selector.
Selectors are always permissive on extras (URL kwargs and
get_selector_kwargs are dynamic), so the only fatal misuses are
requesting framework-only keys that don't exist in the selector pool
(data, instance, result).
expected_kind (when supplied) fails fast if spec.kind does not
match — e.g. a LIST spec mounted on :class:SelectorRetrieveView
raises at as_view() time rather than producing surprising
runtime behaviour.
validate_mutation_view_spec ¶
Validate view_cls.spec on a standalone mutation view.
No-op when spec is unset (the base classes inherit a None
placeholder so as_view() itself doesn't trip).
validate_selector_view_spec ¶
Validate view_cls.spec on a standalone selector view.
No-op when spec is unset (the spec then means "use vanilla DRF" and
there is nothing to validate). expected_kind is the kind the view
is shaped for (LIST for :class:SelectorListView, RETRIEVE
for :class:SelectorRetrieveView); a spec whose kind does not
match fails fast at as_view() time.