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
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
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
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
7c4303645757a459effbc994c14f2dad9ee4f3045d4078ce4db76ffb4a37fad9
|
|
| MD5 |
fa1705605531c86f8bd7677b9b1f93d1
|
|
| BLAKE2b-256 |
e6952ecf2a041270eceef2cd31010db12b3f644817f45db788bf6547abfc33e3
|
Provenance
The following attestation bundles were made for dj_hyperview-0.1.0a3.tar.gz:
Publisher:
release.yml on eamigo86/dj-hyperview
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
dj_hyperview-0.1.0a3.tar.gz -
Subject digest:
7c4303645757a459effbc994c14f2dad9ee4f3045d4078ce4db76ffb4a37fad9 - Sigstore transparency entry: 2720145915
- Sigstore integration time:
-
Permalink:
eamigo86/dj-hyperview@75ab869daf7863574c8a97e35f08dded3f2c8096 -
Branch / Tag:
refs/tags/v0.1.0a3 - Owner: https://github.com/eamigo86
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@75ab869daf7863574c8a97e35f08dded3f2c8096 -
Trigger Event:
push
-
Statement type:
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
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
74f80a257cb1ab8b80c077ad9e703bf78ab12f2825652c4ed9ea303f84058903
|
|
| MD5 |
8b7ec597dc2a34ea8c52187e78521362
|
|
| BLAKE2b-256 |
62de715b2552075ff3d990a540cf1a8ee6e29a48167be458232d2d78b6737a3a
|
Provenance
The following attestation bundles were made for dj_hyperview-0.1.0a3-py3-none-any.whl:
Publisher:
release.yml on eamigo86/dj-hyperview
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
dj_hyperview-0.1.0a3-py3-none-any.whl -
Subject digest:
74f80a257cb1ab8b80c077ad9e703bf78ab12f2825652c4ed9ea303f84058903 - Sigstore transparency entry: 2720146561
- Sigstore integration time:
-
Permalink:
eamigo86/dj-hyperview@75ab869daf7863574c8a97e35f08dded3f2c8096 -
Branch / Tag:
refs/tags/v0.1.0a3 - Owner: https://github.com/eamigo86
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@75ab869daf7863574c8a97e35f08dded3f2c8096 -
Trigger Event:
push
-
Statement type: