next.partial API reference#

Module summary#

next.partial exposes the server side of partial rendering. The surface covers the Patches builder that authors a patch envelope, the response and stream classes that carry it, and the zone-render and origin helpers. It also covers the custom-verb registration hook and the protocol backend that serialises the wire format. The wire protocol, the data-next-* attributes, and the client runtime live in the topic section, see Partial rendering reference.

API tiers#

The surface splits into tiers that describe the intended audience for each name. The lists below are representative. The autodoc blocks under Public API are the exhaustive surface.

Stable.

Patches, PatchResponse, PatchEventStream, render_zone, ZoneRenderResult, zone_requested, is_partial_request, partial_intent, register_patch_op, UnknownZoneError, ForeignPageNotAuthorizedError, and LayerHrefWithoutZoneError. Envelope, Patch, Asset, and FormMeta are the frozen value objects of the wire contract. Import all of these from next.partial. Use them in page modules, action handlers, and stream sources.

Advanced.

shape_partial and PartialProtocolBackend are imported from next.partial. resolve_partial_origin stays in next.partial as a thin helper that reads the host page out of the X-Next-Origin header so a done step can pass it to morph(page=). OriginSource lives in next.partial.origin. ZoneInfo and zones_of live in next.partial.registry. The custom-verb exceptions live in next.partial.patches. The signals and checks submodules carry the partial telemetry. Use these when writing a custom protocol backend, a wire-format plugin, or telemetry.

Framework machinery.

REQUEST_ID is the X-Next-Request-Id header name and lives in next.partial.headers. PartialIntent and MergeMode live in next.partial.headers. PartialOrigin lives in next.partial.origin. ActionRef, shape_validate, and drain_messages live in next.partial.shaping. PatchOpRegistry, the patch_op_registry instance, and BUILTIN_OPS live in next.partial.registry.

Internal hooks.

Underscore-prefixed helpers inside the submodules are implementation details. next.partial.__all__ is the source of truth for the curated package surface and exports no underscore names.

Public API#

Detecting a partial request#

is_partial_request returns True when the request carries the X-Next-Request switch, the test a render escape hatch reads before shaping a patch response. partial_intent parses and memoises the X-Next-* headers into a PartialIntent. zone_requested answers whether the intent names a given zone, the guard a lazy zone’s context provider reads to skip an expensive query on a full render.

next.partial.is_partial_request(request: HttpRequest) bool[source]#

Return True when the request asks for a partial response.

next.partial.partial_intent(request: HttpRequest) PartialIntent[source]#

Return the partial intent of the request, memoised on the request.

next.partial.zone_requested(request: HttpRequest, name: str) bool[source]#

Return True when the partial intent of the request names the zone.

class next.partial.headers.PartialIntent(partial: bool = False, zones: tuple[str, ...] = (), validate_fields: tuple[str, ...] = (), merge: MergeMode | None = None, version: str | None = None, request_id: str | None = None, origin: str | None = None)[source]#

Parsed partial-request headers naming what the client asks for.

The fields mirror the request-header table of the wire protocol. A request without the X-Next-Request switch is not partial and every derived field stays empty. Names are server-registry indices, never selectors or swap strategies.

partial: bool#
zones: tuple[str, ...]#
validate_fields: tuple[str, ...]#
merge: MergeMode | None#
version: str | None#
request_id: str | None#
origin: str | None#
__init__(partial: bool = False, zones: tuple[str, ...] = (), validate_fields: tuple[str, ...] = (), merge: MergeMode | None = None, version: str | None = None, request_id: str | None = None, origin: str | None = None) None#
class next.partial.headers.MergeMode(*values)[source]#

Merge intent of a paginating partial request.

APPEND = 'append'#
PREPEND = 'prepend'#

Building patches#

Patches is the request-bound builder. Each method records one operation and returns self for chaining, and response finalises a PatchResponse or falls back to a redirect when the runtime is absent. Envelope, Patch, Asset, and FormMeta are the frozen value objects the builder assembles, surfaced for a custom backend that serialises the wire format itself.

class next.partial.Patches(request: HttpRequest, *, echo_of: str | None = None)[source]#

Request-bound builder of a patch envelope.

Built from a request, the builder takes its asset version from the active protocol backend and resolves the origin page lazily, so a morph(zone=…) renders against the page that owns the request. The versioned classmethod builds a request-free assembler for paths that already hold the version and render their own HTML.

__init__(request: HttpRequest, *, echo_of: str | None = None) None[source]#

Start an empty builder bound to the request.

Pass echo_of with the originating mutation’s request id so the envelope carries it as request_id, letting an SSE subscriber suppress its own echo. Only the stream path passes it, the HTTP response path leaves it unset since the answer already reaches the initiator.

classmethod versioned(version: str, *, echo_of: str | None = None) Patches[source]#

Start an empty request-free builder stamped with a literal version.

The builder stays a low-level envelope assembler with no request, used by paths that already hold the version and render their own HTML.

property version: str#

Return the asset version stamped on the envelope.

morph(target: Mapping[str, Any] | None = None, html: str | None = None, *, extract: bool = False, **select) Patches[source]#

Morph a target into HTML, the default verb.

A thin facade over the typed per-verb morph methods that keeps the single-verb mental model. The facade routes the selector keyword to the method that owns its contract, so two selectors in one call or an unknown selector raises rather than being silently dropped.

morph_zone(zone: str, *, overrides: Mapping[str, Any] | None = None) Patches[source]#

Render the named zone of the origin page and morph it in place.

morph_foreign_zone(zone: str, page: Path | str, *, url_kwargs: Mapping[str, Any] | None = None) Patches[source]#

Render a zone of a foreign page out of band, re-running its guards.

The page is named by its page path or by a URL of it, which is resolved through the URLconf to the page that serves it. The foreign page’s body resolution runs first, so a redirect or a denial short-circuits before any zone renders and raises instead of morphing an empty body. A render() string body has no zone to render standalone, so it is refused the same way the OOB view branch refuses it. With the page authorized, the named zone renders standalone with the foreign page’s URL kwargs and morphs in place addressed by zone name.

morph_form(uid: str, html: str) Patches[source]#

Extract-morph the form addressed by its uid into the given HTML.

replace(target: Mapping[str, Any], html: str) Patches[source]#

Replace the target node wholesale with the given HTML.

inner(target: Mapping[str, Any], html: str) Patches[source]#

Replace only the contents of the target with the given HTML.

append(target: Mapping[str, Any], html: str, *, dedupe: Literal['key', 'id'] = 'key') Patches[source]#

Append children to the target, deduplicating by key or id.

prepend(target: Mapping[str, Any], html: str, *, dedupe: Literal['key', 'id'] = 'key') Patches[source]#

Prepend children to the target, deduplicating by key or id.

remove(target: Mapping[str, Any]) Patches[source]#

Remove the target node.

refresh(*, zone: str) Patches[source]#

Ask the client to refetch the named zone with its own cookies.

context(**names) Patches[source]#

Merge named serialize provider values into the client context.

Only the names of registered serialize=True providers on the origin page are accepted. A framework-owned init-payload key raises ReservedContextKeyError whether or not the origin page registered it, so the refusal does not depend on the collision the check warns about. The values are serialized through resolve_serializer() so the wire carries plain data.

layer_open(*, zone: str | None = None, href: str | None = None) Patches[source]#

Open a server-initiated layer, optionally seeding a zone or href.

A seeded href needs a zone to load into and must be same-site, so a href without a zone raises LayerHrefWithoutZoneError and a cross-site value raises CrossSiteHrefError.

layer_close(*, result: object = None, dismiss: str | None = None) Patches[source]#

Close the top layer with an accept result or a dismissal.

A dismissal sets the boolean dismiss flag the client reads and carries the reason string under reason, matching the wire shape the runtime expects rather than overloading dismiss with the text.

