Here's how you could write that in jinjasql. I'm writing this out of memory, and it has errors/issues - but I hope you get the idea.
SELECT
{% for metric in userquery.metrics %}
{{ metric.id }} as '{{metric.description}}'
{% endfor %}
FROM {{ userquery.cube }}
WHERE
{% for filter in userquery.filters %}
{{ filter.key }} {{ filter.operator }} {{ filter.value }}
{% endfor %}
GROUP BY
{% for dimension in userquery.dimensions %}
{{ dimension }}
{% endfor %}
This way, your template doesn't have to map to exactly one visualization.Now, another interesting this in the security aspect. You can also pass the logged in user data model, and then append a where clause that restricts the rows based on the logged in user id or his role or whatever else is your application security model.
I'm not sure how much effort it would have saved you, but it does help us a lot - mostly because we don't have to translate between SQL and the way SQL ALchemy / ORM works.