DBT Macros to perform computations - macros

i was trying to write a macros which would be capable of doing various computations and return the final result.
Does macros accept only Block level codes like if or for loops? and not set of individual statements?
please help me with this
Thanks

There's a lot you can do with macros. Most native python operations are possible, albeit in a clunky non-pythonic way (no list comprehensions here).
For example, one can call any method of a python builtin data type:
{% set my_base_models = [] %}
{% for i in graph.nodes.values() %}
{% if i["name"].startswith('base_model') %}
{% do my_list.append(i) %}
{% endif %}
{% endfor %}
Key points:
call {% do <obj>.<method>() %} with any state-altering method (eg. pop() , append(), etc) to execute it
Put results into variables with {% set <var> = <expression> %}
Check out out filters and tests in standard jinja to filter and test mappings (dicts) and lists
Check out the list of dbt specific jinja context variables and methods

Related

Adding Query Tag to DBT Test

I would like to kindly ask you about adding query tags for Snowflake queries to dbt tests.
Is there any way to add query tags for tests to project or schema yaml file? I have tried but I could not find a way for it.
I can modify some packages like dbt_utils.unique_combination_of_columns.combination_of_columns adding query tag statement. But if I use the schema tests in the yaml like below, where and how can I define the query tags for tests?
models:
name: dim_customers
columns:
- name: company
tests:
- not_null
- unique
The default macro in the dbt package for snowflake, in line 3 calls a macro named set_query_tag(), that it's defined here. What you can do is to create a custom set_query_tag() in your dbt project, following this example (taken from here):
{% macro set_query_tag() -%}
{# select some property of the node #}
{% set new_query_tag = model.name %}
{% if new_query_tag %}
{% set original_query_tag = get_current_query_tag() %}
{{ log("Setting query_tag to '" ~ new_query_tag ~ "'. Will reset to '" ~ original_query_tag ~ "' after materialization.") }}
{% do run_query("alter session set query_tag = '{}'".format(new_query_tag)) %}
{{ return(original_query_tag)}}
{% endif %}
{{ return(none)}}
{% endmacro %}
This will override the default macro and tag all your runs with the test name (something like not_null__model_name__column_name). If you only want to do it with tests, you need to add some extra if/else using
node.resource_type == 'test'
Also, check this it seems that this is possible since a few days ago, so it could now work as expected with older versions...

Jinja templates, how to do an import of all macros to follow the DRY principle?

currently I have in many places:
{% from "macros/render_product_materials.html" import render_product_materials %}
{% from "macros/render_citation.html" import render_citation %}
{% from "macros/render_product_packages.html" import render_product_packages %}
{% from "macros/render_icon_explanation_section.html" import render_icon_explanation_section %}
{% from "macros/render_percentage_items.html" import render_percentage_items %}
this list goes on!
then i call the code in the template:
{{render_percentage_items('some args',2,34,55)}}
If I change a function name, I have to change it everywhere, if I add a new function, I have to go and import it each time everywhere
I rather just want to do this, something like this:
{% include 'macros/all_macros.html' %}
then I just put all imports into all_macros.html
But: the imports do not get available in the context
e.g.
{% include 'macros/all_macros.html' %}
{{render_percentage_items('some args',2,34,55)}}
doesn't work. render_percentage_items not found. Now what is the way to do this the jinja way?
Collecting related macros into a single file is a start. Then use import … as rather than include.
{% import 'macros/all_macros.html' as m %}
{{ m.render_percentage_items('some args', 2, 34, 55) }}
So, basically, exactly what SumanKalyan already said.
In many (most?) cases, you won't even need the with context, but you can refer here to decide if maybe you do. As mentioned in the documentaiton, with context disables caching, and usually imported templates only contain macros anyway, which can and should be cached.
Reference.

How to exceed 255 characters limit of product tag in Shopify?

I am looking for a way to exceed the 255 characters limit on product tag in Shopify admin. Please suggest any way of doing so.
In general this does not sound like a good idea. If you are trying to embed extra information per item you might want to look at putting that in a snippet file and then formatting your tag like __extra File1 and then a snippet like the following in your product template:
{% for tag in product.tags %}
{%if tag contains '__extra' %}
{% assign snip = tag | remove_first : '__extra ' %}
{% include snip %}
{% endif %}
{% endfor %}
This would allow you to share large chunks of information per product while not blowing up your tags.
If you adopt this approach then you'll also want to go through your theme and make sure you filter out tags beginning with '__'. e.g.
{% for tag in collection.all_tags %}
{% assign tag_pref = tag | slice:0,2 %}
{% unless tag_pref == '__' %}
... do your tag related layout
{% endunless %}
{% endfor %}

Can I use a filter on all compress blocks except for one?

In my settings for compressor I'm using SlimIt for most of my javascript:
COMPRESS_JS_FILTERS = ['compressor.filters.jsmin.SlimItFilter', ]
Some of my js files shouldn't go through SlimIt though because the file is already minified, or the javascript throws some error when its minified with other files. My template block ends up looking like this:
{# code that I minify #}
{% block compressed_libs %}
{% compress js %}
<script src="/static/js/compress_this.js"></script>
<script src="/static/js/also_compress_this.js"></script>
...
{% endcompress %}
{% endblock %}
{# code that shouldn't minify #}
{% block non-compressible_libs %}
<script src="/static/js/already.min.js"></script>
<script src="/static/js/breaks-everything.js"></script>
{% endblock %}
Can I set different compress filter rules for different blocks/files so that my "non-compressible" files can still be concatenated together by compressor while skipping SlimIt?
As approxiblue said, it doesn't look there's a way to specify which filters to use per compress block in a template (in Compressor 1.5).
I'll update this answer someone comes up with something.
It seems like this could be solved by adding a parameter to the compress template tag to allow it to return a CompressorNode with a flag to skip the filter in base.py hunks()
I'll see what the Compressor community thinks about this, but let me know if you have any ideas.

Customize form_row for different form types

I want to set different form_row layout for different form type. I found in templates block named "choice_widget_collapsed", but it render only select tag with options.
I cannot find where this block is being used. Actually it seems that it is rendered instead of form_widget block. I suppose there is somewhere switch/if structure which checks form type and renders appropriate block, but i dont know where to find this switch, or dont know how to check input type inside form_row block.
I know that block type can be found inside form.vars.block_prefixes array, but this sux, because its position may change in the future as it was already.
So the question is: how can i make form_row display different thing depending on form field type?
You should override the normal block in a theme of yours. This block should work like this:
{% block form_appropriate_block %}
{% spaceless %}
{% if form.vars.widget = 'myIntendedWidgetType' %}
[yourTemplate]
{% else %}
{{ parent() }}
{% endif %}
{% endspaceless %}
{% endblock form_appropriate_block %}
And then, in your template, activate your theme using:
{% form_theme form 'MyBundle:Form:formTheme.html.twig' %}
In this way, your form theme is used only when needed, and if the type is not the one you want, it falls back to the normal behaviour.