Skip to content

Custom entities in a template

Read the records a reference attribute links to, load their own attributes with export.load_custom_entities, and look custom entity records up by any value.

A reference attribute links a product to records of a custom entity — its certificates, its manufacturer, its care instructions. The attribute itself carries only each linked record’s id, name and identifier; to read anything else you need export.load_custom_entities, or export.custom_entity for lookups that are not driven by a reference. This page covers all three.

When it has links, it holds a list, even if there is only one.

Each reference carries the id, name and identifier of the linked record — but none of its other values:

Property Description
target_id Internal id of the linked record
target_name Name of the linked record, from the custom entity’s label attribute
target_identifier The linked record’s identifier
{{ for m in record.manufacturer }}
<manufacturer>{{ m.target_name }}</manufacturer>
{{ end }}

Reading every linked record — export.load_custom_entities

Section titled “Reading every linked record — export.load_custom_entities”

To read a linked record’s own attributes — not just its id, name and identifier — load it with export.load_custom_entities, naming the record (or a variant) and the reference attribute’s code:

{{ for cert in export.load_custom_entities(record, 'certificates') }}
<certificate id="{{ cert._id.entityid }}">
<name>{{ cert._meta.name }}</name>
<number>{{ cert.certificate_number }}</number>
<valid_until>{{ cert.valid_until | date.to_string '%Y-%m-%d' }}</valid_until>
</certificate>
{{ end }}

This returns every linked record, in the order they were linked on the product — one call, whatever the attribute’s cardinality. An attribute with no links returns an empty list, so the loop needs no separate guard. Duplicates (the same record linked twice) are kept, not collapsed.

The variant form works the same way, for a reference attribute defined on variants rather than the product:

{{ for v in variants }}
{{ for c in export.load_custom_entities(v, 'care_instructions') }}<care>{{ c.text }}</care>{{ end }}
{{ end }}

On a loaded record:

Property Description
cert.my_attribute_code Value of that attribute
cert._meta.name Name of the record
cert._meta.identifier Identifier of the record
cert._id.entityid Internal id of the record

_meta.name and _meta.identifier always match the reference’s own target_name and target_identifier, so a loop over export.load_custom_entities does not need to carry the reference alongside it.

A wrong attribute code, an attribute that is not a reference attribute, or a reference to something other than a custom entity (a product reference, for example) is reported in the job log — see background jobs.

Reading one linked record you already have a reference for

Section titled “Reading one linked record you already have a reference for”

If a template loops record.<attribute> itself — to read target_name, or to keep the link order alongside other work — export.custom_entity(key).get accepts the reference directly, instead of picking target_id and the lookup field apart by hand:

{{ for r in record.certificates }}
{{ cert = export.custom_entity('certificates').get(r) }}
{{ if cert }}
<certificate number="{{ cert.certificate_number }}">{{ r.target_name }}</certificate>
{{ end }}
{{ end }}

get(r) is equivalent to get('id.entityid', r.target_id), and it exists so a template never has to know that. The long form is a trap: id.entityid pairs with r.target_id, meta.identifier with r.target_identifier.value — and mixing them up, or dropping the .value, silently finds nothing.

For a lookup that is not reference-driven — every manufacturer in Germany, say, rather than the one(s) a product links to — use export.custom_entity(key).get or .find with any attribute code, passing the custom entity’s key from Settings → Custom entities:

{{ for m in export.custom_entity('manufacturer').find('country_code', 'DE') }}
{{ m._meta.name }}
{{ end }}

get returns the first match, find returns all of them.

.get attribute, value The first matching record. Also accepts a reference directly: .get(r).
.find attribute, value, limit All matching records, optionally capped.
.key The custom entity’s key, as stored.

For both, the value must match exactly — case sensitive, no partial matching.

The old <attribute>.data.<attribute> fields

Section titled “The old <attribute>.data.<attribute> fields”

Older templates may read a linked record through keys like record.manufacturer.data.support_url. Those keys are gone; replace them with export.load_custom_entities (above).