Files
Forge-Engine/screens/rendering/resolve_bound_row.py
T
william aa3be503ce Expose relation fields in the champ pickers and fix their column resolution
data_definition_options() used to exclude relation-type fields from its field list entirely, so a filter/binding could never reference "the linked object" of a row — and even when a relation field's clean name was typed manually (as the earlier "level.parcour" example needed), it silently matched nothing: every column lookup for a filter/repeater field used the field's own name, but a relation is actually stored in a "<field>_id" column (see create_definition.py), so the lookup always missed.

Relation fields now appear in the champ dropdowns (Répéteur's filtre_champ/filtre2_champ, Donnée liée's data_filtre_champ/data_filtre2_champ) labeled with the object they point to (e.g. "parcour (→ parcours)"), and a new _field_column() helper in filter_repeater_rows.py resolves the right "<field>_id" column whenever the field turns out to be a relation — used consistently by the filter comparison itself, the "{{Objet.champ}}" dynamic-value resolver, the repeater's row content ({{champ}}), and the Donnée liée row context. Jauge's own champ/champ_nom pickers (which need an actual displayable value, not an id) still exclude relations, both server- and client-side.

Verified end to end: the dropdown shows the relation field with its target-object label, and filtering "level" rows by the clean relation field name "parcour" (not "parcour_id") against a dynamic {{game.current_parcours}} reference now actually matches, alongside the existing "number" filter. Full suite green (89).
2026-08-25 06:48:04 +02:00

39 lines
1.8 KiB
Python

import db
from .filter_repeater_rows import filter_repeater_rows, _field_column
def _resolve_bound_row_ctx(slug, attrs):
"""Si cet élément est lié à un objet de données (attr:_data_definition_id —
réglage "Lier à un objet de données" des widgets Texte/Titre, voir
DATA_BINDING_CONTROLS), retrouve LA ligne qui correspond aux 1 ou 2
filtres réglés (_data_filtre*/_data_filtre2*) — même moteur que le
filtre du Répéteur de données, voir filter_repeater_rows.py — et
renvoie ses champs sous forme de {nom_du_champ: valeur en texte},
exploitable par {{champ}} dans le contenu. Permet d'afficher UNE seule
valeur calculée (ex: l'objectif du niveau en cours) sans avoir besoin de
poser un Répéteur, qui affiche lui plusieurs lignes. Renvoie None si
rien n'est lié ou si aucune ligne ne correspond."""
definition_id = attrs.get("_data_definition_id")
if not definition_id or not slug:
return None
try:
definition = db.get_definition(slug, int(definition_id))
except (TypeError, ValueError):
return None
if not definition:
return None
rows = db.list_rows(slug, definition)
rows = filter_repeater_rows(slug, definition, rows, {
"_filtre_champ": attrs.get("_data_filtre_champ"),
"_filtre_operateur": attrs.get("_data_filtre_operateur"),
"_filtre_valeur": attrs.get("_data_filtre_valeur"),
"_filtre2_champ": attrs.get("_data_filtre2_champ"),
"_filtre2_operateur": attrs.get("_data_filtre2_operateur"),
"_filtre2_valeur": attrs.get("_data_filtre2_valeur"),
})
if not rows:
return None
row = rows[0]
return {f["name"]: str(row.get(_field_column(definition, f["name"]), "") or "") for f in definition["fields"]}