Skip to content

Commit b88350f

Browse files
committed
Better sort/sort-desc SQL, avoiding deep nesting - closes #135
1 parent 9517de4 commit b88350f

4 files changed

Lines changed: 55 additions & 4 deletions

File tree

django_sql_dashboard/templates/django_sql_dashboard/widgets/default.html

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -45,8 +45,8 @@
4545
<th alt="{{ column.name }}"
4646
{% if user_can_execute_sql and column.is_unambiguous and not too_long_so_use_post %}
4747
data-count-url="{% url 'django_sql_dashboard-index' %}?sql={% filter sign_sql|urlencode %}select "{{ column.name }}", count(*) as n from ({{ result.sql|safe }}) as results group by "{{ column.name }}" order by n desc{% endfilter %}{{ result.extra_qs }}"
48-
data-sort-asc-url="{% url 'django_sql_dashboard-index' %}?sql={% filter sign_sql|urlencode %}select * from ({{ result.sql|safe }}) as results order by "{{ column.name }}"{% endfilter %}{{ result.extra_qs }}"
49-
data-sort-desc-url="{% url 'django_sql_dashboard-index' %}?sql={% filter sign_sql|urlencode %}select * from ({{ result.sql|safe }}) as results order by "{{ column.name }}" desc{% endfilter %}{{ result.extra_qs }}"
48+
data-sort-asc-url="{% url 'django_sql_dashboard-index' %}?sql={% filter sign_sql|urlencode %}{{ column.sort_sql|safe }}{% endfilter %}{{ result.extra_qs }}"
49+
data-sort-desc-url="{% url 'django_sql_dashboard-index' %}?sql={% filter sign_sql|urlencode %}{{ column.sort_desc_sql|safe }}{% endfilter %}{{ result.extra_qs }}"
5050
data-count-distinct-url="{% url 'django_sql_dashboard-index' %}?sql={% filter sign_sql|urlencode %}select count(distinct "{{ column.name }}") from ({{ result.sql|safe }}) as results{% endfilter %}{{ result.extra_qs }}"
5151
{% endif %}>{{ column.name }}</th>
5252
{% endfor %}

django_sql_dashboard/utils.py

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -105,3 +105,15 @@ def postgresql_reserved_words(connection):
105105
cursor.execute("select word from pg_get_keywords() where catcode = 'R'")
106106
_reserved_words = [row[0] for row in cursor.fetchall()]
107107
return _reserved_words
108+
109+
110+
_sort_re = re.compile('(^.*) order by "[^"]+"( desc)?$', re.DOTALL)
111+
112+
113+
def apply_sort(sql, sort_column, is_desc=False):
114+
match = _sort_re.match(sql)
115+
if match is not None:
116+
sql = match.group(1)
117+
else:
118+
sql = "select * from ({}) as results".format(sql)
119+
return sql + ' order by "{}"{}'.format(sort_column, " desc" if is_desc else "")

django_sql_dashboard/views.py

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222

2323
from .models import Dashboard
2424
from .utils import (
25+
apply_sort,
2526
check_for_base64_upgrade,
2627
displayable_rows,
2728
extract_named_parameters,
@@ -268,7 +269,12 @@ def _dashboard_index(
268269
)
269270
display_rows = displayable_rows(rows[:row_limit])
270271
column_details = [
271-
{"name": column, "is_unambiguous": columns.count(column) == 1}
272+
{
273+
"name": column,
274+
"is_unambiguous": columns.count(column) == 1,
275+
"sort_sql": apply_sort(sql, column),
276+
"sort_desc_sql": apply_sort(sql, column, True),
277+
}
272278
for column in columns
273279
]
274280
query_results.append(

test_project/test_utils.py

Lines changed: 34 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import pytest
22

3-
from django_sql_dashboard.utils import is_valid_base64_json
3+
from django_sql_dashboard.utils import apply_sort, is_valid_base64_json
44

55

66
@pytest.mark.parametrize(
@@ -17,3 +17,36 @@
1717
)
1818
def test_is_valid_base64_json(input, expected):
1919
assert is_valid_base64_json(input) == expected
20+
21+
22+
@pytest.mark.parametrize(
23+
"sql,sort_column,is_desc,expected_sql",
24+
(
25+
(
26+
"select * from foo",
27+
"bar",
28+
False,
29+
'select * from (select * from foo) as results order by "bar"',
30+
),
31+
(
32+
"select * from foo",
33+
"bar",
34+
True,
35+
'select * from (select * from foo) as results order by "bar" desc',
36+
),
37+
(
38+
'select * from (select * from foo) as results order by "bar" desc',
39+
"bar",
40+
False,
41+
'select * from (select * from foo) as results order by "bar"',
42+
),
43+
(
44+
'select * from (select * from foo) as results order by "bar"',
45+
"bar",
46+
True,
47+
'select * from (select * from foo) as results order by "bar" desc',
48+
),
49+
),
50+
)
51+
def test_apply_sort(sql, sort_column, is_desc, expected_sql):
52+
assert apply_sort(sql, sort_column, is_desc) == expected_sql

0 commit comments

Comments
 (0)