Skip to main content
Pre-release

This release is a pre-release and may not be stable for production use.

dj-hyperview

Reusable Django infrastructure for serving Hyperview UI supplied by the host project.

Plan de desarrollo

Consultá el plan de desarrollo para ver el roadmap, la arquitectura, el trabajo completado, el estado actual y los criterios de release.

Install for local development

uv sync --all-groups
uv run pytest

The package will resolve templates provided by a consumer from configured XML directories or, when enabled, from an optional Django database application.

Scope

dj-hyperview provides resolution, rendering, validation, caching, and Django integration. It does not ship application screens, runtime XML/HXML files, mobile components, Redis, or database/admin requirements. XML/HXML fixtures are kept under tests/ and excluded from the published package.

HTTP contract

Responses default to Hyperview's vendor media type, application/vnd.hyperview+xml, while accepting Django's explicit content_type, status, charset, and headers arguments. Consumer projects supply the markup directly or through their own templates:

from dj_hyperview import HyperviewResponse, HyperviewTemplateView


def screen(request):
    return HyperviewResponse("<view>Ready</view>", status=200)


class DetailScreen(HyperviewTemplateView):
    template_name = "mobile/detail.xml"  # Provided by the consumer project.

Template responses remain unrendered until Django renders them, preserving the standard TemplateResponse lifecycle.

Request integration

Add the middleware to attach typed metadata without changing the response:

MIDDLEWARE = [
    "dj_hyperview.middleware.HyperviewMiddleware",
    "django.middleware.csrf.CsrfViewMiddleware",
]

request.hyperview is truthy for the client's optional X-Hyperview-Version header or an explicit Hyperview vendor media type in Accept; its version is None when detection came only from negotiation. Generic application/xml and wildcard requests are not classified as Hyperview.

Mutable Hyperview forms can use Django's normal CSRF protection:

{% load dj_hyperview %}
<form>
  {% hv_csrf_token %}
</form>

The tag emits an XML-escaped hidden csrfmiddlewaretoken field. It calls Django's standard token API and does not bypass CsrfViewMiddleware.

Template sources

Sources are queried in declaration order; the first match wins. The built-in filesystem source reads UTF-8 templates from the consumer's directories:

HYPERVIEW = {
    "TEMPLATE_DIRS": [BASE_DIR / "mobile_screens"],
    "SOURCES": [
        {"BACKEND": "dj_hyperview.sources.FileSystemSource"},
        {
            "BACKEND": "my_project.hyperview.TenantSource",
            "OPTIONS": {"tenant_key": "slug"},
        },
    ],
}
from dj_hyperview import render_template, resolve_template

screen = resolve_template("account/profile.xml")
markup = render_template("account/profile.xml", {"username": "Ada"})

Template names are relative POSIX paths. Absolute paths, empty or dot segments, backslashes, NUL bytes, and filesystem symlink escapes are rejected. A source returns None only for a miss; when every source misses, resolution raises TemplateNotFound. Unicode remains case-preserving and unnormalized; combining marks and format characters are accepted, while control and surrogate code points are rejected.

Optional database app

Install "dj_hyperview.contrib.database" in INSTALLED_APPS and run Django migrations to make the HyperviewTemplate model available. Then enable the database source where its precedence belongs:

INSTALLED_APPS = [
    # ...
    "dj_hyperview.contrib.database",
]

HYPERVIEW = {
    "SOURCES": [
        {"BACKEND": "dj_hyperview.contrib.database.sources.DatabaseSource"},
    ],
}

The source uses Django's default manager and database router. Router-selected sources are intentionally uncached because routing may vary by request or tenant. Set "OPTIONS": {"using": "replica"} to pin a configured database alias and enable generic raw-cache acceleration with an alias-specific identity. Active exact-name rows are hits, including empty content; inactive or absent rows are misses and resolution continues to the next source. Without cache, a later lookup observes row content and revision changes. Explicit aliases retain normal cache TTL and manual invalidation semantics.

The base package does not import the model or require a database table. Model full_clean() checks canonical names and template safety; save() intentionally follows Django's standard behavior and does not call full_clean() automatically. Installed mutation signals still reject a noncanonical name before its SQL write. When django.contrib.admin is installed, its standard model admin routes create, edit, rename, and delete through the validated publication services. Persisted-row edit and individual delete require exactly one hidden revision token; missing, duplicate, or stale tokens fail safely. Add forms expose no token, revision and timestamps stay read-only, and bulk delete remains signal-backed. Omitting admin imports none of it.

Model save/delete plus controlled QuerySet.delete() and QuerySet.update() schedule invalidation with Django's transaction.on_commit() on the mutation database alias. Updates snapshot locked rows and validate their actual stored names after SQL, so literal and expression renames invalidate both old and new names. Rollbacks and rolled-back savepoints discard callbacks. A cache failure remains observable after commit and therefore does not mean the database write rolled back. Raw fixture saves and bulk APIs still require explicit invalidation through invalidate_templates; automatic bulk hooks remain post-MVP. Use the public database services to publish, rename, or delete validated templates. Publication creates revision 1, increments updates once, and accepts expected_revision for optimistic conflict detection; omitting it uses last-write-wins under the selected database row lock. Its immutable result hides the model and confirms persistence in the current transaction; an enclosing transaction may still roll it back. SQLite cannot prove row-lock serialization, so tests inject races. Name uniqueness follows the database backend's collation: the package does not case-fold names, and SQLite's default treats screen.xml and Screen.xml as distinct.

Cache contract

