Using variables in get_columns_in_relation dbt function - jinja2

I'm fairly new to macros and variables in dbt so apologize if this is really straightforward but I'm attempting the below but it's not recognizing the {{customer_dataset}}.{{table_name}} part
{%- set table_name = 'orders' -%}
{%- set customer_dataset = customer.id -%}
{%- set columns = adapter.get_columns_in_relation(`{{customer_dataset}}.{{table_name}}`) -%}
How do I do this properly if I want to dynamically loop through a bunch of tables for the get_columns_in_relation function?

First off, don't nest your curlies.
Second, get_columns_in_relation expects a Relation, not a string. A Relation is the thing returned by {{ ref() }} or {{ source() }}, so it's easiest to get a Relation from a model name, not a database identifier. If you want to use a table in your database that isn't a dbt model, create a source for it first.
In short, your code should probably look like this:
{%- set columns = adapter.get_columns_in_relation(source(customer_dataset, table_name)) -%}

Related

How can I have a global custom macro use context implicitly?

I want to add a custom global macro to Airflow, I found this answer that outlines how this can be done. But I'm wondering if I can get the context implicitly (just like the built-in macros) e.g:
def ds_ny(**context):
"""
Custom macro that returns ds in NY timezone
"""
return context.get("execution_date").in_timezone("America/New_York").format("%Y-%m-%d")
Usage:
select * from table where some_date = '{{ macros.ds_ny }}'
Currently I have to pass a parameter to the macro for it to render {{ macros.ds_ny(execution_date) }}

Django reverse db lookup for multiple objects

Let's say I have a model called TicketSection and TicketSubmission the models look like this: (simplified)
class TicketSection(models.Model):
title = models.CharField(max_length="35")
weight = models.IntegerField()
class TicketSubmission(models.Model):
ticket_section = models.ForeignKey('myapp.TicketSection')
cost = models.IntegerField()
submiter = models.ForeignKey('myapp.User')
submiting_account = models.ForeignKey('myapp.Account')
Now I want to filter TicketSections with it's TicketSubmissions. So I do:
ticket_sections = TicketSection.objects.filter(weight__gte=50).annotate(some_additional_logic)
# Generated SQL Query: SELECT * FROM myapp_ticketsection
WHERE weight >= 50
I know you can select reverse objects by
ticket_sections[0].ticketsubmission_set.filter(again_some_logic).annotate(logic)
# Generated SQL Query: SELECT * FROM myapp_ticketsubmission
WHERE ticket_section = [id of ticket section]
But how do I do it for all ticket sections and how do I use it in template? Should I do:
for ticket_sec in ticket_sections:
ticket_sec.submissions = ticket_sec.ticketsubmission_set.filter(again_some_logic).annotate(logic)
and then in template
{% for ticket_sec in ticket_section %}
{% for submission in ticket_sec.submissions %}
{{ submission.cost }}
^ But that doesn't seem like the right way for me. So how should I reverse lookup for multiple objects to minimalize database hits?
I'm using Django 2.2.5 with MySQL database.

Check variable conditions in template [or,and]

I need to make if statement in html template.
Working:
{% if field == 'var_1' or field == 'var_2' %}
but i can't compare rendered variable with None or ' '
{% if field == 'var_1' or field == 'var_2' and 'var_3' != '' %}
last statement is not working.
var_3 in mysql is NULL or contains varchar(200)
What is proper solution to handle this?
You may looking for the isnull() function.
This function returns True if passed a value set to NULL
.....AND NOT IsNull(var3) AND....

How do I merge two queries in django and select distinct from two unrelated tables