toast(text: str, variant: str = 'info') Patches[source]#

Show a toast, sugar over an event with a built-in container.

event(name: str, detail: Mapping[str, Any] | None = None) Patches[source]#

Dispatch a CustomEvent on document and the Next.on bus.

A framework-owned event name raises ReservedEventNameError so an app cannot forge a runtime lifecycle event.

push_url(href: str) Patches[source]#

Push the validated href onto the browser history.

The href must be same-site, a cross-site value raises CrossSiteHrefError rather than being masked as the origin path.

redirect(href: str, *, external: bool = False) Patches[source]#

Drive a full client navigation to a server-authored href.

An internal href must be same-site, a cross-site value raises CrossSiteHrefError. An external href is sent with a full-navigation marker so a server-authored redirect like OAuth or a payment gateway is not rejected by the same-host validator.

The external=True escape hatch bypasses same-host validation, so the href must be server authored. Never pass user-supplied input through it, or the page becomes an open redirect.

op(name: str, **payload) Patches[source]#

Emit a custom verb registered through register_patch_op.

A built-in verb is refused so it travels only through its typed method, which owns the verb’s wire keys, never as a raw payload.

add_asset(kind: str, url: str, *, inline: str | None = None) Patches[source]#

Record a co-located asset in the envelope manifest.

The insertion verb comes from the kind registry, so an unregistered kind still travels and only loses the field the runtime would use. An inline body keeps the verb only when the runtime builds the same element the full page render wraps it in.

set_form(form: FormMeta) Patches[source]#

Attach the machine-readable form meta to the envelope.

set_csrf(csrf: Mapping[str, Any]) Patches[source]#

Attach the rotated CSRF payload so the runtime refreshes tokens.

envelope() Envelope[source]#

Return the assembled envelope value object.

response(fallback: str | None = None) PatchResponse | HttpResponse[source]#

Assemble the response for the current request.

With the partial switch on the request the envelope travels as a PatchResponse. Without the switch, mutation falls back to the full cycle: a 303 to the request origin when no fallback is given, or a redirect to fallback when it is.

class next.partial.PatchResponse(body: bytes, *, content_type: str = 'application/vnd.next.patches+json', version: str | None = None, status: int = 200)[source]#

HTTP response that carries a serialized patch envelope.

The response is an HttpResponse subclass so it passes the handler normalisation contract that requires rich return types to subclass HttpResponse. The body bytes and content type come from the active protocol backend, the partial Vary headers are set on construction.

__init__(body: bytes, *, content_type: str = 'application/vnd.next.patches+json', version: str | None = None, status: int = 200) None[source]#

Build the response from serialized envelope bytes.

class next.partial.Envelope(version: str, ops: Sequence[Patch] = (), assets: Sequence[Asset] = (), form: FormMeta | None = None, csrf: Mapping[str, Any] | None = None, request_id: str | None = None)[source]#

A patch envelope carrying ordered ops and protocol meta.

Every field but version is optional, an absent value is empty on the wire. The csrf and request_id meta are stamped only when set so the wire shape stays stable whether or not they travel.

version: str#
ops: Sequence[Patch]#
assets: Sequence[Asset]#
form: FormMeta | None#
csrf: Mapping[str, Any] | None#
request_id: str | None#
as_dict() dict[str, Any][source]#

Return the wire form of the envelope as an ordered mapping.

__init__(version: str, ops: Sequence[Patch] = (), assets: Sequence[Asset] = (), form: FormMeta | None = None, csrf: Mapping[str, Any] | None = None, request_id: str | None = None) None#
class next.partial.Patch(op: str, target: Mapping[str, Any] | None=None, html: str | None = None, extras: Mapping[str, Any]=<factory>)[source]#

One addressed DOM operation of a patch envelope.

A patch carries a verb, an optional target object with a single key, optional HTML payload, and any verb-specific extras.

