Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

docs: jinja #15019

Merged
merged 6 commits into from Jun 9, 2021
Merged
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
141 changes: 141 additions & 0 deletions docs/src/pages/docs/installation/sql_templating.mdx
Expand Up @@ -87,3 +87,144 @@ FEATURE_FLAGS = {

The available validators and names can be found in
[sql_validators](https://github.com/apache/superset/tree/master/superset/sql_validators).

### Available Macros

In this section, we'll walkthrough the pre-defined Jinja macros in Superset.

**Current Username**

The `{{ current_username() }}` macro returns the username of the currently logged in user.

**Current User ID**

The `{{ current_uer_id()}}` macro returns the user_id of the currently logged in user.
rusackas marked this conversation as resolved.
Show resolved Hide resolved

**Custom URL Parameters**

The `{{ url_param('custom_variable') }}` macro lets you define arbitrary URL
parameters and reference them in your SQL code.

Here's a concrete example:

- You write the following query in SQL Lab:

```
SELECT count(*)
FROM ORDERS
WHERE country_code = '{{ url_param('countrycode') }}'
```

- You're hosting Superset at the domain www.example.com and you send your
coworker in Spain the following SQL Lab URL `www.example.com/superset/sqllab?countrycode=ES`
and your coworker in the USA the following SQL Lab URL `www.example.com/superset/sqllab?countrycode=US`
- For your coworker in Spain, the SQL Lab query will be rendered as:

```
SELECT count(*)
FROM ORDERS
WHERE country_code = 'ES'
```

- For your coworker in the USA, the SQL Lab query will be rendered as:

```
SELECT count(*)
FROM ORDERS
WHERE country_code = 'US'
```

**Skipping Cache**

If you have enabled caching, you will need to chain the `{{ cache_key_wrapper() }}`
function to avoid retrieving the
same current_username or current_user_id for different users.

Here's an example:

- Skip cache when retrieving current user_id: `{{ cache_key_wrapper(current_user_id()) }}`
- Skip cache when retrieving current username: `{{ cache_key_wrapper(current_username()) }}`
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

cache_key_wrapper is nowadays applied by default to current_user_id and current_username (it can be disabled by calling them with add_to_cache_keys=False). See here for more context:

def current_user_id(self, add_to_cache_keys: bool = True) -> Optional[int]:
"""
Return the user ID of the user who is currently logged in.
:param add_to_cache_keys: Whether the value should be included in the cache key
:returns: The user ID
"""
if hasattr(g, "user") and g.user:
if add_to_cache_keys:
self.cache_key_wrapper(g.user.get_id())
return g.user.get_id()
return None
def current_username(self, add_to_cache_keys: bool = True) -> Optional[str]:
"""
Return the username of the user who is currently logged in.
:param add_to_cache_keys: Whether the value should be included in the cache key
:returns: The username
"""
if g.user and hasattr(g.user, "username"):
if add_to_cache_keys:
self.cache_key_wrapper(g.user.username)
return g.user.username
return None

cache_key_wrapper is therefore only needed when it's necessary to wrap your own custom function return values into the cache key. See here for more context:

def cache_key_wrapper(self, key: Any) -> Any:
"""
Adds values to a list that is added to the query object used for calculating a
cache key.
This is needed if the following applies:
- Caching is enabled
- The query is dynamically generated using a jinja template
- A `JINJA_CONTEXT_ADDONS` or similar is used as a filter in the query
:param key: Any value that should be considered when calculating the cache key
:return: the original value ``key`` passed to the function
"""
if self.extra_cache_keys is not None:
self.extra_cache_keys.append(key)
return key


**Filter Values**

You can retrieve the value for a specific filter as a list using `{{ filter_values() }}`.

This is useful if:
- you want to use a filter component to filter a query where the name of filter component column doesn't match the one in the select statement
- you want to have the ability for filter inside the main query for speed purposes

Here's a concrete example:

```
SELECT action, count(*) as times
FROM logs
WHERE
action in ({{ "'" + "','".join(filter_values('action_type')) + "'" }})
GROUP BY action
```

You can use thisfeature to reference the start & end datetimes from a time filter using:

- `{{ from_dttm }}`: start datetime value
- `{{ to_dttm }}`: end datetime value

**Filters for a Specific Column**

The `{{ get_filters() }}` macro returns the filters applied to a given column. In addition to
returning the values (similar to how `filter_values()` does), the `get_filters()` macro
returns the operator specified in the Explore UI.

This is useful if:
- you want to handle more than the IN operator in your SQL clause
- you want to handle generating custom SQL conditions for a filter
- you want to have the ability to filter inside the main query for speed purposes

Here's a concrete example:

```
WITH RECURSIVE
superiors(employee_id, manager_id, full_name, level, lineage) AS (
SELECT
employee_id,
manager_id,
full_name,
1 as level,
employee_id as lineage
FROM
employees
WHERE
1=1

{# Render a blank line #}
{%- for filter in get_filters('full_name', remove_filter=True) -%}

{%- if filter.get('op') == 'IN' -%}
AND
full_name IN ( {{ "'" + "', '".join(filter.get('val')) + "'" }} )
{%- endif -%}

{%- if filter.get('op') == 'LIKE' -%}
AND
full_name LIKE {{ "'" + filter.get('val') + "'" }}
{%- endif -%}

{%- endfor -%}
UNION ALL
SELECT
e.employee_id,
e.manager_id,
e.full_name,
s.level + 1 as level,
s.lineage
FROM
employees e,
superiors s
WHERE s.manager_id = e.employee_id
)

SELECT
employee_id, manager_id, full_name, level, lineage
FROM
superiors
order by lineage, level
```