I'm working in Django (1.8) with a MySQL backend. I have two unrelated tables that I want to query. The results are a concatenation of two columns from each table. I want to merge the results, sort them and select distinct. For the sake of example let's assume I have tableA and tableB both of which have first_name and last_name columns. Each cloumn heading has a reference to the table in it (e.g. first_name_A). Currently I'm running a query on each table and using sorted(chain(queryA, queryB)) to put them together. The problem comes when I try to reference the result in a for loop in the template.
Codewise I have this:
views.py
queryA = tableA.objects.order_by('first_name_A', 'last_name_A').values('first_name_A', 'last_name_A').distinct()
queryB = tableB.objects.order_by('first_name_B', 'last_name_B').values('first_name_B', 'last_name_B').distinct()
query = sorted(chain(queryA, queryB))
return render_to_response('results.html', {'queries': query})
html
<div>
{% for query in queries %}
<tr>
<td>{{ query.first_name_A }} {{ query.last_name_A }}</td>
</tr>
{% endfor %}
</div>
The above html will obviously only return the first and last names from tableA. These are sorted and distinct but no info from tableB is included. As the columns from each table have different headings and no properties alike other than the values (rather than the keys) how can I merge them, sort and select distinct combinations? Or should I just rename the columns to remove the table reference?
You can use the extra() modifier to assign alternative names.
queryA = tableA.extra(select={'alias' : 'first_name_A'}).order_by('first_name_A').values('alias').distinct()
queryB = tableB.extra(select={'alias' : 'first_name_B'}).order_by('first_name_B').values('alias').distinct()
The django documentation discourages the use of extra but there doesn't seem to be an easy way to create aliases in a queryset. This ticket suggest that this may change in the future. The last comment describes an alternative method with F expressions and annotate which should work with django v1.8
I've solved it but I'm not sure it's the most pythonic (nor correct) way. If anyone has any better suggestions then please let me know.
views.py
queryA = tableA.objects.order_by('first_name_A','last_name_A').values('first_name_A', 'last_name_A').distinct()
queryB = tableB.objects.order_by('first_name_B','last_name_B').values('first_name_B', 'last_name_B').distinct()
chain_query = sorted(chain(queryA, queryB))
valueList = []
for q in chain_query:
wholeName = ' '.join(q.values())
valueList.append(wholeName)
query = sorted(set(valueList))
return render_to_response('results.html', {'queries': query})
html
{% for query in queries %}
<tr>
<td>{{ query }}</td>
</tr>
{% endfor %}

Trying to make a Django / Mezzanine "Page last updated by on date"

I'm trying to make a "page last updated by (username)" on (date)" line at the bottom of my mezzanine site page. I've successfully got the date part with the syntax:
{{ page.updated|date:"M d, Y" }}
but the username seems more complicated. I have a nice list in django_admin_log containing action_time, user_id and object_id and I can access & display object_id from within my template using {{ page.id }}.
The username is in auth_user and in days of old I would have put in some SQL with a nice table join on user_id to retrieve this information and display it.
I can't seem to get the syntax right - this kind of thing looked promising:
page.objects.raw("select id, title from page")
but no matter how I try to massage it into my base.html with {{ xxx }} or {% xxx %}, I always seem to get an error.
Thanks for any pointers
I've found a way to do this:
I created a custom tag, saved in /appname/templatetags/appname_tags.py, code as follows:
from django import template
from django.db import connection
register = template.Library()
#register.inclusion_tag(‘appname/_updated_by.html')
def updated_by(pageid):
cursor = connection.cursor()
cursor.execute('select user_id from django_admin_log where object_id = %s order by id desc limit 1', [pageid])
userid_set = cursor.fetchone()
userid = userid_set[0]
cursor.execute('select first_name, last_name from auth_user where id = %s', [userid])
username = cursor.fetchone()
return {'first_name': username[0],'last_name': username[1]}
This tag gets rendered via /appname/templates/appname/_updated_by.html, which simply contains:
{{ first_name }} {{ last_name }}
The whole thing then gets called from base.html via this code:
{% load appname_tags %}
And then add at the appropriate point:
Page Last Updated: {{ page.updated|date:"d/n/Y" }} by {% updated_by page.id %}
Hope this helps somebody, I'd be interested to hear if there's an easier / more elegant way to do this!
You should use the LogEntry model[1].
I would probably make a custom context processor[2] that get's the request's
Page, finds the newest LogEntry for the Page instance and adds updated_on
and updated_by context variables.
[1] https://github.com/django/django/blob/master/django/contrib/admin/models.py#L27
[2] https://docs.djangoproject.com/en/dev/ref/templates/api/#writing-your-own-context-processors