op: str#
target: Mapping[str, Any] | None#
html: str | None#
extras: Mapping[str, Any]#
__post_init__() None[source]#

Refuse an extras payload that names a structural wire key.

as_dict() dict[str, Any][source]#

Return the wire form of the patch as an ordered mapping.

__init__(op: str, target: Mapping[str, Any] | None=None, html: str | None = None, extras: Mapping[str, Any]=<factory>) None#
class next.partial.Asset(kind: str, url: str, inline: str | None = None, load: str | None = None)[source]#

One co-located asset of a rendered target by kind, URL, and inline body.

The load field is the client insertion verb resolved from the kind registry. It stays None for a kind the runtime cannot insert, and the wire then omits the field entirely.

kind: str#
url: str#
inline: str | None#
load: str | None#
as_dict() dict[str, str][source]#

Return the wire form of the asset, carrying its inline body when set.

__init__(kind: str, url: str, inline: str | None = None, load: str | None = None) None#
class next.partial.FormMeta(uid: str, valid: bool, errors: Mapping[str, Sequence[str]]=<factory>)[source]#

Machine-readable state of a form built from its field specs.

uid: str#
valid: bool#
errors: Mapping[str, Sequence[str]]#
as_dict() dict[str, Any][source]#

Return the wire form of the form meta object.

__init__(uid: str, valid: bool, errors: Mapping[str, Sequence[str]]=<factory>) None#

Custom verbs#

register_patch_op registers a custom verb name on the server, validated by the next.E066 check, and earns the generic Patches.op channel. An unregistered name fails at runtime with UnknownPatchOpError. The client supplies the handler through Next.partial.defineOp. See Extending the protocol for the end-to-end recipe.

next.partial.register_patch_op(name: str) None[source]#

Register a custom patch verb with the builder side of the protocol.

Zones#

render_zone renders one or more zones of a page standalone with the full page context, returning a ZoneRenderResult that carries the wrapped HTML and the collected assets. zones_of returns the compiled zones of a template as a mapping of ZoneInfo, both reached through next.partial.registry.

next.partial.render_zone(page_path: Path, zone_names: tuple[str, ...], request: HttpRequest, url_kwargs: dict[str, object] | None = None, overrides: dict[str, object] | None = None, *, context_data: dict[str, object] | None = None) ZoneRenderResult[source]#

Render the named zones of a page with the full page context.

The context is collected once for the whole batch of names through build_render_context and a fresh collector is seeded by the same convention as the canonical render path, so co-located assets of the zone bodies are gathered. A caller that already built the origin context passes it as context_data so it is reused rather than rebuilt. The manifest travels outward in the result rather than through inject, which is a no-op for fragments. Unknown zone names are skipped so one stale name never poisons a batch, but a batch of only unknown names raises so a single-zone request keeps its 400.

class next.partial.ZoneRenderResult(html: dict[str, str], bodies: dict[str, str], collector: StaticCollector)[source]#

Rendered zones plus the assets their bodies collected.

html maps each rendered zone name to its wrapped marker element and bodies maps it to the bare inner body. A morph or replace addresses the wrapped element, an append or prepend grafts the bare body into the live zone. collector carries the co-located assets the bodies registered so the caller can ship a manifest outward, past the no-op inject.

html: dict[str, str]#
bodies: dict[str, str]#
collector: StaticCollector#
url_assets() Iterator[tuple[str, str]][source]#

Yield the (kind, url) pair of each collected asset that has a URL.

inline_assets() Iterator[tuple[str, str]][source]#

Yield the (kind, inline body) of each collected inline asset.

js_context_delta() dict[str, object][source]#

Return the zone js-context as wire-ready values for a context patch.

__init__(html: dict[str, str], bodies: dict[str, str], collector: StaticCollector) None#
class next.partial.registry.ZoneInfo(name: str, partial: ZonePartial, options: ZoneOptions)[source]#

One compiled zone of a composed page template.

The render paths consume options whole, and the scalar read surface delegates to it so no mode can drift between the two.

