jinjardf

Table of Contents

jinjardf.rdf_filters

This module defines a number of custom filters for Jinja2. Each filter is defined as a function. When used as a filter in a Jinja template, some of the function’s parameters are already set.

Filter Parameters

If the @pass_environment decorator is used, the Jinja environment that called the template is being passed to the filter function as the first parameter (usually called environment):

@pass_environment
def rdf_property(environment: RDFEnvironment, subject: IdentifiedNode, predicate: str, language: str=None, unique: bool=False) -> List[Identifier]:
...

The parameter after environment (or the first parameter, if the environment is not passed) is the value that the filter is being applied to in the template. The remaining parameters of the filter function are passed by the filter explicitly as parameters.

Lets consider the filter function rdf_get(iri: str). This filter could be used in a template as follows:

{{ 'https://example.com/foo/bar' | rdf_get }}

In this case, ‘https://example.com/foo/bar’ would be passed to rdf_get() as the iri parameter.

The rdf_property() function (see above) would be used as a filter like this:

{{ node | rdf_property(RDFS.label, 'en', true) }}

In this case, the function’s environment parameter was passed by the pass_environment decorator, node from the template is passed as the subject parameter, and RDFS.label, 'en' and true are passed as the function’s remaining three parameters predicate, language and unique.

Examples

The documentation for the filter functions below includes examples of how to use them as filters in a Jinja template. For these examples, we assume the graph loaded by the RDFEnvironment contains the RDF from this dataset: https://berlinonline.github.io/jinja-rdf-demo/example/ducks/

DEFAULT_TITLE_PROPERTIES

DEFAULT_TITLE_PROPERTIES = [
    RDFS.label,
    DCT.title,
    FOAF.name,
    SCHEMA.name,
    SKOS.prefLabel,
]

A list of properties that all mean something like ‘title’ and are used by the title() and title_any() filters.

DEFAULT_DESCRIPTION_PROPERTIES

DEFAULT_DESCRIPTION_PROPERTIES = [
    RDFS.comment,
    DCT.description,
    SCHEMA.description,
]

A list of properties that all mean something like ‘description’ and are used by the description() and description_any() filters.

RDFFilters Objects

class RDFFilters(Extension)

Implementation of a Jinja2 extension that provides various filters for working with RDF data. The filters are based on the rdflib library.

See https://rdflib.readthedocs.io/en/stable/

rdf_get

@staticmethod
def rdf_get(iri: str) -> URIRef

Return an rdflib URIRef with the IRI that was passed as the value. When used as a Jinja filter, the value passed is the iri. This is useful when we want to use use rdflib’s API for URIRefs, or if we want to compare the IRI with another URIRef.

Examples:

