Skip to content

Commit

Permalink
feat: add a where_in filter for Jinja2 (#19574)
Browse files Browse the repository at this point in the history
  • Loading branch information
betodealmeida authored Apr 7, 2022
1 parent 6d5771a commit 83c3779
Show file tree
Hide file tree
Showing 3 changed files with 53 additions and 2 deletions.
6 changes: 4 additions & 2 deletions docs/docs/installation/sql-templating.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -206,10 +206,12 @@ Here's a concrete example:
SELECT action, count(*) as times
FROM logs
WHERE
action in ({{ "'" + "','".join(filter_values('action_type')) + "'" }})
action in {{ filter_values('action_type')|where_in }}
GROUP BY action
```

There `where_in` filter converts the list of values from `filter_values('action_type')` into a string suitable for an `IN` expression.

**Filters for a Specific Column**

The `{{ get_filters() }}` macro returns the filters applied to a given column. In addition to
Expand Down Expand Up @@ -243,7 +245,7 @@ Here's a concrete example:
{%- if filter.get('op') == 'IN' -%}
AND
full_name IN ( {{ "'" + "', '".join(filter.get('val')) + "'" }} )
full_name IN {{ filter.get('val')|where_in }}
{%- endif -%}
{%- if filter.get('op') == 'LIKE' -%}
Expand Down
22 changes: 22 additions & 0 deletions superset/jinja_context.py
Original file line number Diff line number Diff line change
Expand Up @@ -401,6 +401,25 @@ def validate_template_context(
return validate_context_types(context)


def where_in(values: List[Any], mark: str = "'") -> str:
"""
Given a list of values, build a parenthesis list suitable for an IN expression.
>>> where_in([1, "b", 3])
(1, 'b', 3)
"""

def quote(value: Any) -> str:
if isinstance(value, str):
value = value.replace(mark, mark * 2)
return f"{mark}{value}{mark}"
return str(value)

joined_values = ", ".join(quote(value) for value in values)
return f"({joined_values})"


class BaseTemplateProcessor:
"""
Base class for database-specific jinja context
Expand Down Expand Up @@ -433,6 +452,9 @@ def __init__(
self._env = SandboxedEnvironment(undefined=DebugUndefined)
self.set_context(**kwargs)

# custom filters
self._env.filters["where_in"] = where_in

def set_context(self, **kwargs: Any) -> None:
self._context.update(kwargs)
self._context.update(context_addons())
Expand Down
27 changes: 27 additions & 0 deletions tests/unit_tests/jinja_context_test.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
# Licensed to the Apache Software Foundation (ASF) under one
# or more contributor license agreements. See the NOTICE file
# distributed with this work for additional information
# regarding copyright ownership. The ASF licenses this file
# to you under the Apache License, Version 2.0 (the
# "License"); you may not use this file except in compliance
# with the License. You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing,
# software distributed under the License is distributed on an
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
# KIND, either express or implied. See the License for the
# specific language governing permissions and limitations
# under the License.

from superset.jinja_context import where_in


def test_where_in() -> None:
"""
Test the ``where_in`` Jinja2 filter.
"""
assert where_in([1, "b", 3]) == "(1, 'b', 3)"
assert where_in([1, "b", 3], '"') == '(1, "b", 3)'
assert where_in(["O'Malley's"]) == "('O''Malley''s')"

0 comments on commit 83c3779

Please sign in to comment.