Views¶
Each view takes a kernel spec on spec. See Concepts for
what the edge reads from the request and how a refusal is answered. Each also
takes pool_seeds, the seeds its spec's callables are resolved with (on
MutationFlowMixin below for the mutation views); see
Registering a seed.
Mutation views¶
ServiceCreateView ¶
Bases: MutationFlowMixin, GenericAPIView
POST endpoint that runs a service callable to create a resource.
Configure by setting spec to a kernel ServiceSpec. The view'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 — no
queryset / lookup_field required on the subclass. A spec never falls
back to DRF's get_object(), its target being its own lookup's, so
as_view() refuses one that declares neither a target selector nor many.
Configure by setting spec to a kernel ServiceSpec. The view'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 — no
queryset / lookup_field required on the subclass. A spec never falls
back to DRF's get_object(), its target being its own lookup's, so
as_view() refuses one that declares neither a target selector nor many.
Configure by setting spec to a kernel ServiceSpec. Its validator is
optional (for delete-with-payload patterns such as a deletion reason); the
view's success_status defaults to 204 No Content; set
output_selector_spec with a presenter on the spec to render a body
instead.
Selector views¶
SelectorListView ¶
Bases: _ServesRaisedSchema, ListModelMixin, GenericAPIView
GET endpoint that delegates to a selector spec or to get_queryset().
A kernel SelectorSpec on spec runs through the kernel's own
dispatch and is presented by its own presenter, the page's rows
alone when the view paginates, with the affordance answers it declares;
a spec declaring neither presenter nor affordances is rendered by the
view's serializer_class, as DRF renders a queryset.
get_queryset() answers the spec's set to a GET or HEAD alone,
from the one dispatch list presents, so a permission class asking it
first does not run the selector twice; to an OPTIONS or any other method
it is DRF's own, the view's queryset.
spec = None (the default) keeps get_queryset() and the rest of the
flow as vanilla DRF. The rest of the list flow (filter backends,
pagination, response rendering) is the standard DRF ListModelMixin.
pool_seeds
class-attribute
instance-attribute
¶
The seeds the spec's callables are resolved with: the kernel's
DEFAULT_POOL_SEEDS unless the view registers others, as a class
attribute or as as_view(pool_seeds=...). One registry for the view's
life: as_view() checks the spec against it, refusing a callable that
requires a seed it does not register, and every call's checks, target
lookup and dispatch are handed that object (test_pool_seeds).
SelectorRetrieveView ¶
Bases: _ServesRaisedSchema, RetrieveModelMixin, GenericAPIView
GET endpoint that returns a single object.
A kernel SelectorSpec on spec runs through the kernel's own
dispatch and is presented by its own presenter, with the affordance
answers it declares. Its selector replaces get_object(): a miss is a
404, or, when the spec sets allow_none=True, a 200 with a JSON
null body presented by nothing (the nullable-resource contract). A spec
declaring neither presenter nor affordances is rendered by the view's
serializer_class.
spec = None (the default) keeps get_object(), standard DRF lookup
using queryset and lookup_field, as vanilla DRF.
pool_seeds
class-attribute
instance-attribute
¶
The seeds the spec's callables are resolved with: the kernel's
DEFAULT_POOL_SEEDS unless the view registers others, as a class
attribute or as as_view(pool_seeds=...). One registry for the view's
life: as_view() checks the spec against it, refusing a callable that
requires a seed it does not register, and every call's checks, target
lookup and dispatch are handed that object (test_pool_seeds).
get_object ¶
Resolve the target row through the spec's selector, if one is set.
filter_backends do not apply here. DRF runs
filter_queryset() inside its own get_object(), so overriding
that method - this view, or any hand-written override - drops them.
A tenant-scoping backend in DEFAULT_FILTER_BACKENDS scopes a
sibling list view and not this lookup. Scope the selector's own
queryset, or declare the rule as SelectorSpec.filter_set, which
the dispatcher applies on both the list and the retrieve path.
Mutation flow mixin¶
MutationFlowMixin ¶
Bases: _ServesRaisedSchema
Provides _run_mutation for service-backed views and viewset mixins.
The flow itself lives in dispatch_mutation_for_spec, so
@service_action can reach it without being a class; this mixin is the OO
entry point that the per-action mixins (ServiceCreateMixin and friends)
and the standalone single-purpose views compose, calling
self._run_mutation(...) after resolving their per-action spec.
Four hook chains feed a mutation, each layered view-wide → per-action →
per-spec and merged with dict.update so the more specific hook wins on
overlapping keys:
- extra service kwargs:
get_service_kwargs→get_<action>_service_kwargs→ the spec'skwargsprovider. - the serializer's input dict, merged on top of
request.databefore validation:get_input_data→get_<action>_input_data→ the spec'sinput_dataprovider. - the input serializer's
context=:get_serializer_context(DRF's own) →get_input_serializer_context→get_<action>_input_serializer_context→ theSerializerValidator'sserializer_context. - the output serializer's
context=: the same chain withoutputin place ofinput, applied during response rendering.
The per-action layer reads self.action, so it applies to viewsets only.
A kernel spec declares neither HTTP answer, so the response's two HTTP hooks
are the view's: success_status and response_finalizer below, read
off the class unbound. On a viewset an action may declare its own, which
outranks these: @service_action's keywords, or a
get_<action>_success_status / get_<action>_response_finalizer hook
(action_answer, test_per_action_answers).
success_status
class-attribute
¶
The success status: an int, or a callable declaring any of
result / instance / request / view, or None for the
action's default (201 create, 200 update, 204 delete). A body
never goes out under 204, so a status resolving to it answers 200
with one; set, it applies to an answer with nothing to present too
(test_callable_success_status). On a DiscriminatedAction a callable may
also declare variant, the key the discriminator chose.
response_finalizer
class-attribute
¶
The post-presentation hook on a 2xx: declares any of
response / result / request / view / instance / data
and returns a Response to replace the one built, or None to keep it
(test_response_finalizer). On a DiscriminatedAction it may also declare
variant, the key the discriminator chose.
lenient_json_body
class-attribute
instance-attribute
¶
Whether this view's serializer alone answers a JSON body, as it answers a
form body: {"title": 5} for a CharField is "5", and every refusal
is DRF's, in DRF's words. None reads SPEC_REST_LENIENT_JSON_BODY, which
is False unset, so the spec's declared types are checked first. Only what
this view dispatches changes: the spec's schema, its tool schema and every
other transport stay typed (test_a_lenient_json_body_over_http). A
@service_action may declare its own, as a keyword of the decorator.
pool_seeds
class-attribute
instance-attribute
¶
The seeds the spec's callables are resolved with: the kernel's
DEFAULT_POOL_SEEDS unless the view registers others, as a class
attribute or as as_view(pool_seeds=...). One registry for the view's
life: as_view() checks the spec against it, refusing a callable that
requires a seed it does not register, and every call's checks, target
lookup and dispatch are handed that object (test_pool_seeds).
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 get_serializer_context, so overriding the
DRF-standard hook flows into input validation automatically; override
here for keys visible only during input validation.
get_output_serializer_context ¶
Hook for the context= dict passed to the output serializer.
Defaults to get_serializer_context, so overriding the
DRF-standard hook flows into response rendering automatically; override
here for keys visible only during response rendering.
Settings¶
lenient_json_body ¶
SPEC_REST_LENIENT_JSON_BODY: whether a view's serializer alone answers a JSON body.
False when the project sets nothing, so a JSON body is checked against the
spec's declared types first. A view's own lenient_json_body outranks it.
Read on every call, never at import, as every setting the kernel reads is: a
test's override_settings must reach a view built before it.
Raises:
| Type | Description |
|---|---|
ImproperlyConfigured
|
anything but a |
The write's target¶
resolve_mutation_instance ¶
Resolve the mutation target, or defer to the core.
A spec's target is its own target selector's, or none: the view's
get_object() is never asked for one, because the kernel takes a target
only where the spec declares the selector that would find it, and refuses
one handed to a spec declaring none. A standalone update or delete view
refuses such a spec at as_view(); a caller that check never reaches (a
viewset's action, @service_action) is handed no row
(test_service_update_view's test_a_kernel_spec_with_no_target_selector_is_handed_no_row).
``None`` for a **bulk** spec (``many=True`` or a
| Type | Description |
|---|---|
Any
|
|
Any
|
the spec carries an |
Any
|
with the right kwarg pool, error label and reserved-seed strip, and the |
Any
|
view's object permissions are asked of the row it finds. |
Any
|
spec with no target at all. |
filter_backends do not apply: DRF runs filter_queryset() inside
its own get_object(), which a spec's lookup bypasses, exactly as a
hand-written get_object() override does. A tenant-scoping backend in
DEFAULT_FILTER_BACKENDS therefore does not narrow the row a PATCH or
DELETE may reach here, while the sibling list action stays scoped. Put the
scoping in the selector's own queryset.