The (somewhat contrieved) example is using rdf_get to turn a string into a URIRef object, so what we can use the n3() function on it. (https://rdflib.readthedocs.io/en/stable/apidocs/rdflib.html#rdflib.term.URIRef.n3))

{% set iri_string = 'https://example.com/foo/bar' %}
{% set iri = iri_string | rdf_get %}
{{ iri.n3() }}

Output:

<https://example.com/foo/bar>

Arguments:

Returns:

to_python

@staticmethod
def to_python(node: Node)

Returns an appropriate python datatype for the rdflib type of node, or None if node is None. This is useful if we want to compare the value of a literal with a Jinja (Python) object, such as a String.

Examples:

{% set gender = node | rdf_property_any(SCHEMA.gender) | to_python %}
{% if gender == 'female' %}
    weiblich
{% elif gender == 'male' %}
    männlich
{% else %}
    sonstiges
{% endif %}

Output:

weiblich

Arguments:

Returns:

is_iri

@staticmethod
def is_iri(node: Node) -> bool

Return True if node is an IRI (URI) resource, False if not.

Examples:

{% if node | is_iri %}
    {{ node }} is an IRI.
{% endif %}

Output:

https://berlinonline.github.io/jinja-rdf-demo/example/ducks/DellaDuck is an IRI.

Arguments:

Returns:

is_bnode

@staticmethod
def is_bnode(node: Node) -> bool

Return True if node is a blank node resource, False if not.

Examples:

{% if node | is_bnode %}
    {{ node }} is a Bnode.
{% else %}
    {{ node }} is not a Bnode.
{% endif %}

Output:

https://berlinonline.github.io/jinja-rdf-demo/example/ducks/DellaDuck is not a Bnode.

Arguments:

Returns:

is_resource

@staticmethod
def is_resource(node: Node) -> bool

Return True if node is a resource (either IRI or bnode), False if not.

Examples:

{% if node | is_resource %}
    {{ node }} is a resource.
{% endif %}

Output:

https://example.com/foo/bar is a resource.

Arguments:

Returns:

is_literal

@staticmethod
def is_literal(node: Node) -> bool

Return True if node is a literal, False if not.

Examples:

{% set title = node | title_any(language=['en']) %}
{{ node }} is a literal: {% node | is_literal %}<br/>
'{{ title }}' is a literal: {% title | is_literal %}<br/>

Output:

https://berlinonline.github.io/jinja-rdf-demo/example/ducks/DellaDuck is a literal: False
'Della Duck' is a literal: True

Arguments:

Returns:

rdf_property

@staticmethod
@pass_environment
def rdf_property(environment: RDFEnvironment,
                 subject: IdentifiedNode,
                 predicate: str,
                 language: str = None,
                 unique: bool = False) -> List[Identifier]

Return the objects for the pattern (subject, predicate, OBJ). If an optional language code is provided, the results will be filtered to only contain literals with that language. When used as a Jinja filter, the value passed is the subject.

Examples:

schema:names: {{ node | rdf_property(SCHEMA.name) }}<br/>
Japanese schema:names: {{ node | rdf_property(SCHEMA.name, 'ja') }}<br/>
rdfs:labels: {{ node | rdf_property(RDFS.label) }}<br/>

Output:

schema:names: [ 'Della And', 'Della Duck', 'Ντέλλα Ντακ', 'Della Duck', 'Bella Pato', 'デラ・ダック', … ]
Japanese schema:names: [ 'デラ・ダック' ]
rdfs:labels: []

Arguments:

Returns:

rdf_property_any

@staticmethod
@pass_environment
def rdf_property_any(environment: RDFEnvironment,
                     subject: IdentifiedNode,
                     predicate: str,
                     language: str = None) -> Identifier

Return one arbitrary object for the pattern (subject, predicate, OBJ). If an optional language code is provided, only a literal with that language will be returned. When used as a Jinja filter, the value passed is the subject.

Examples:

any schema:name: {{ node | rdf_property_any(SCHEMA.name) }}<br/>
any Japanese schema:name: {{ node | rdf_property_any(SCHEMA.name, 'ja') }}<br/>
any rdfs:label: {{ node | rdf_property_any(RDFS.label) }}<br/>

Output:

any schema:name: 'Della And'
any Japanese schema:name: 'デラ・ダック'
any rdfs:label: None

Arguments:

Returns:

rdf_inverse_property

@staticmethod
@pass_environment
def rdf_inverse_property(environment: RDFEnvironment,
                         object: IdentifiedNode,
                         predicate: str,
                         unique: bool = False) -> List[IdentifiedNode]

Return the subjects for the pattern (SUBJ, predicate, object). When used as a Jinja filter, the value passed is the object.

Examples:

{# Find Della's children by using `hasParent` in the inverse direction. #}
{% set children =  node | rdf_inverse_property(FAMILY.hasParent) %}
Della's children: 
<ul>
{% for child in children %}
  <li>{{ child }}</li>
{% endfor %}
</ul>

Output:

Della's children:
<ul>
  <li>https://berlinonline.github.io/jinja-rdf-demo/example/ducks/Dewey</li>
  <li>https://berlinonline.github.io/jinja-rdf-demo/example/ducks/Huey</li>
  <li>https://berlinonline.github.io/jinja-rdf-demo/example/ducks/Louie</li>
</ul>

Arguments:

Returns:

rdf_inverse_property_any

@staticmethod
@pass_environment
def rdf_inverse_property_any(environment: RDFEnvironment,
                             object: IdentifiedNode,
                             predicate: str) -> IdentifiedNode

Return one arbitrary subject for the pattern (SUBJ, predicate, object). When used as a Jinja filter, the value passed is the object.

Examples:

{# Pick any of Della's children by using `hasParent` in the inverse direction. #}
{% set child =  node | rdf_inverse_property_any(FAMILY.hasParent) %}
Della's child: {{ child }}

Output:

Della's child: https://berlinonline.github.io/jinja-rdf-demo/example/ducks/Huey

Arguments:

Returns:

sparql_query

@staticmethod
@pass_environment
def sparql_query(environment: RDFEnvironment, resourceURI: URIRef,
                 query: str) -> Result

Run a custom SPARQL query, where each occurrence of ?resourceUri is replaced with the resourceURI parameter. Returns an rdflib.query.Result object. What this actually is depends on the type of query (see https://rdflib.readthedocs.io/en/stable/apidocs/rdflib.html#rdflib.query.Result)..) In the typical case of a SELECT query the result is an iterator of rdflib.query.ResultRow obejcts, where each row represents one result and gives access to the variable bindings as attributes (result.variable) or via [] notation (`result[‘variable’]).

Returns an iterator over the resultset, where each result contains the bindings for the selected variables (in the case of a SELECT query). See https://rdflib.readthedocs.io/en/latest/apidocs/rdflib.html#rdflib.query.Result.

Examples:

{% set sibling_query = '''
SELECT DISTINCT ?sibling
WHERE {
    ?resourceUri family:hasParent ?parent .
    ?sibling family:hasParent ?parent .
    FILTER(?sibling != ?resourceUri)
}
'''%}
{% set results = node | sparql_query(sibling_query) %}
{% if results %}
    <ul>
    {% for result in results %}
        <li>{{ result['sibling'] }}</li>
    {% endfor %}
    </ul>
{% endif %}

Output:

<ul>
    <li>https://berlinonline.github.io/jinja-rdf-demo/example/ducks/DonaldDuck</li>
</ul>

Arguments:

Returns:

statements_as_subject

@staticmethod
@pass_environment
def statements_as_subject(environment: RDFEnvironment,
                          resource: IdentifiedNode,
                          as_list: bool = False) -> Generator

Return all statements/triples in the graph where the current resource as passed to the filter is the subject.

Examples:

Statements with literal objects about {{ node }}:
{% set statements = node | statements_as_subject(as_list=true) %}
{%- if statements | length > 0 %}
    <table>
        <tr>
            <th>Property</th>
            <th>Literal</th>
        </tr>
        {% for s, p, o in statements -%}
            {%- if o | is_literal %}
                <tr>
                    <td>{{ p }}</td>
                    <td>"{{ o }}"</td>
                </tr>
            {%- endif -%}
        {%- endfor %}
    </table>
{% endif %}

Output:

Statements with literal objects about https://berlinonline.github.io/jinja-rdf-demo/example/ducks/:
<table>
    <tr>
        <th>Property</th>
        <th>Literal</th>
    </tr>
    <tr>
        <td>http://purl.org/dc/terms/created</td>
        <td>"2024-12-13"</td>
    </tr>
    <tr>
        <td>http://purl.org/dc/terms/description</td>
        <td>"Excerpt of the family tree of the fictional character Donald Duck. Sources for the family tree are Wikipedia, for names in different languages Wikidata."</td>
    </tr>
    <tr>
        <td>http://purl.org/dc/terms/modified</td>
        <td>"2025-01-21"</td>
    </tr>
    <tr>
        <td>http://purl.org/dc/terms/title</td>
        <td>"Duck Family Tree"</td>
    </tr>
</table>

Arguments:

Yields:

statements_as_object

@staticmethod
@pass_environment
def statements_as_object(environment: RDFEnvironment,
                         resource: IdentifiedNode,
                         as_list: bool = False) -> Generator

Return all statements/triples in the graph where the current resource as passed to the filter is the object.

Examples:

Statements in the graph where {{ node }} is the object:
{% set statements_as_object = node | statements_as_object(as_list=true) %}
{%- if statements | length > 0 %}
    <table>
        <tr>
            <th>Subject</th>
            <th>Property</th>
        </tr>
        {% for s, p, o in statements -%}
            <tr>
                <td>{{ s }}</td>
                <td>{{ p }}</td>
            </tr>
        {%- endfor %}
    </table>

Output:

Statements in the graph where https://berlinonline.github.io/jinja-rdf-demo/example/ducks/DellaDuck is the object:
<table>
    <tr>
        <th>Subject</th>
        <th>Property</th>
    </tr>
    <tr>
        <td>https://berlinonline.github.io/jinja-rdf-demo/example/ducks/Dewey</td>
        <td>https://berlinonline.github.io/jinja-rdf-demo/example/ducks/vocab/hasParent</td>
    </tr>
    <tr>
        <td>https://berlinonline.github.io/jinja-rdf-demo/example/ducks/Huey</td>
        <td>https://berlinonline.github.io/jinja-rdf-demo/example/ducks/vocab/hasParent</td>
    </tr>
    <tr>
        <td>https://berlinonline.github.io/jinja-rdf-demo/example/ducks/Louie</td>
        <td>https://berlinonline.github.io/jinja-rdf-demo/example/ducks/vocab/hasParent</td>
    </tr>
</table>

Arguments:

Yields:

get_text

@staticmethod
@pass_environment
def get_text(environment: RDFEnvironment,
             resource: IdentifiedNode,
             properties: list,
             languages: list = [],
             return_first: bool = False,
             default: str = None) -> List[Literal]

Find all literals connected to resource via any of the properties, for all languages and return them as a list. This is e.g. used to get all titles, or all descriptions of a resource, by passing a list of desired title-properties (rdfs:label, dct:title, schema:name etc.) or description-properties (rdfs:comment, dct:description, schema:description etc.).

Most likely, this filter will not be used directly in a template. Instead, title or description will probably be used with the DEFAULT_TITLE_PROPERTIES and DEFAULT_DESCRIPTION_PROPERTIES.

Arguments:

Returns:

title

@staticmethod
@pass_environment
def title(environment: RDFEnvironment,
          resource: IdentifiedNode,
          languages: list = [],
          return_first: bool = False,
          default: str = None) -> List[Literal]

Find all titles (as defined by in the environment) for all languages specified and return them as a list.

Examples:

{% set names = node | title(languages=['de', 'en', 'fi', 'ko']) %}
<h2>Names in different languages for {{ node }}</h2>
{% if names %}
    <ul>
    {% for name in names %}
        <li>"{{ name }}" ({{ name.language }})</li>
    {% endfor %}
    </ul>
{% endif %}

Output:

<h2>Names in different languages for https://berlinonline.github.io/jinja-rdf-demo/example/ducks/DellaDuck</h2>
<ul>
    <li>"Della Duck" (de)</li>
    <li>"Della Duck" (en)</li>
    <li>"Della Ankka" (fi)</li>
    <!-- there is no Korean name in the data -->
</ul>

Arguments:

Returns:

title_any

@staticmethod
@pass_environment
def title_any(environment: RDFEnvironment,
              resource: IdentifiedNode,
              languages: list = [],
              default: str = None) -> Literal

Like title, but returns the first title found instead of a list of all titles.

Examples:

<h1>{{ node | title_any(languages=['de', 'en'], default='(Unbekannt)') }}</h1>

Output:

<h1>Della Duck</h1>

Arguments:

Returns:

description

@staticmethod
@pass_environment
def description(environment: RDFEnvironment,
                resource: IdentifiedNode,
                languages: list = [],
                return_first: bool = False,
                default: str = None) -> List[Literal]

Find all descriptions (as defined by in the environment) for all languages specified and return them as a list.

Examples:

{% set descriptions = node | description(languages=['de', 'en', 'fi', 'ko'], default='No description found.') %}
<h2>Descriptions in different languages for {{ node }}</h2>
{% if descriptions %}
    <ul>
    {% for description in descriptions %}
        <li>{{ description }}</li>
    {% endfor %}
    </ul>
{% endif %}

Output:

<h2>Descriptions in different languages for https://berlinonline.github.io/jinja-rdf-demo/example/ducks/DellaDuck</h2>
<ul>
    <li>No description found.</li>
</ul>

Arguments:

Returns:

description_any

@staticmethod
@pass_environment
def description_any(environment: RDFEnvironment,
                    resource: IdentifiedNode,
                    languages: list = [],
                    default: str = None) -> Literal

Like description, but return the first description found instead of all descriptions.

Examples:

<p>{{ node | description_any(languages=['de', 'en'], default='No description found.') }}</p>

Output:

<p>Excerpt of the family tree of the fictional character Donald Duck. Sources for the family tree are Wikipedia, for names in different languages Wikidata.</p>
<!-- there was no description in German, so the English one was chosen. -->

Arguments:

Returns:

relative_uri

@staticmethod
@pass_environment
def relative_uri(environment: RDFEnvironment, resource: IdentifiedNode) -> str

Returns the URI of this resource relative to the site URL. This is helpful if you want links that work both on the actual site (at the URL that is part of the resource URI) and on a local development server (at e.g. localhost:8000).

Examples:

<a href="{{ res | relative_url }}">{{ res | title_any(languages=['de', 'en']) }}</a>

Output:

<a href="/jinja-rdf-demo/example/ducks/DellaDuck">Della Duck</a>

<a href="{{ res | relative_url }}">{{ res | title_any(languages=['de', 'en']) }}</a>

Output:

<a href="/DellaDuck">Della Duck</a>

<a href="{{ res | relative_url }}">{{ res | title_any(languages=['de', 'en'], default=res) }}</a>

Output:

<a href="https://schema.org/Person">https://schema.org/Person</a>

Arguments:

Returns: