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/latest/apidocs/rdflib.query/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.query/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@staticmethod
def markdown_filter(text: str) -> str
Filter for generating HTML output from markdown input. The output has already been marked as safe Markup, so things like element brackets won’t be escaped by Jinja.
Arguments:
text str - the markdown inputReturns:
str - the html output