Add custom HXML elements¶
Extend validation and editor autocomplete with project-owned XML Schema 1.1 files. A custom schema describes the elements understood by your mobile client; it does not implement their React Native behavior.
Quick path¶
- Choose a permanent namespace URI for the project's custom elements.
- Define those elements in a local XSD 1.1 file.
- Register the file through
EXTRA_SCHEMAS. - Declare a prefix for the same namespace in each HXML document.
- Run Django's system checks before opening the editor or serving HXML.
The base installation includes automatic XSD 1.1 validation:
For optional Admin autocomplete and formatting, install dj-hyperview[editor]
and add django_ace to INSTALLED_APPS. Neither installation selects a schema.
1. Choose a namespace¶
Use a URI controlled by the project and keep it stable. It identifies the vocabulary; it does not need to return a web page.
This guide uses:
The prefix app is only a local abbreviation. The namespace URI, rather than
the prefix, is the identity that must match between the XSD and HXML.
2. Define the custom elements¶
Create schema/hypertodo.xsd in the Django project:
<?xml version="1.0" encoding="UTF-8"?>
<xs:schema
xmlns:xs="http://www.w3.org/2001/XMLSchema"
xmlns:app="https://example.com/hypertodo"
targetNamespace="https://example.com/hypertodo"
elementFormDefault="qualified"
version="1.1"
>
<xs:element name="swipe-row">
<xs:complexType>
<xs:sequence>
<xs:element
ref="app:swipe-action"
minOccurs="1"
maxOccurs="unbounded"
/>
<xs:any
namespace="https://hyperview.org/hyperview"
processContents="lax"
/>
</xs:sequence>
<xs:attribute name="id" type="xs:ID" />
<xs:attribute name="direction" use="required">
<xs:simpleType>
<xs:restriction base="xs:string">
<xs:enumeration value="left" />
<xs:enumeration value="right" />
</xs:restriction>
</xs:simpleType>
</xs:attribute>
</xs:complexType>
</xs:element>
<xs:element name="swipe-action">
<xs:complexType>
<xs:attribute name="label" type="xs:string" use="required" />
<xs:attribute name="href" type="xs:anyURI" use="required" />
<xs:attribute name="verb" default="post">
<xs:simpleType>
<xs:restriction base="xs:string">
<xs:enumeration value="delete" />
<xs:enumeration value="patch" />
<xs:enumeration value="post" />
</xs:restriction>
</xs:simpleType>
</xs:attribute>
</xs:complexType>
</xs:element>
</xs:schema>
This definition gives the editor enough information to suggest:
app:swipe-actioninsideapp:swipe-row;- unused attributes for each element;
leftandrightfordirection;delete,patch, andpostforverb;- required-attribute metadata for validation.
The checked-in complete example can be copied as a starting point.
To let Hyperview elements appear inside a custom element, declare an xs:any with
the Hyperview namespace, as swipe-row does. An xs:import of that namespace does
not help: every extra schema is also compiled on its own, without the Hyperview
schema, so a ref or type that names a Hyperview component is rejected.
3. Register the schema¶
Configure the local path; the bundled Hyperview validator is automatic:
from pathlib import Path
BASE_DIR = Path(__file__).resolve().parent.parent
INSTALLED_APPS = [
"django.contrib.admin",
"django_ace",
"dj_hyperview",
"dj_hyperview.contrib.database",
]
HYPERVIEW = {
"ADMIN": {
"EDITOR": True,
},
"EXTRA_SCHEMAS": [
BASE_DIR / "schema" / "hypertodo.xsd",
],
}
EXTRA_SCHEMAS accepts multiple local root files. Give each independent custom
vocabulary its own namespace. The package merges their declarations with the
bundled Hyperview 0.111.0 registry lazily.
4. Use the namespace in HXML¶
Declare the namespace on the document and use its chosen prefix:
<?xml version="1.0" encoding="UTF-8"?>
<doc
xmlns="https://hyperview.org/hyperview"
xmlns:app="https://example.com/hypertodo"
>
<screen id="tasks">
<body>
<app:swipe-row id="task-42" direction="left">
<app:swipe-action
label="Delete"
href="/hv/tasks/42/"
verb="delete"
/>
<view>
<text>Review custom schema support</text>
</view>
</app:swipe-row>
</body>
</screen>
</doc>
The same schema continues to work if the document uses another prefix such as
todo, provided that prefix maps to https://example.com/hypertodo.
5. Verify the integration¶
Run checks from the Django project directory:
The check rejects missing files, invalid XSD 1.1, remote references, and local references that escape the schema root. It also compiles every extra schema together with the bundled Hyperview schema, so two schemas that declare the same element differently, or a schema that declares a Hyperview element differently from the bundled one, fail the check instead of the first request. Then exercise at least one valid and one invalid rendered response in the consumer project's test suite.
With the editor enabled, open a database template in Django Admin and verify that autocomplete proposes the qualified custom element, contextual children, unused attributes, and enumeration values. The catalog endpoint is private and requires the template model's view permission.
Split larger schemas safely¶
An extra schema may use xs:include, xs:import, or xs:redefine only for
local files that remain under the root XSD's directory:
URLs, network retrieval, and paths such as ../shared.xsd are rejected.
schemaLocation must use a plain local path: percent encoding, backslashes,
surrounding whitespace, control or other non-printable characters, the characters
?, # and :, and any .. segment are rejected, even a segment such as
components/../swipe.xsd that stays inside the root. URI normalization could make
the compiler load a different file from the one inspected by the safety guard: the
compiler folds .. lexically, drops tabs and carriage returns from a location,
drops an empty ? or # (x.xsd? becomes x.xsd), and reads a first segment
such as 1:. as a drive, while the guard follows symlinks. A schema file whose
name contains ?, # or : therefore cannot be referenced and must be renamed.
For the plain paths it accepts, the guard reads a reference from the directory of
the file that contains it, without resolving symlinks first, as the compiler does.
A schema reached through a symlink therefore finds its dependencies beside the
link, not beside its target, and two links to one file are inspected separately
because each one finds its dependencies beside itself. The root file's own
location is not restricted, so a directory named like a%20b loads literally.
Symlinks cannot authorize dependencies outside the configured root directory. Each
root is limited to 256 distinct file paths, including itself; local include cycles
are visited once rather than recursively expanded forever.
The registry and custom completion caches share the complete transitive graph of resolved paths, sizes, and nanosecond modification times, keyed by the internal correction revision and normalized registrations. They inspect references before cache hits. Saving a deeply included schema therefore refreshes both validation and autocomplete without restarting Django.
DTD and entity declarations are forbidden in root and included schemas. Both compiler paths also disable entity expansion and reject compilation warnings, including warnings on imported schemas, rather than silently dropping a configured dependency and validating against an incomplete registry.
A schema that fails dj_hyperview.E012 is reported with its cause at the end of
the message:
The reason is the stable validation code of the failure (schema_invalid,
forbidden_declaration, forbidden_schema_reference or schema_dependency),
unreadable when the file is missing or cannot be read, or not_a_path when the
entry is not a path. It never contains a file path.
When every file passes on its own, the check compiles them all together with the bundled Hyperview schema, the same registry the first request uses, and reports a failure against the whole setting:
EXTRA_SCHEMAS must compile together with the Hyperview schema (reason: duplicate_schema_declaration).
The reason is duplicate_schema_declaration when two schemas, or a schema and the
Hyperview or alert namespaces, declare the same global name differently, or
schema_invalid when the combined registry cannot be built. A registry that
compiles cleanly is cached, so the first request reuses it instead of compiling it
again. The merged compilation is skipped while SCHEMA_EXTENSIONS has errors
(dj_hyperview.E021), because the registry cannot be built without them.
A conflict is a configuration error: every call that reads the settings raises
HyperviewConfigurationError, and the Admin shows existing templates read-only
without the editor. A schema file that starts to conflict after the settings were
first read raises TemplateValidationError from the call that notices it.
Do not use xs:override in EXTRA_SCHEMAS: it is rejected with the stable
forbidden_schema_reference validation code (dj_hyperview.E012 during
configuration checks). The only allowed override belongs to the package's fixed
trusted correction, not to arbitrary project schemas.
One corrected registry¶
All runtime and Admin paths use one corrected registry. There is no selectable profile. The configuration reference lists the verified corrections and exclusions. Arbitrary extra schemas cannot replace the fixed overlay or broaden unrelated Hyperview types.
Know the boundary¶
The schema validates rendered XML structure and feeds editor suggestions. It does not implement a custom mobile component, register a Hyperview behavior, or prove that the React Native client supports the element. The project must implement and test that client-side contract separately.
Publication of a database template compiles Django template syntax but does not render it. Values and branches that depend on runtime context are validated when the final document or fragment is served.
Typed registrations and registry identity¶
Use SCHEMA_EXTENSIONS when a registered mobile
behavior or a known built-in element needs additional typed attributes. Keep
namespaced custom components in EXTRA_SCHEMAS. Neither setting installs a
mobile component or behavior. The configuration reference
contains the complete descriptor contract and a small example.
For a registered action, the package generates an in-memory trusted XSD 1.1
alternative with its exact attributes. The final alternative retains the
standard behavior type. A message allowed on show-snackbar therefore remains
invalid on reload or an unknown action. An image.variant registration does
not admit variant on text, and unrelated attributes remain forbidden.
Only package code produces this overlay from normalized data. Registrations
cannot contain raw XSD, XPath, import callbacks, custom regexes, or schema
locations. External xs:override stays forbidden. Existing local-reference,
DTD/entity, symlink-confinement and transitive-file limits remain unchanged.
Conflicting global declarations across extra schemas are reported by the system
check (dj_hyperview.E012) and rejected before runtime validation, static Admin
validation or catalog use; matching completion
enums alone do not prove matching XSD types or child models. This comparison
also includes effective schema defaults (such as local attribute/element forms
and default attributes), node-scoped QName bindings, and XPath namespace context.
Changing the order of EXTRA_SCHEMAS cannot select between incompatible
interpretations of the same declaration. Equivalent explicit overrides remain
accepted; for example, local form="unqualified" is independent of the root's
attributeFormDefault.
Custom elements may also use simple XSD types, such as xs:integer or a named
string enumeration. Their catalogs contain no attributes or child elements;
rendered validation still enforces the value type and rejects unknown attributes.
Admin source checks reject those attributes without evaluating dynamic text.
The registry snapshot is detached and immutable. Its identity includes the
internal correction revision, normalized registrations and all configured dependency fingerprints.
References are checked before cache reuse. The compiled registry and the catalogs
are cached by that identity, so a change to the HYPERVIEW setting does not clear
them: another EXTRA_SCHEMAS or SCHEMA_EXTENSIONS value selects another entry,
and the same value reuses the compiled one, as does a change to any other key.
Each cache keeps at most 64 entries.
Catalog format 2¶
Active catalog responses always contain catalog_format: 2 and behavior_variants, keyed
by the literal registered action. Each variant has the same element-definition
shape as elements.behavior, with its complete allowed attributes. The baseline
behavior completion offers registered action names, but not their unrelated
attributes. Static validation's baseline retains only standard action names;
it selects a registered variant when the action is known literally.
Unqualified attribute keys remain local names; qualified keys use Clark names,
for example {https://hyperview.org/hyperview-alert}message. The editor resolves
the prefix actually declared in the source, rather than confusing that attribute
with an unqualified message. Dynamic actions receive conservative suggestions
and require rendered validation for a complete answer.
The packaged corrected catalog is generated deterministically from its fixed schema;
regressions compare it with fresh schema introspection and check the approved
attribute deltas. Runtime variants are derived from the same compiled registry
as validation. The config-free get_hyperview_schema_path() and
build_hyperview_catalog() helpers retain their archived 0.110.0 upstream
inspection meaning and
original catalog format. That inspection output is not the active runtime registry.
Migration boundary¶
Validate a synthetic rendered corpus, including error and conditional branches, before upgrading an existing application to automatic validation. Correct invalid structure in the application rather than adding a permissive schema exception. Existing database templates can override corrected filesystem templates; review those effective rows separately with explicit database authorization. This package performs no data migration, publication, cache flush or deployment.