stapel-categories
Category tree with typed features: a hierarchical category tree (django-treenode) and a parallel feature tree whose typed config is validated by stapel-attributes, an ordered category<->feature M2M, feature inheritance, and a feature-editor lifecycle (keep/add/edit/inherit/remove/create/replace) with optimistic-concurrency apply over a category subtree.
Part of the Stapel framework — composable Django apps that deploy as a monolith or as microservices without changing module code.
Install
pip install stapel-categories
At a glance
| Fact | Value |
|---|---|
| Version | 0.17.0 |
| Python | >=3.11 (3.11, 3.12, 3.13, 3.14) |
| HTTP operations | 33 |
| Config axes | 1 |
| Usage surface | 22 |
| Extension points | 4 |
| Error codes | 64 |
| Fleet dependencies | stapel-attributes · stapel-core |
Documentation
OpenAPI · capabilities.json · llms.txt (for agents)
What this is
A hierarchical category tree (django-treenode) and a parallel feature
tree whose typed config is validated by
stapel-attributes. Categories
own the tree structure, feature inheritance, the ordered category↔feature M2M,
and the feature-editor lifecycle; the attribute engine (types, config/DTO/DAO
validation, polymorphic serializers, admin widgets) lives in stapel-attributes
and is imported, never re-implemented.
Quick start
INSTALLED_APPS = [
# ...
"treenode", # django-treenode (tree-cache signals)
"stapel_categories",
]
# urls.py — this module's own urls.py bakes in only `v1/`; the host
# contributes `api/`, giving the canonical `/categories/api/v1/...` prefix
# (exactly what stapel-example-monolith already does for this module).
path("categories/api/", include("stapel_categories.urls"))
stapel-attributes is an imported library (no app to install); its config
editor ships static assets, so run collectstatic if you use the admin.
Settings
All configuration lives in the STAPEL_CATEGORIES namespace (dict setting,
flat setting, or env var — resolved lazily):
| Key | Default | Meaning |
|---|---|---|
CAROUSEL_CACHE_TIMEOUT |
300 |
Seconds the carousel response is cached. |
FEATURE_DISPLAY_CACHE_TIMEOUT |
60 |
Seconds an admin feature display label is memoized. |
DISPLAY_TRANSLATOR |
stapel_categories.translation.identity_translator |
Dotted path (key)->str for rendering translation keys (default: identity). |
comm surface
| Kind | Name | Contract |
|---|---|---|
| Function | categories.features |
{"category_id": int} -> {"category_id", "revision", "features":[{id,slug,name,mandatory,config}]} — resolved schema (own + inherited), cacheable by revision |
| Function | categories.path |
{"category_ids": [int, ...]} -> {"<id>": ["<root_id>", ..., "<id>"]} — root->leaf ancestry, one query for the batch; segments are ids, an unknown id is absent |
| Action (emit) | category.changed |
{"category_id": int, "revision": int} on any category/feature mutation — for downstream cache invalidation |
categories.features lets stapel-listings validate attribute values against a
category's schema without importing this module. categories.path is the
provider stapel-search declares by canonical name for category rollup — without
it a search index degrades to a single path segment and a filter on a parent
category finds none of its descendants.
Contract
docs/{schema,flows,errors}.json are emitted from a single-module
{categories + core} Django instance mounted at the canonical
/categories/api/v1 prefix (make contract / make contract-check; see
_codegen.py) — the same mechanism stapel-search, stapel-chat and
stapel-forms already use. docs/flows.json is []: no flow is declared via
@flow yet, same state as every other contract-complete module today.
docs/capabilities.json stays hand-authored (see the Makefile comment); only
its surface section is derived.
The ten feature-value shapes (FeatureConfig, FeatureDto) are a proper
discriminated oneOf keyed by type, contributed by stapel-attributes and
now used consistently everywhere a config or a values-DTO crosses the wire —
Feature.config, FeatureBulk.config, the convert-type request body and
validate-dto's features field all resolve through it (previously the
last three fell back to an untyped JSONField/DictField).
Delta note — one pair of fields stays untyped, and it isn't this module's
to fix. FeatureValidationResult.id / .ref_value render as free-form in
the schema. Both are defined by stapel-attributes
(FeatureValidationResult dataclass + its serializers.JSONField
projection in results.py), not by this module: id is
Optional[Union[int, str]] and ref_value is
Optional[Union[str, int, float, list]] — plain scalar unions with no
type discriminator to key a oneOf on, unlike the ten-way config/DTO
shapes. Typing them is upstream's serializer to extend, not a gap
stapel-categories introduced or can close by itself.
Extension points
See MODULE.md — the agent-facing map of every fork-free seam (settings, serializer seams, comm surface, feature-editor actions, admin-UI pointer to stapel-attributes).
Development
pip install -e . && pip install pytest pytest-django ruff
./setup-hooks.sh
pytest tests/
License
MIT — see LICENSE.
This page is assembled by stapel-readme from docs/readme.md plus the contract artifacts in docs/. Edit the prose in docs/readme.md; the badges, facts and links above and below it are generated — do not hand-edit README.md.
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 stapel_categories-0.17.0.tar.gz.
File metadata
- Download URL: stapel_categories-0.17.0.tar.gz
- Upload date:
- Size: 208.8 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via:
twine/7.0.0 CPython/3.13.14
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
4bc7f2736ffc3749373aba20a2c0b870283c4d2217b4c3f72907bd4203a0c7d3
|
|
| MD5 |
be42881cd514d6f4c357a976537d0f6e
|
|
| BLAKE2b-256 |
486727831406eb16f53bd3d5c15e76d4c7fe151c283c51039d1451bfe9b30497
|
Provenance
The following attestation bundles were made for stapel_categories-0.17.0.tar.gz:
Publisher:
publish.yml on usestapel/stapel-categories
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
stapel_categories-0.17.0.tar.gz -
Subject digest:
4bc7f2736ffc3749373aba20a2c0b870283c4d2217b4c3f72907bd4203a0c7d3 - Sigstore transparency entry: 2695610811
- Sigstore integration time:
-
Permalink:
usestapel/stapel-categories@58b5dc1d69f4a7f3648b559873bfdd2a9b1638be -
Branch / Tag:
refs/tags/v0.17.0 - Owner: https://github.com/usestapel
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@58b5dc1d69f4a7f3648b559873bfdd2a9b1638be -
Trigger Event:
push
-
Statement type:
File details
Details for the file stapel_categories-0.17.0-py3-none-any.whl.
File metadata
- Download URL: stapel_categories-0.17.0-py3-none-any.whl
- Upload date:
- Size: 145.1 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 |
42f1aa5b660c87c3cbe9d940079ceb967c01b6a4273c7c1e854779d1e0574301
|
|
| MD5 |
80fd8b384129b2d04710191c59e55b73
|
|
| BLAKE2b-256 |
e76f2acf6294725f29989fd42ed6bfdde3b3c3a9a38176bc12b80698e3a78bbb
|
Provenance
The following attestation bundles were made for stapel_categories-0.17.0-py3-none-any.whl:
Publisher:
publish.yml on usestapel/stapel-categories
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
stapel_categories-0.17.0-py3-none-any.whl -
Subject digest:
42f1aa5b660c87c3cbe9d940079ceb967c01b6a4273c7c1e854779d1e0574301 - Sigstore transparency entry: 2695610887
- Sigstore integration time:
-
Permalink:
usestapel/stapel-categories@58b5dc1d69f4a7f3648b559873bfdd2a9b1638be -
Branch / Tag:
refs/tags/v0.17.0 - Owner: https://github.com/usestapel
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@58b5dc1d69f4a7f3648b559873bfdd2a9b1638be -
Trigger Event:
push
-
Statement type: