How to create and update a global variable in Jinja? - variables

I'd like to create a global (boolean) variable with Jinja and update it in an if-statement. I try to do this as follows:
{% set variable = true %}
{% if True %}
{% set variable = false %}
{% endif %}
{{ variable }}
I'd like the print to be False, but now it is True.
Should I create a variable to a namespace or is there simpler way? Thanks for your help!

Related

DBT - how can i add model configuration (using a macro on {{this}}) in dbt_project.yml

I want to add node_color to all of my dbt models based on my filename prefix to make it easier to navigate through my dbt documentation :
fact_ => red
base__ => black.
To do so i have a macro that works well :
{% macro get_model_color(model) %}
{% set default_color = 'blue' %}
{% set ns = namespace(model_color=default_color) %}
{% set dict_patterns = {"base__[a-z0-9_]+" : "black", "ref_[a-z0-9_]+" : "yellow", "fact_[a-z0-9_]+" : "red"} %}
{% set re = modules.re %}
{% for pattern, color in dict_patterns.items() %}
{% set is_match = re.match(pattern, model.identifier, re.IGNORECASE) %}
{% if is_match %}
{% set ns.model_color = color %}
{% endif %}
{% endfor %}
{{ return({'node_color': ns.model_color}) }}
{% endmacro %}
And i call it in my model .sql :
{{config(
materialized = 'table',
tags=['daily'],
docs=get_model_color(this),
)}}
This works well but force me to add this line of code in all my models (and in all the new ones).
Is there a way i can define it in my dbt_project.yml to make it available to all my models automatically?
I have tried many things like the config jinja function or this kind of code in dbt_project.yml
+docs:
node_color: "{{ get_model_color(this) }}"
returning Could not render {{ get_model_color(this) }}: 'get_model_color' is undefined
But nothing seems to work
Any idea? Thanks

DBT run model only once

I've created a model to generate a calendar dimension which I only want to run when I explicitly specify to run it.
I tried to use incremental materialisation with nothing in is_incremental() block hoping dbt would do nothing if there was no query to satisfy the temporary view. Unfortunately this didn't work.
Any suggestion or thoughts for how I might achieve this greatly appreciated.
Regards,
Ashley
I've used a tag for this. Let's call this kind of thing a "static" model. In your model:
{{ config(tags=['static']) }}
and then in your production job:
dbt run --exclude tag:static
This doesn't quite achieve what you want, since you have to add the selector at the command line. But it's simple and self-documenting, which is nice.
I think you should be able to hack the incremental materialization to do this. dbt will complain about empty models, but you should be able to return a query with zero records. It'll depend on your RDBMS if this is really much better/faster/cheaper than just running the model, since dbt will still execute a query with the complex merge logic.
{{ config(materialized='incremental') }}
{% if is_incremental() %}
select * from {{ this }} limit 0
{% else %}
-- your model here, e.g.
{{ dbt_utils.date_spine( ... ) }}
{% endif %}
Your last/best option is probably to create a custom materialization that checks for an existing relation and no-ops if it finds one. You could borrow most of the code from the incremental materialization to do this. (You would add this as a macro in your project). Haven't tested this, but to give you an idea:
-- macros/static_materialization.sql
{% materialization static, default -%}
-- relations
{%- set existing_relation = load_cached_relation(this) -%}
{%- set target_relation = this.incorporate(type='table') -%}
{%- set temp_relation = make_temp_relation(target_relation)-%}
{%- set intermediate_relation = make_intermediate_relation(target_relation)-%}
{%- set backup_relation_type = 'table' if existing_relation is none else existing_relation.type -%}
{%- set backup_relation = make_backup_relation(target_relation, backup_relation_type) -%}
-- configs
{%- set unique_key = config.get('unique_key') -%}
{%- set full_refresh_mode = (should_full_refresh() or existing_relation.is_view) -%}
{%- set on_schema_change = incremental_validate_on_schema_change(config.get('on_schema_change'), default='ignore') -%}
-- the temp_ and backup_ relations should not already exist in the database; get_relation
-- will return None in that case. Otherwise, we get a relation that we can drop
-- later, before we try to use this name for the current operation. This has to happen before
-- BEGIN, in a separate transaction
{%- set preexisting_intermediate_relation = load_cached_relation(intermediate_relation)-%}
{%- set preexisting_backup_relation = load_cached_relation(backup_relation) -%}
-- grab current tables grants config for comparision later on
{% set grant_config = config.get('grants') %}
{{ drop_relation_if_exists(preexisting_intermediate_relation) }}
{{ drop_relation_if_exists(preexisting_backup_relation) }}
{{ run_hooks(pre_hooks, inside_transaction=False) }}
-- `BEGIN` happens here:
{{ run_hooks(pre_hooks, inside_transaction=True) }}
{% set to_drop = [] %}
{% if existing_relation is none %}
{% set build_sql = get_create_table_as_sql(False, target_relation, sql) %}
{% elif full_refresh_mode %}
{% set build_sql = get_create_table_as_sql(False, intermediate_relation, sql) %}
{% set need_swap = true %}
{% else %}
{# ----- only changed the code between these comments ----- #}
{# NO-OP. An incremental materialization would do a merge here #}
{% set build_sql = "select 1" %}
{# ----- only changed the code between these comments ----- #}
{% endif %}
{% call statement("main") %}
{{ build_sql }}
{% endcall %}
{% if need_swap %}
{% do adapter.rename_relation(target_relation, backup_relation) %}
{% do adapter.rename_relation(intermediate_relation, target_relation) %}
{% do to_drop.append(backup_relation) %}
{% endif %}
{% set should_revoke = should_revoke(existing_relation, full_refresh_mode) %}
{% do apply_grants(target_relation, grant_config, should_revoke=should_revoke) %}
{% do persist_docs(target_relation, model) %}
{% if existing_relation is none or existing_relation.is_view or should_full_refresh() %}
{% do create_indexes(target_relation) %}
{% endif %}
{{ run_hooks(post_hooks, inside_transaction=True) }}
-- `COMMIT` happens here
{% do adapter.commit() %}
{% for rel in to_drop %}
{% do adapter.drop_relation(rel) %}
{% endfor %}
{{ run_hooks(post_hooks, inside_transaction=False) }}
{{ return({'relations': [target_relation]}) }}
{%- endmaterialization %}
We are working with dbt run --select MODEL_NAME for each model we want to run. So a dbt run in our environment never executes more then one model. By doing so you never run in a situation where you execute a model by accident.

specify variable array index for liquid

My liquid code is
{% assign _product_id = product.id %}
{% assign _product_tag = product.collections[0].title}
{% assign _product_name = product.title %}
{{_product_tag}}
{% assign pagla_array = collections[_product_tag].products %}
{{ pagla_array.first.title }}
Here last line showing nothing. if I use a static index for assigning pagla_array like {% assign pagla_array = collections['Beans'].products %} then it show value. What wrong did I make here?
This line:
{% assign _product_tag = product.collections[0].title}
Is not closed correctly. It should end with %}
In addition you should use handles for the collections, not title.
So it should become:
{% assign _product_tag = product.collections[0].handle %}
....
{% assign pagla_array = collections[_product_tag].products %}

How to use variables in Twig filter 'replace'

Handing over an array from php of form
$repl_arr = array('serach-string1' => 'replace1', ...)
to a Twig template I would like to replace strings in a Twig variable per replace filter similar to this:
{{ block | replace({ repl_arr }) }}
That does not function and neither a variable loop like
{% for key,item in repla_arr %}
{% set var = block | replace({ key : item }) %}
{% endfor %}
does. What is wrong with it? How could it work?
Either you pass the whole array, or you loop the replaces.
But when looping the replaces you need to wrap key and value in parentheses to force interpolation of those
{% set replaces = {
'{site}' : '{stackoverflow}',
'{date}' : "NOW"|date('d-m-Y'),
} %}
{% set haystack = '{site} foobar {site} {date} bar' %}
{{ haystack | replace(replaces) }}
{% set output = haystack %}
{% for key, value in replaces %}
{% set output = output|replace({(key) : (value),}) %}
{% endfor %}
{{ output }}
fiddle

Shopify - Increment or Counter

I'm trying to set up a simple way to increment within for loops in my themes and can't figure out how to get it to work. I'm familiar with two ways to increment:
{% assign variable = 0 %}
{% for .....
{% assign variable = variable | plus: 1 %}
.... endfor %}
and
{% assign variable = 0 %}
{% increment variable %}
However neither of these work. Update: Currently the following block of code will output "0" when it should be "1"
{% assign variable = 0 %}
{% assign variable = variable | plus: 1 %}
{{ variable }}
What am I doing wrong?
What you are doing with the assign should work however there is an easier way:
{{forloop.index0}}
See the docs for the loop object