Skip to content

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 fetched via DRF's get_object() (so set queryset and lookup_field on the subclass), or by overriding get_object() for custom resolution.

Configure by setting spec to a :class:ServiceSpec. The spec's success_status defaults to 200 OK when unset.

ServiceDeleteView

Bases: MutationFlowMixin, GenericAPIView

DELETE endpoint that runs a service callable.

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_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.selector overrides get_queryset(); None falls back to the inherited queryset attribute.
  • spec.output_serializer overrides get_serializer_class(); None falls back to DRF's standard serializer_class attribute.

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

get_selector_kwargs() -> dict[str, Any]

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.selector overrides get_object(); None falls back to self.get_object() — standard DRF lookup using queryset and lookup_field. Returning None or raising Model.DoesNotExist results in a 404.
  • spec.output_serializer overrides get_serializer_class(); None falls back to DRF's standard serializer_class attribute.

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):

  1. get_service_kwargs(self) — global fallback on the view.
  2. get_<action>_service_kwargs(self) — per-action override (viewsets only; self.action must be set).
  3. ServiceSpec.kwargs — per-spec callable, co-located with the service it feeds.

get_service_kwargs

get_service_kwargs() -> dict[str, Any]

Hook for additional kwargs available to every mutation service.

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; None on 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:

  1. view.<catch_all_hook>() — global fallback declared on the view (get_service_kwargs / get_selector_kwargs). No-op when the method is not present.
  2. view.<action_hook>() — per-action method on the view, e.g. get_create_service_kwargs / get_list_selector_kwargs. Skipped when action_hook is None (e.g. on standalone single-purpose views) or the method is absent.
  3. 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.

get_class_attr

get_class_attr(view: Any, name: str) -> Any

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

resolve_callable_kwargs(fn: Callable[..., Any], pool: dict[str, Any]) -> dict[str, Any]

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

is_overridden(view_cls: type, base_cls: type, method_name: str) -> bool

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 output_selector.

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) -> 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).

validate_mutation_view_spec

validate_mutation_view_spec(view_cls: type, *, has_instance: bool) -> None

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_selector_view_spec(view_cls: type) -> None

Validate view_cls.spec on a standalone selector view.

No-op when spec is unset or carries no selector (the spec then means "use vanilla DRF" and there is nothing to validate).