name: str#
partial: ZonePartial#
options: ZoneOptions#
property lazy: str | None#

Lazy trigger of the zone, read from its options.

property poll: int | None#

Poll interval of the zone in milliseconds, read from its options.

property tag: str#

Wrapper tag name of the zone, read from its options.

__init__(name: str, partial: ZonePartial, options: ZoneOptions) None#
next.partial.registry.zones_of(template: Template) Mapping[str, ZoneInfo][source]#

Return the named zones of a compiled template, memoised per object.

The cache keys on the compiled template object, so a recompiled page gets a fresh entry while the stale object is collected. The first read of a template announces its zones through zone_registered.

Origin and authorisation#

resolve_partial_origin is a thin helper that reads the host page that owns a zone out of the same-site X-Next-Origin header, falling back to the posted form origin, so a done step can hand the path to morph(page=) for a server out-of-band swap. It stays importable from next.partial but sits in the Advanced tier, the canonical done choreography addresses the foreign zone through morph(page=, url_kwargs=). OriginSource, which discriminates the two sources, lives in next.partial.origin.

next.partial.resolve_partial_origin(request: HttpRequest) PartialOrigin | None[source]#

Resolve the host page of a partial request for an out-of-band morph.

The X-Next-Origin header the runtime stamps with the host page URL wins, so a master rendered inside a layer morphs the zone of the page that owns the layer rather than the master’s own step page. When the header is absent or does not resolve to a page the posted form origin is the fallback, which keeps the resolver usable from a master that posts straight from its host page. The header is validated same-site before it is trusted, so an off-site origin cannot redirect the morph.

class next.partial.origin.OriginSource(*values)[source]#

Where the resolved host-page origin was taken from.

HEADER = 'header'#
FORM = 'form'#
class next.partial.origin.PartialOrigin(page_path: Path | None, url_kwargs: dict[str, object], origin: str, source: OriginSource)[source]#

The host page an out-of-band morph addresses, with its URL kwargs.

page_path names the page source whose zone a done handler morphs out of band, url_kwargs are that page’s captured URL parameters, and source records whether the host page came from the X-Next-Origin header or fell back to the posted form origin.

page_path: Path | None#
url_kwargs: dict[str, object]#
origin: str#
source: OriginSource#
__init__(page_path: Path | None, url_kwargs: dict[str, object], origin: str, source: OriginSource) None#

Shaping#

shape_partial turns a form action outcome into a patch envelope, the body of the bundled form backend’s partial-aware shape_response. A custom backend that overrides shape_response routes partial requests through it, or the next.W068 check warns that the runtime receives a full page instead.

next.partial.shape_partial(backend: FormActionBackend, request: HttpRequest, outcome: ActionOutcome) HttpResponse[source]#

Shape one action outcome as a patch envelope for a partial request.

The CSRF rotation marker is read here, before any form or zone re-render mints a token and sets the marker as a side effect, so a login on the submit path stamps the fresh token onto whichever shape the outcome takes. Reading it after a re-render would flag every response as rotated.

SSE stream#

PatchEventStream is a StreamingHttpResponse that serialises each Patches from a sync or async source as one next-patches event. An async source requires ASGI and a sync source requires WSGI, and a mismatch raises ImproperlyConfigured when the response is built. See SSE under WSGI and ASGI for the WSGI and ASGI contract.

class next.partial.PatchEventStream(request: HttpRequest, source: Iterable[Patches] | AsyncIterable[Patches], *, heartbeat_seconds: float | None = None, clock: Callable[[], float] | None = None)[source]#

SSE response that emits patch envelopes as next-patches events.

Each envelope yielded by the source travels as one next-patches event serialized by the active protocol backend, the same shape an HTTP partial response carries. A sync source streams envelopes as they arrive with no heartbeat, a blocked next() having nothing to interrupt it without a thread. An async source under ASGI interleaves heartbeat comments during quiet periods through asyncio.wait. The politeness headers and the leading retry hint are set on construction so a buffering proxy or GZipMiddleware does not eat the flush. The sse_stream_opened signal fires on construction and sse_stream_closed fires when the stream ends.

