Multi-valued fields

Use set[T] when a predicate may have multiple objects (for example several tags or duplicate foaf:nick literals without an RDF list). TripleModel emits one triple per value and collects all objects on import. T must be a scalar type; set[TripleModel] is not supported — use a single nested field instead.

For ordered rdf:List values, use list[T] — see RDF lists and language-tagged literals.

Sets (unordered, unique)

tag: set[str] = rdf_field("http://example.org/tag", default_factory=set)
person = Person(slug="a", name="A", tag={"python", "rdf"})

On import, duplicate objects in the graph collapse to one set member. Export order is not guaranteed.

  • None elements are skipped on export.

  • Empty set set() exports no triples for that predicate.

URI references (set / list)

set[ResourceRef] / list[ResourceRef]

Multiple object IRIs on one predicate (unordered set or ordered rdf:List):

from triplemodel import ResourceRef, TripleModel, rdf_field

refs: set[ResourceRef] = rdf_field("http://example.org/mentions", default_factory=set)

ref_field collections

Link to related TripleModel classes without embedding each resource (URI-only objects in the graph):

from triplemodel import TripleModel, ref_field

tags: list[Tag] = ref_field("http://example.org/tagged", model=Tag, default_factory=list)

Use list[SomeModel] with ref_field for several URI links on one predicate (not an RDF list — one triple per member). Use set[ResourceRef] or list[ResourceRef] for bare IRIs (list uses an rdf:List). Pydantic model instances are not hashable, so set[TripleModel] with ref_field is rejected at class definition.

Import hydrates each object URI into a Tag instance. Export writes one triple per member (subject, predicate, object IRI). Use hydrate_refs(instances, graph, "tags") to batch-load linked models and reuse one Python instance per shared URI.

list[TripleModel] / set[TripleModel] without ref_field remain rejected (use a single nested embed field instead).

set[TypedLiteral] — per-object XSD datatypes

Use set[TypedLiteral] (or list[TypedLiteral] for an ordered rdf:List) when several objects on one predicate may each carry a different ^^datatype IRI. This differs from set[int] with literal_datatype=, which forces the same XSD type on every object.

from triplemodel import TypedLiteral, TripleModel, rdf_field
from triplemodel.store.namespaces import XSD

class Measured(TripleModel):
    class Rdf:
        namespace = "http://example.org/"
        id_field = "slug"

    slug: str
    amount: set[TypedLiteral] = rdf_field(
        "http://example.org/amount", default_factory=set
    )

m = Measured(
    slug="m1",
    amount={
        TypedLiteral("1", str(XSD.integer.value)),
        TypedLiteral("1.0", str(XSD.decimal.value)),
    },
)

Import keeps both literals (same lexical form, different datatypes). For set[TypedLiteral], duplicate identical (value, datatype) pairs respect on_duplicate on from_graph. For list[TypedLiteral] (rdf:List), duplicate entries in the list are preserved (no set-style deduplication). For a single scalar with an unknown datatype, use OpaqueLiteral instead.

Scalars vs collections

Field shape

Multiple objects in graph

str, int, nested model, …

First only; on_duplicate applies

set[T]

All objects imported as a set

list[T]

RDF list (rdf:first / rdf:rest); multiple list heads → first only — see guide 09

Sync and cleared fields

When a field is cleared (None, empty set(), or empty list), sync_to_graph(..., mode="replace"|"patch") removes owned triples for that predicate. See Updating graphs.

For ordered rdf:List values, use list[T] instead — see RDF lists and language-tagged literals.