{% from "admin/components/icons.html" import icon %} {# Context-aware read-only rendering of a field value. Default renderer, overridable by replacing this template. `with context` so the macro can see `admin`, `base_path`, and `relation_permissions` from whichever page imports it, without every call site having to pass them through explicitly. #} {% macro render_related_link(relation, related_obj) %} {%- set target_admin = admin.get_model_admin(relation.target) if admin else none -%} {%- set label = target_admin.get_field(relation.display_field).get_value(related_obj) if target_admin else related_obj -%} {%- if target_admin and relation_permissions.get(relation.target) -%} {{ label }} {%- else -%} {{ label }} {%- endif -%} {% endmacro %} {# A boolean renders as a check or a cross rather than "Yes"/"No": a glyph reads as a shape at a glance where two similar-length words have to be read. The word stays as an sr-only label, so nothing depends on the icon alone. Exports stringify through core/exporter.py and are untouched. The emerald pair is picked for contrast against bg-card in both themes, since there is no shadcn "success" token to defer to. #} {% macro render_field_value(field, value, empty=DEFAULT_EMPTY_VALUE) -%} {#- Blank counts as empty, not just None: a column of empty cells and a column of dashes say different things to a reader, and "" is by far the more common way a value goes missing in practice. A zero or a False is a real value and must not be dashed. -#} {%- if value is none or (value is string and not value.strip()) -%} {{ empty }} {%- elif field.field_type == "boolean" -%} {%- if value -%} {{ icon("check", "size-4") }}{{ _("Yes") }} {%- else -%} {{ icon("close", "size-4") }}{{ _("No") }} {%- endif -%} {%- elif field.field_type == "password" -%} •••••••• {%- elif field.field_type in ("foreignkey", "onetoone") -%} {{ render_related_link(field.relation, value) }} {%- elif field.field_type == "manytomany" -%} {%- if not value -%} {{ empty }} {%- else -%} {%- for related_obj in value -%} {{ render_related_link(field.relation, related_obj) }}{% if not loop.last %}, {% endif %} {%- endfor -%} {%- endif -%} {%- elif field.field_type == "date" and value|iso_date -%} {%- elif field.field_type == "datetime" and value|iso_datetime -%} {%- elif field.field_type == "decimal" -%} {{ value|decimal_display }} {%- elif field.field_type == "image" -%} {# A failed load flips the img and its fallback sibling's inline display rather than replacing DOM: no JS dependency beyond the one-line onerror. Hidden via an inline style, not the `hidden` attribute: the browser's `[hidden] { display: none }` is a lowest-priority UA rule, so the fallback's own display:inline-flex (an ordinary author-stylesheet rule, which always outranks a UA one regardless of selector order) would otherwise win and show it beside every successfully-loaded thumbnail. An inline style outranks both. #} {%- else -%} {{ value }} {%- endif -%} {%- endmacro %}