The source kind must match the server kind. Django buffers an async iterator fully under WSGI and a sync iterator fully under ASGI before the first byte, which hangs an infinite stream, so the constructor raises ImproperlyConfigured when an async source meets a WSGI request or a sync source meets an ASGI request.

__init__(request: HttpRequest, source: Iterable[Patches] | AsyncIterable[Patches], *, heartbeat_seconds: float | None = None, clock: Callable[[], float] | None = None) None[source]#

Build the stream over a sync or async source of patch builders.

The heartbeat interval falls back to the active backend’s HEARTBEAT_SECONDS option when no explicit argument is passed.

Protocol backend#

PartialProtocolBackend owns the patch wire format and is the first entry of PARTIAL_BACKENDS. Subclass it and serialise a different envelope shape to support another wire format.

class next.partial.PartialProtocolBackend(config: Mapping[str, Any] | None = None)[source]#

Owner of the wire format for patch envelopes.

The default backend serialises envelopes as a compact JSON envelope under application/vnd.next.patches+json. A third party may swap the wire format, for example to emulate Turbo Streams, by registering a different backend through PARTIAL_BACKENDS without touching shaping or the registries. Both serialize_envelope and sse_event operate over the same JSON envelope.

content_type: str = 'application/vnd.next.patches+json'#
__init__(config: Mapping[str, Any] | None = None) None[source]#

Store the merged backend config and its options.

property options: Mapping[str, Any]#

Return the backend OPTIONS mapping from settings.

serialize_envelope(envelope: Envelope) bytes[source]#

Serialize one envelope for an HTTP response body.

sse_event(envelope: Envelope) str[source]#

Serialize one envelope as an SSE event frame.

Exceptions#

UnknownZoneError, ForeignPageNotAuthorizedError, and LayerHrefWithoutZoneError are curated next.partial exceptions. UnknownZoneError is raised when a partial request names a zone the template does not declare, surfacing as a 400 before any render. ForeignPageNotAuthorizedError is raised when an out-of-band morph of a foreign page fails that page’s own authorisation, so a zone never travels in a response the page would have denied. LayerHrefWithoutZoneError is raised when a layer seeds an href but names no zone= to load it into, so the builder refuses the layer instead of opening an empty one on the client. The remaining nine are rarely caught and stay out of the curated surface. They guard the custom-verb contract, the event-name, context-key, and dedupe vocabularies, and the foreign-page and href rules, and live in next.partial.patches.

exception next.partial.UnknownZoneError(zone_name: str, declared: tuple[str, ...] = ())[source]#

Raised when a partial request names a zone the page does not declare.

The unified view turns this into a 400 before any zone renders, so a typo or a stale client never trips a partial render. The message names the declared zones so a builder-path typo points at what is available.

__init__(zone_name: str, declared: tuple[str, ...] = ()) None[source]#

Store the unknown zone name and the declared zone names available.

exception next.partial.ForeignPageNotAuthorizedError(page_path: Path, status_code: int)[source]#

Raised when an OOB morph names a foreign page that denies the request.

A morph(page=…) re-runs the foreign page’s body resolution before rendering its zone, so the zone never travels in the master’s response when the page would have redirected or denied the caller on its own request. The denial is surfaced rather than swallowed into an empty morph so the master path can answer with a clear shape.

__init__(page_path: Path, status_code: int) None[source]#

Store the page path and the short-circuit status code.

exception next.partial.LayerHrefWithoutZoneError(href: str)[source]#

Raised when a layer seeds an href but names no zone to load it into.

The client fetch path needs a zone to know which fragment of the href to pull, so an href without one would silently open an empty modal, which is a caller bug refused at the builder.

__init__(href: str) None[source]#

Store the rejected href and build a readable message.

exception next.partial.patches.UnknownPatchOpError(name: str)[source]#

Raised when the builder is asked to emit an unregistered verb.

The runtime guard pairs with the next.E066 check, so an unknown verb fails fast in op() rather than reaching the client.

__init__(name: str) None[source]#

Store the unknown verb name and build a readable message.

exception next.partial.patches.BuiltinPatchOpError(name: str)[source]#

Raised when the generic op() channel names a built-in verb.

A built-in verb owns typed wire keys, so it must travel through its typed builder method rather than the raw op() payload channel.

__init__(name: str) None[source]#

Store the built-in verb name and build a readable message.

exception next.partial.patches.ReservedPatchKeyError(op: str, reserved: frozenset[str])[source]#

Raised when a custom op payload names a structural wire key.

The op, target, and html keys carry the patch structure, so a payload that names one of them is refused rather than overwriting it.

__init__(op: str, reserved: frozenset[str]) None[source]#

Store the offending verb and the reserved keys it collided with.

exception next.partial.patches.UnknownContextNameError(name: str, available: tuple[str, ...] = ())[source]#

Raised when context() names a value that is not a serialize provider.

Only the names of registered serialize=True context providers may travel in a context patch, so an arbitrary mapping is rejected at the builder rather than serialized blind. The message names the available providers so a typo points at what is registered.

__init__(name: str, available: tuple[str, ...] = ()) None[source]#

Store the rejected name and the available serialize provider names.

exception next.partial.patches.ReservedContextKeyError(reserved: frozenset[str])[source]#

Raised when context() names a key the init payload owns.

A full render keeps a reserved key for the framework, so a context patch that names one would leave the client store disagreeing with the page it patches. The explicit naming is a caller bug refused at the builder rather than merged on the client.

__init__(reserved: frozenset[str]) None[source]#

Store the reserved keys the call collided with.

exception next.partial.patches.ReservedEventNameError(name: str)[source]#

Raised when event() names a framework-owned client-bus event.

The ready and context-updated events and the partial: and next: prefixes belong to the runtime lifecycle, so an app event under one of those names is refused rather than forging a framework signal.

__init__(name: str) None[source]#

Store the reserved name and build a readable message.

exception next.partial.patches.DynamicForeignPageError(page_path: Path)[source]#

Raised when an OOB morph names a foreign page with a render() body.

A render() string body never reaches the composed-template cache, so it has no compiled source to render a standalone zone against. The OOB view branch refuses the same shape with a 400, so the builder refuses it here rather than morphing the page’s stale static template.

__init__(page_path: Path) None[source]#

Store the page path and build a readable message.

exception next.partial.patches.UnknownDedupeError(dedupe: str)[source]#

Raised when a merge op names a dedupe strategy the client cannot apply.

The client keys a merge row by data-next-key then id, so only key and id mean anything on the wire, an unknown value is refused at the builder rather than dropped to a silent no-dedup downstream.

__init__(dedupe: str) None[source]#

Store the rejected dedupe value and build a readable message.

exception next.partial.patches.CrossSiteHrefError(href: str)[source]#

Raised when a builder href sink names a cross-site URL.

The push_url, layer_open(href=), and internal redirect sinks author an in-app navigation, so a cross-site href is a caller bug refused at the builder rather than masked as a fallback to the origin path. A server-authored external destination travels through redirect(external=True) instead.

__init__(href: str) None[source]#

Store the rejected href and build a readable message.

Signals#

See Signals reference and Signals for the partial signals (zone_registered, zone_rendered, patch_op_registered, field_validated, sse_stream_opened, sse_stream_closed).

System checks#

See System checks for the zone-placement, template-compile, custom-verb, and backend-configuration checks (next.E060 through next.E067, next.E072, next.E073, next.W067 through next.W071).

See also#

See also

Partial rendering for the topic subtree. Partial rendering reference for the wire protocol and client runtime. Extending the protocol for custom verbs and server-pushed context. Settings for PARTIAL_BACKENDS.