TemplateCache stores only serialized raw ResolvedTemplate data through a configured Django cache alias; it never caches compiled templates. A lookup returns None when no entry exists, CACHE_MISS for a cached source miss, and returns a CacheEntry for content—even when that content is empty. Every hit or miss is bound to its source, name, and revision. Backend failures use a stable public error without exposing cached template data. Fixed-length SHA-256 keys isolate each namespace. A non-empty HYPERVIEW.CACHE is opt-in; omitting it or using {} never initializes a cache backend. Source identities hash position plus effective backend options (including filesystem roots) without exposing secrets; an unrepresentable custom configuration stays uncached. Fingerprinting accepts a closed domain: exact built-in scalars, dict, list, tuple, standard pathlib paths, and verified importable callables. Subclasses, generic container implementations, ranges, mutable/buffer containers such as bytearray, memoryview, and array.array stay uncached instead of risking an identity that omits observable semantics. Cached JSON is treated as untrusted: exact version/shape/type and lookup identity must match, while duplicate keys or invalid aliases raise SourceUnavailable. Checks and runtime accept an alias only when Django resolves its configured name to a cache backend; leading underscores alone do not make an alias invalid.

Call invalidate_templates("screens/home.xml") after publishing raw content. Invalidation rotates a shared, namespaced token for each canonical name, so all sources' older hits and misses become invisible and a read that started earlier cannot repopulate the new generation. Already-running renders may still finish with their pinned snapshot. The call returns only after every requested token rotation succeeds; any cache failure raises SourceUnavailable, independently of the resolver's ordinary bypass policy. Multi-name rotation is not atomic, so a partial failure is observable and callers may safely retry every name. Every successfully claimed root or successor token leaves a shared, non-expiring tombstone. Claim metadata binds each token to its root/successor lifecycle, so repeated entropy cannot reuse an older generation even after raw TTLs pass. Failed and concurrent candidates also remain claimed: correctness costs roughly one small marker per generated candidate, reclaimed only with the cache namespace/backend lifecycle. Successors are domain-separated digests of the previous token plus fresh entropy, not the entropy itself. Every raw content or miss write rechecks the shared generation and removes a superseded exact key only when the backend returns exactly True; None, False, and exceptions are ambiguous failures. bypass may still return authoritative source data, but never reports stale cache publication as successful or weakens its tombstone. If an operator evicts a tombstone while retaining raw entries, generic caches cannot prove uniqueness; cryptographic uniqueness is the fallback, not a durable transaction.

Template engine

render_template() uses a dedicated Django template engine backed only by the configured Hyperview sources. Root templates, {% include %}, and {% extends %} therefore use the same canonical names and source precedence; the host project's HTML template loaders are not modified. No compiled-template cache is installed, so a new render sees newly published source content. Templates returned by get_template() or select_template() preserve Django's render signature and metadata while enforcing the same validation.

During one render, the first result—or miss—for each template name is pinned per resolver identity. Nested engines sharing a resolver reuse its snapshot, while different resolvers remain isolated even when they render the same name. Repeated includes cannot mix revisions if a source changes concurrently, while separate sync or async request contexts remain isolated. Dynamic names that have not yet been resolved still observe source state at their first lookup because the source protocol intentionally provides point lookups rather than a global transaction.

When HYPERVIEW["SOURCES"] is configured, HyperviewTemplateResponse and HyperviewTemplateView use this engine while preserving Django's lazy response, status, header, context, and escaping behavior. An explicit using= continues to select the consumer's standard Django template engine.

HXML validation

Every public engine/response render path escapes context, rejects active DTD/entities before compile without misclassifying comments or CDATA, and enforces rendered XML schema, byte, depth, and node limits. Consumer XSD includes/imports are denied to prevent network or traversal access. Failures raise TemplateValidationError; no application schema or screen is included.

Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

dj_hyperview-0.1.0a3.tar.gz (31.9 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

dj_hyperview-0.1.0a3-py3-none-any.whl (44.9 kB view details)

Uploaded Python 3

File details

Details for the file dj_hyperview-0.1.0a3.tar.gz.

File metadata

  • Download URL: dj_hyperview-0.1.0a3.tar.gz
  • Upload date:
  • Size: 31.9 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/7.0.0 CPython/3.13.14

File hashes

Hashes for dj_hyperview-0.1.0a3.tar.gz
Algorithm Hash digest
SHA256 7c4303645757a459effbc994c14f2dad9ee4f3045d4078ce4db76ffb4a37fad9
MD5 fa1705605531c86f8bd7677b9b1f93d1
BLAKE2b-256 e6952ecf2a041270eceef2cd31010db12b3f644817f45db788bf6547abfc33e3

See more details on using hashes here.

Provenance

The following attestation bundles were made for dj_hyperview-0.1.0a3.tar.gz:

Publisher: release.yml on eamigo86/dj-hyperview

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file dj_hyperview-0.1.0a3-py3-none-any.whl.

File metadata

  • Download URL: dj_hyperview-0.1.0a3-py3-none-any.whl
  • Upload date:
  • Size: 44.9 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/7.0.0 CPython/3.13.14

File hashes

Hashes for dj_hyperview-0.1.0a3-py3-none-any.whl
Algorithm Hash digest
SHA256 74f80a257cb1ab8b80c077ad9e703bf78ab12f2825652c4ed9ea303f84058903
MD5 8b7ec597dc2a34ea8c52187e78521362
BLAKE2b-256 62de715b2552075ff3d990a540cf1a8ee6e29a48167be458232d2d78b6737a3a

See more details on using hashes here.

Provenance

The following attestation bundles were made for dj_hyperview-0.1.0a3-py3-none-any.whl:

Publisher: release.yml on eamigo86/dj-hyperview

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.
Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page