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.
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
.
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 = [
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 = [
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.
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/
@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:
iri
str - The IRI of the resource.Returns:
URIRef
- The returned resource.@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:
node
Node - the node to convertReturns:
_type_
- an appropriate python representation of node
(str, int, boolean etc.)@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:
node
Node - the node to testReturns:
bool
- True
is node
is an IRI, False
if not.@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:
node
Node - the node to testReturns:
bool
- True
is node
is a bnode, False
if not.@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:
node
Node - the node to testReturns:
bool
- True
is node
is a resource, False
if not.@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:
node
Node - the node to testReturns:
bool
- True
is node
is a literal, False
if not.@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:
environment
RDFEnvironment - the RDFEnvironmentsubject
IdentifiedNode - the subject resourcepredicate
str - URI of the propertylanguage
str, optional - language code like en
or fr
. Defaults to None
.unique
bool, optional - Set to True
if only unique results should be returned. Defaults to False
.Returns:
List[rdflib.term.Identifier]
- a list of nodes (URIRef
, Literal
or BNode
)@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:
environment
RDFEnvironment - the RDFEnvironmentsubject
IdentifiedNode - the subject resourcepredicate
str - URI of the propertylanguage
str, optional - language code like en
or fr
. Defaults to None
.Returns:
Identifier
- a URIRef
, Literal
or BNode
@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:
environment
RDFEnvironment - the RDFEnvironmentobject
IdentifiedNode - The object resource.predicate
str - URI of the predicateunique
bool, optional - Set to True
if only unique results should be returned. Defaults to False
.Returns:
List[rdflib.IdentifiedNode]
- a list of subject nodes (URIRef
or BNode
)@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:
environment
RDFEnvironment - the RDFEnvironmentobject
IdentifiedNode - the object resourcepredicate
str - URI of the predicateReturns:
IdentifiedNode
- one instance of IdentifiedNode
, either a URIRef
or a BNode
.@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:
environment
RDFEnvironment - the RDFEnvironmentresourceURI
URIRef - URIRef to drop into the queryquery
str - the actual queryReturns:
rdflib.query.Result
- the query result@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:
environment
RDFEnvironment - the RDFEnvironmentresource
IdentifiedNode - The resource as passed to the filter as the value.as_list
bool - If True, return a list instead of a generator. Defaults to False
.
Useful if we want to know the size of the resultset before iterating through it.Yields:
Generator
- The matching statements@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:
environment
RDFEnvironment - the RDFEnvironmentresource
IdentifiedNode - The resource as passed to the filter as the value.as_list
bool - If True, return a list instead of a generator. Defaults to False
.
Useful if we want to know the size of the resultset before iterating through it.Yields:
Generator
- The matching statements@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:
environment
RDFEnvironment - the RDFEnvironmentresource
IdentifiedNode - the resource for which to find literalsproperties
list - the list of properties to uselanguages
list, optional - list of language codes. Defaults to []
. UNTAGGED
will always be added.return_first
bool, optional - If True
, only return the first literal found. Defaults to False
.default
str, optional - If no matching literals are found, return this. Defaults to None
.Returns:
List[Literal]
- the list of literals found@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:
environment
RDFEnvironment - the RDFEnvironmentresource
IdentifiedNode - the resource for which to find the titlelanguages
list - List of language codes. Defaults to []. UNTAGGED
will always be added.return_first
bool, optional - If True, only return the first title found. Defaults to False.default
str - If no matching titles are found, return this. Defaults to None
.Returns:
list
- the list of titles found@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:
environment
RDFEnvironment - the RDFEnvironmentresource
IdentifiedNode - the resource for which to find the titlelanguages
list, optional - List of languages. Defaults to []. UNTAGGED
will always be added.default
str, optional - If no matching title is found, return this. Defaults to None
.Returns:
Literal
- 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:
environment
RDFEnvironment - the RDFEnvironmentresource
IdentifiedNode - the resource for which to find the descriptionlanguages
list - List of language codes, defaults to []. UNTAGGED
will always be added.return_first
bool, optional - If True, only return the first description found. Defaults to False.default
str - If no matching descriptions are found, return this. Defaults to None
.Returns:
list
- the list of titles found@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:
environment
RDFEnvironment - the RDFEnvironmentresource
IdentifiedNode - the resource for which to find the descriptionlanguages
list, optional - List of language codes, defaults to []. UNTAGGED
will always be added.default
str, optional - If no matching descriptions are found. Defaults to None
.Returns:
Literal
- description@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:
res
: https://berlinonline.github.io/jinja-rdf-demo/example/ducks/DellaDuckenvironment.resource_prefix
: https://berlinonline.github.io/jinja-rdf-demo/example/ducks/environment.site_url
: https://berlinonline.github.io/<a href="{{ res | relative_url }}">{{ res | title_any(languages=['de', 'en']) }}</a>
Output:
<a href="/jinja-rdf-demo/example/ducks/DellaDuck">Della Duck</a>
res
: https://berlinonline.github.io/jinja-rdf-demo/example/ducks/DellaDuckenvironment.resource_prefix
: https://berlinonline.github.io/jinja-rdf-demo/example/ducks/environment.site_url
: http://localhost:8000<a href="{{ res | relative_url }}">{{ res | title_any(languages=['de', 'en']) }}</a>
Output:
<a href="/DellaDuck">Della Duck</a>
res
: https://schema.org/Personenvironment.resource_prefix
: https://berlinonline.github.io/jinja-rdf-demo/example/ducks/environment.site_url
: http://localhost:8000<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:
environment
RDFEnvironment - the RDFEnvironmentresource
IdentifiedNode - the resource for which we want to get the relative URIReturns:
str
- the relative URI