Skip to main content

Zibanu Django Template

Optional Zibanu Django package with helpers for the template layer: a context processor that publishes site-wide values to every template, plus the zb_tags library of template tags and filters.

Overview

zibanu.django.template has no models, no migrations and no REST endpoints. It is a presentation-layer package only, and it consists of three things:

  • zb_context_data — the supported context processor, publishing eight site-wide keys to every template. full_static_uri and site are kept as deprecated wrappers that delegate to it and publish a single key each.
  • zb_tags — one template tag library holding five tags (zb_concat, zb_query_filter, zb_static_uri, zb_subtotal_dict, zb_sum_dict) and three filters (zb_divide, zb_multiply, zb_replace).
  • Settings defaults — ZB_FULL_URI and the five ZB_SITE_* keys, applied in ZbDjangoTemplate.ready(), which is the single source of those defaults.

The package imports only Django and the standard library: it depends neither on the zibanu.django base package nor on any other optional Zibanu package.

Installation

Install the package from its standalone installer:

pip install ./installers/zibanu-django-template/

Add it to INSTALLED_APPS in mainApp/components/base.py (the Django app label is zb_template):

INSTALLED_APPS = [
    # ... existing apps ...
    "zibanu.django.template",
]

No migrate step is needed — the package defines no models.

Installing the app is mandatory, not optional wiring: ZbDjangoTemplate.ready() is what applies the ZB_* setting defaults that zb_context_data reads, and Django only discovers the zb_tags library inside an application listed in INSTALLED_APPS. Without it, {% load zb_tags %} fails to resolve and zb_context_data raises AttributeError on the first missing ZB_* setting.

Quick Start

Wire the context processor into your TEMPLATES setting:

TEMPLATES = [
    {
        "BACKEND": "django.template.backends.django.DjangoTemplates",
        "OPTIONS": {
            "context_processors": [
                # ... Django's own processors ...
                "django.template.context_processors.request",
                "zibanu.django.template.context_processors.zb_context_data",
            ],
        },
    },
]

django.template.context_processors.request is required alongside it: the zb_static_uri tag reads request from the render context and raises TemplateSyntaxError when it is absent.

Then, in a template:

{% load zb_tags %}
<img src="{% zb_static_uri "img/logo.png" %}" alt="{{ site_name }}">
<p>{{ site_description }} — v{{ site_version }}</p>

Features

  • Eight site-wide context keys published by a single context processor.
  • Absolute (scheme + host) static and media URIs, suitable for content that leaves the site — e-mail bodies, PDF renderings — where Django's {% static %} host-relative path is not enough.
  • Arithmetic filters that normalize both operands through Decimal(str(x)), so string, int, float and Decimal template values can be mixed without float rounding artifacts.
  • Fail-soft template callables: zb_divide, zb_multiply and zb_query_filter never raise — bad input renders as nothing instead of turning a page into a 500.
  • Grouping and aggregation tags (zb_subtotal_dict, zb_sum_dict) for report-style templates with subtotal lines.
  • Translatable error messages (gettext_lazy); catalogs live in locale/.

Usage

Context keys published by zb_context_data

Key Value
full_static_uri Absolute URI of the static base, built from the request + STATIC_URL (falls back to / when STATIC_URL is unset or empty)
full_media_uri Absolute URI of the media base, built from the request + MEDIA_URL (same / fallback)
full_uri ZB_FULL_URI when non-empty, otherwise the request's absolute URI of /
site_name ZB_SITE_NAME
site_logo ZB_SITE_LOGO
site_favicon ZB_SITE_FAVICON
site_description ZB_SITE_DESCRIPTION
site_version ZB_SITE_VERSION

All eight are available in every template rendered with a RequestContext once the processor is listed in TEMPLATES["OPTIONS"]["context_processors"].

Tags and filters at a glance

The kind of each name matters: a tag is invoked as {% name ... %}, a filter as {{ value|name:"arg" }}. The table below matches the actual registrations in templatetags/zb_tags/__init__.py.

Name Kind Registration Notes
zb_concat tag register.simple_tag Concatenates every argument through str(); a None renders as the literal None.
zb_query_filter tag register.simple_tag Filters (and optionally orders) a queryset. Requires as var. The order keyword is reserved for order_by.
zb_static_uri tag register.tag (compiled) Takes exactly one argument. Requires request in the context and a non-empty STATIC_URL.
zb_subtotal_dict tag register.simple_tag Groups a list of dicts by a control key and subtotals chosen keys. Requires as var.
zb_sum_dict tag register.simple_tag Sums one key across a list of dicts; returns a formatted string, so it renders inline.
zb_divide filter register.filter Decimal division. Returns Decimal, or "" on a non-numeric operand or a zero divisor.
zb_multiply filter register.filter Decimal multiplication. Returns Decimal, or "" on a non-numeric operand.
zb_replace filter register.filter Substring replacement driven by a single "what|to" argument.

Basic Usage

{% load zb_tags %}

{# Absolute URI of a static resource — literal or context variable #}
<img src="{% zb_static_uri "img/logo.png" %}">
<img src="{% zb_static_uri resource_path %}">

{# String concatenation #}
{% zb_concat "order-" order.id %}          {# order-42 #}

{# Arithmetic filters #}
{{ total|zb_divide:count }}
{{ price|zb_multiply:quantity }}

{# Substring replacement: the argument holds both operands, separated by a pipe #}
{{ "aaa"|zb_replace:"a|b" }}               {# bbb #}
{{ code|zb_replace:"-|" }}                 {# empty replacement deletes the substring #}

{# Aggregation that renders inline #}
Total: {% zb_sum_dict rows "amount" ",.2f" %}   {# 1,250.00 #}

Tags that return data structures need as var

zb_query_filter and zb_subtotal_dict return a QuerySet and a list respectively. As simple tags, their return value is rendered directly unless it is captured, so they must be assigned with as <var> and consumed afterwards; used bare, the template prints the object's repr (<QuerySet [...]>, [{'control': ...}]).

{% zb_query_filter articles status="published" order="-created_at" as published %}
{% for article in published %}
  <li>{{ article.title }}</li>
{% endfor %}

{% zb_subtotal_dict rows "zone" "amount" as groups %}
{% for group in groups %}
  <h3>{{ group.control }}</h3>
  {% for row in group.data %}<p>{{ row.amount }}</p>{% endfor %}
  <strong>{{ group.totals.amount }}</strong>
{% endfor %}

zb_concat and zb_sum_dict return strings and are meant to render inline, so they do not need as (they accept it, like any simple tag).

Advanced Usage — subtotals

zb_subtotal_dict(source_list, key_control, *args) returns one dictionary per group, each with control (the shared value of the control key), totals (one entry per key named in *args) and data (the group's records, copied without the control key). Behavior worth knowing before using it:

  • The input must already be sorted by the control key. A group is closed only when the value changes between adjacent records, so unsorted input yields one repeated group per run of equal values instead of a single group.
  • Records lacking the control key are grouped under control = None.
  • Only int, float and Decimal values feed a subtotal; booleans are excluded on purpose, and any other value is ignored for the total while still being kept in data.
  • A key listed in *args that never appears keeps its initial subtotal of 0; an empty input yields an empty list, with no spurious empty group.
  • Passing no subtotal key raises TemplateSyntaxError — this is the one callable of the package that raises by design.

zb_sum_dict(source_list, key, format_string=None) applies the same numeric rules over a flat list and always returns a string: non-dict records and a non-list source_list are skipped (zero sum, no error), and an invalid format_string falls back to str(sum).

When to use zb_query_filter

This tag builds a database query in the template layer. That makes the query invisible to view-level tests, makes N+1 traversals easy to write by accident, and degrades data errors to a silently blank region (the failure is logged as a warning through the root logger and reported as a None return). Reserve it for ad-hoc, report-style templates where the filtering is genuinely presentational. Anything a view, a manager or a serializer can express belongs there instead, where the query stays testable and its failure stays visible.

Note also that order is consumed as a control keyword and never applied as a lookup, so a model field actually named order cannot be filtered through this tag.

API Reference

Context processors — zibanu.django.template.context_processors

  • zb_context_data(request) -> dict — the supported entry point. Returns the eight keys listed above. Raises AttributeError if a ZB_* setting is missing because the package is not in INSTALLED_APPS and ZbDjangoTemplate.ready() never applied the defaults.
  • full_static_uri(request) -> dict — deprecated. Returns {"full_static_uri": ...} by delegating to zb_context_data. Superseded by it; kept for backwards compatibility only.
  • site(request) -> dict — deprecated. Returns {"site": ...}, taking the value from zb_context_data's full_uri key (note the published key differs from the source key). Superseded by zb_context_data; kept for backwards compatibility only.

New projects should wire only zb_context_data: it already publishes both values.

Template library — zibanu.django.template.templatetags.zb_tags

Owns the register object Django loads for {% load zb_tags %} and exports register, StaticNodeUri and the eight callables.

  • zb_concat(first_arg, *args) -> str — concatenation of every argument coerced with str(); equals str(first_arg) when no extra value is given.
  • zb_query_filter(queryset, **kwargs) -> QuerySet | str — applies **kwargs as lookups, except the reserved order key, whose value goes to order_by. Returns "" when the input is not a queryset or a lookup/value/ordering field is invalid (AttributeError, FieldError, TypeError, ValidationError, ValueError are caught and logged). The returned queryset stays lazy.
  • zb_static_uri(parse, token) -> StaticNodeUri — compilation function registered with register.tag; raises TemplateSyntaxError unless the tag receives exactly one argument.
  • StaticNodeUri(template.Node) — node produced by the above. render(context) returns the absolute STATIC_URL followed by the resolved resource path, and raises TemplateSyntaxError at render time when the context has no request or when STATIC_URL is undefined/empty. The resource path is kept as a compiled filter expression, so it may be a literal, a variable or a filtered expression, resolved on every render.
  • zb_subtotal_dict(source_list, key_control, *args) -> list[dict] — see Advanced Usage. Raises TemplateSyntaxError when no subtotal key is given.
  • zb_sum_dict(source_list, key, format_string=None) -> str — see Advanced Usage.
  • zb_divide(value, arg) / zb_multiply(value, arg) — decimal arithmetic over Decimal(str(x)) operands; return "" instead of raising on invalid input (and, for zb_divide, on a zero divisor).
  • zb_replace(value, arg) -> str — @stringfilter; arg is "<what>|<to>" and must split into exactly two parts, otherwise value is returned unchanged (a silent no-op, not an error). An empty <to> deletes the substring.

templatetags/__init__.py is an intentional package marker with no code: it must stay in place for {% load zb_tags %} to resolve, and must not define a register of its own.

Application config — zibanu.django.template.apps

  • ZbDjangoTemplate(AppConfig) — name = "zibanu.django.template", label = "zb_template", translatable verbose_name. ready() is the single source of the package setting defaults: it applies each ZB_* default with getattr(settings, key, default), so any value already declared by the project is preserved.

Configuration

All six settings are optional — ZbDjangoTemplate.ready() fills in the defaults below — and all six are consumed by zb_context_data.

Setting Default Purpose
ZB_FULL_URI "" Canonical site URI; empty makes the processor fall back to the request's absolute URI of /
ZB_SITE_NAME "Zibanu Django" Site name, published as site_name
ZB_SITE_LOGO "" Logo location, published as site_logo
ZB_SITE_FAVICON "" Favicon location, published as site_favicon
ZB_SITE_DESCRIPTION "" Site description, published as site_description
ZB_SITE_VERSION "0.0.0" Site version, published as site_version

Following the project convention, declare them in their own component file under mainApp/components/ and include it from mainApp/settings.py:

# mainApp/components/zb_template.py
ZB_FULL_URI = "https://app.example.com/"
ZB_SITE_NAME = "My Application"
ZB_SITE_VERSION = "2.1.0"

Setting ZB_FULL_URI explicitly is what keeps full_uri (and the deprecated site key) correct behind a reverse proxy, or in worker-rendered output such as e-mail, where the request host is not the public one.

Django settings read by the package but not owned by it: STATIC_URL (used by zb_context_data and required, non-empty, by zb_static_uri) and MEDIA_URL.

Testing

python manage.py test unit_tests.template

There is currently no unit_tests/template/ module in this repository — the package sits at 0% test coverage. (This project has no local execution environment; tests run on the remote server declared in .claude/project.config.json.)

Requirements

  • Python 3.11+
  • Django

Nothing else: the package imports only django and the standard library (decimal, logging). It does not depend on the zibanu.django base package or on any other optional Zibanu package, so it can be installed on its own.

License

GNU General Public License v3 — see the LICENSE file in this package and the headers in apps.py. Developed by CQ Inversiones SAS. Copyright © 2019–2026.

Changelog

All notable changes to this package are documented here, one section per principal version (x.y.z). Pre-releases of the same version are folded into its section.

[2.0.0] — 2026-10-01

Added

  • Bilingual (EN/ES) README.md documenting the package's tags, filters, settings and the eight context keys published by zb_context_data.

Changed

  • Breaking: the app's Django label is now the explicit zb_template (it was previously the implicit template, derived from the dotted app name), matching the naming convention of the sibling optional packages. Code that looked the app up by its old implicit label (e.g. apps.get_app_config("template")) needs updating.
  • Breaking: zb_subtotal_dict no longer returns a placeholder group for an empty input list — it now correctly returns [] instead of a single entry with zeroed totals and no data.
  • full_static_uri and site are now deprecated wrappers delegating to zb_context_data, the supported entry point; both keep returning the same keys/values as before, so existing callers are unaffected.
  • full_static_uri and site now emit a DeprecationWarning on every call (.. deprecated:: 2.0.0); both wrappers are scheduled for removal in 3.0.0.
  • zb_static_uri now compiles its argument with Django's filter-expression compiler instead of manually stripping quotes off the raw token, which also allows the argument to be a template variable, not only a quoted literal.
  • zb_divide/zb_multiply now normalize both operands through Decimal(str(x)), so numeric strings are accepted and mixed-type operands work; zb_multiply no longer silently repeats a string operand as a sequence instead of rejecting it.
  • zb_query_filter's AttributeError fallback and zb_divide's divide-by-zero/non-numeric fallback now return "" instead of None, so a failed calculation renders blank in a template instead of the literal string "None".
  • site() now honours ZB_FULL_URI (by delegating to zb_context_data) instead of always returning the raw request URI, aligning it with zb_context_data's own full_uri.
  • Breaking: zb_replace now partitions its argument on the first pipe instead of splitting on every pipe, so the <to> operand may contain literal pipes ("x|a|b" replaces x with a|b). An argument with two or more pipes was previously a silent no-op; it now performs a replacement. A pipe-less argument remains a documented no-op.
  • zb_context_data now re-raises a missing ZB_* setting as ImproperlyConfigured with an actionable message (add zibanu.django.template to INSTALLED_APPS) instead of a bare AttributeError, and memoises its dictionary on the request (request._zb_context_data) so delegating processors no longer recompute it.

Fixed

  • Critical: zb_static_uri could never render: it tested hasattr(context, "request"), which is never true on a Django template Context, so the tag always raised "requires 'request' var in context" regardless of whether the request context processor was enabled.
  • Critical: zb_context_data carried a stray, unused import of jinja2.compiler.has_safe_repr (left over from when ZB_FULL_URI support was added in 1.0.4). In an environment without jinja2 installed, importing this module — and therefore rendering any template using the context processor — raised ImportError.
  • Critical: zb_sum_dict's numeric-type guard (type(x) == int or float) was always true regardless of x's actual type (or float makes the expression unconditionally truthy), so a non-numeric value in the summed column raised TypeError instead of being skipped.
  • zb_replace now accepts non-string arguments (coerced via @stringfilter/ str()) instead of raising AttributeError.
  • zb_sum_dict's optional format spec is now applied inside a try/except, so an invalid format string degrades to the plain value instead of raising.
  • Spanish translation catalog (locale/es/) regenerated for the renamed zb_static_uri user-facing error messages.

Documentation

  • NumPy-style docstrings added across the package.

[1.0.4] — 2026-07-22

Added

  • ZB_FULL_URI setting: when set, zb_context_data publishes it verbatim as full_uri instead of deriving it from the current request's absolute URI — useful behind a reverse proxy where the request's own host doesn't match the public site URL.

[1.0.3] — 2026-05-15

Added

  • zb_context_data, a new context processor publishing eight keys in one call: full_static_uri, full_media_uri, full_uri, site_name, site_logo, site_favicon, site_description and site_version.
  • ZB_SITE_NAME, ZB_SITE_LOGO, ZB_SITE_FAVICON, ZB_SITE_DESCRIPTION and ZB_SITE_VERSION settings, defaulted in ZbDjangoTemplate.ready() so a consumer project gets them without declaring anything.

This version went through an alpha/beta promotion cycle before its final release; no package changes beyond the version marker itself landed during that cycle.

[1.0.2] — 2025-08-27

Added

  • zb_query_filter accepts an order keyword, applying .order_by() to the filtered queryset before returning it.

[1.0.1] — 2025-04-01

Added

  • zb_replace template filter, to replace substrings in template output ({{ value|zb_replace:"needle|replacement" }}).

Fixed

  • Critical: zb_divide and zb_multiply, present since 1.0.0, were registered with the wrong keyword argument (function= instead of filter_func=), which left them unusable as template filters.

[1.0.0] — 2025-03-14

Initial release. Provides the zb_tags template tag/filter library (zb_concat, zb_divide, zb_multiply, zb_query_filter, zb_static_uri, zb_subtotal_dict, zb_sum_dict) and the full_static_uri/site context processors.

Metadata

Release files for zibanu-django-template 2.0.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for zibanu-django-template 2.0.0
File Size Uploaded
zibanu_django_template-2.0.0.tar.gz 51.8 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for zibanu-django-template 2.0.0
File Interpreter ABI Platform
zibanu_django_template-2.0.0-py3-none-any.whl Python 3 none any Details

Total release size: 95.4 kB

Release files / zibanu_django_template-2.0.0.tar.gz

Download URL zibanu_django_template-2.0.0.tar.gz
Size 51.8 kB
Tags Source
SHA-256 checksum
How to use checksums
389a43cfc226f7329b2d8e0ca1abaf3a2361887718e050c0393002e6a64949e0
BLAKE2b-256 checksum
How to use checksums
6f4ca323e04327c5f2c4dcc6ae5faf75aa013b94f801f170a13d95ece263709c
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/6.2.0 CPython/3.11.13

Release files / zibanu_django_template-2.0.0-py3-none-any.whl

Download URL zibanu_django_template-2.0.0-py3-none-any.whl
Size 43.6 kB
Tags Python 3
SHA-256 checksum
How to use checksums
7c3eb8213b59fad6698fc8c53e1eca0a96a63f648e3416756332495e571aeec8
BLAKE2b-256 checksum
How to use checksums
c85577133cd7ed3dea1ea3c16a4b0753c1484702ec287f4f9ebc7290ad0c0d47
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/6.2.0 CPython/3.11.13

Release history Release notifications | RSS feed

This release

2.0.0 This release

2 release files

1.0.4

2 release files

1.0.3

2 release files

1.0.2

2 release files

1.0.1

2 release files

1.0.0

2 release files

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