Skip to content

Commit 53cb872

Browse files
committed
New testproj views to see CSS classes in action
1 parent 2d8bf5d commit 53cb872

File tree

12 files changed

+207
-9
lines changed

12 files changed

+207
-9
lines changed

README.md

Lines changed: 61 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -171,6 +171,65 @@ That's it!
171171

172172
The library provides a few settings that you can define in the Django settings of your project:
173173

174-
- `DEFAULT_SORT_UP`, the HTML character to display the up symbol in the column headers (' ↑' by default).
175-
- `DEFAULT_SORT_DOWN`, the HTML character to display the down symbol in the column headers (' ↓' by default).
174+
### Sort indicators
175+
176+
By default, sort direction is shown using HTML entities (arrows):
177+
178+
- `DEFAULT_SORT_UP`, the HTML character to display the up symbol (' ↑' by default).
179+
- `DEFAULT_SORT_DOWN`, the HTML character to display the down symbol (' ↓' by default).
180+
181+
Alternatively, you can use CSS classes for more flexible styling:
182+
183+
- `SORTING_CSS_CLASS_ASC`, CSS class added to the anchor when sorted ascending (empty by default).
184+
- `SORTING_CSS_CLASS_DESC`, CSS class added to the anchor when sorted descending (empty by default).
185+
186+
Example with CSS classes:
187+
188+
```python
189+
# settings.py
190+
SORTING_CSS_CLASS_ASC = "sorted-asc"
191+
SORTING_CSS_CLASS_DESC = "sorted-desc"
192+
```
193+
194+
This will produce `<a class="sorted-asc" ...>` when sorted ascending, allowing you to style the indicator with CSS:
195+
196+
```css
197+
.sorted-asc::after { content: " \2191"; } /* Up arrow */
198+
.sorted-desc::after { content: " \2193"; } /* Down arrow */
199+
```
200+
201+
### Error handling
202+
176203
- `SORTING_INVALID_FIELD_RAISES_404`, if true, a 404 response will be returned on invalid use of query parameters (false by default).
204+
205+
## Default Sort Direction
206+
207+
By default, clicking a column header sorts ascending first. You can change this per-column to sort descending on first click (useful for date columns where you typically want most recent first):
208+
209+
Django template:
210+
```html
211+
{% anchor created_date _("Created") "desc" %}
212+
```
213+
214+
Jinja2 template:
215+
```html
216+
{{ sorting_anchor(request, "created_date", "Created", "desc") }}
217+
```
218+
219+
## Performance Considerations
220+
221+
The library uses Django ORM's `order_by()` for database fields, which is efficient. However, when sorting by model properties or computed attributes (not database fields), it falls back to Python sorting which loads all objects into memory.
222+
223+
For large querysets, ensure you're sorting by database fields only. You can check if a field will use Python sorting:
224+
225+
```python
226+
from webstack_django_sorting.common import need_python_sorting
227+
228+
# Returns True if Python sorting will be used (slower)
229+
need_python_sorting(queryset, "my_property")
230+
```
231+
232+
If you must sort by a computed value on large datasets, consider:
233+
- Adding a database field to store the computed value
234+
- Using database-level annotations
235+
- Limiting the queryset size before sorting

src/testproj/testproj/settings.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -112,3 +112,8 @@
112112
STATICFILES_DIRS = (BASE_DIR / "testproj/static",)
113113

114114
DEFAULT_AUTO_FIELD = "django.db.models.AutoField"
115+
116+
# Django-sorting CSS classes configuration
117+
# When set, these CSS classes are added to the anchor tags instead of HTML entities
118+
SORTING_CSS_CLASS_ASC = "sorted-asc"
119+
SORTING_CSS_CLASS_DESC = "sorted-desc"

src/testproj/testproj/static/css/style.css

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,22 @@ thead > tr > th > a {
1818
thead > tr > th > a:visited {
1919
color: inherit;
2020
}
21+
22+
/* Sorting CSS classes - demonstrates using CSS instead of HTML entities */
23+
thead > tr > th > a.sorted-asc {
24+
font-weight: bold;
25+
color: #e91e63; /* Pink */
26+
background-color: #fce4ec;
27+
padding: 2px 6px;
28+
border-radius: 4px;
29+
}
30+
thead > tr > th > a.sorted-desc {
31+
font-weight: bold;
32+
color: #9c27b0; /* Purple */
33+
background-color: #f3e5f5;
34+
padding: 2px 6px;
35+
border-radius: 4px;
36+
}
2137
tr > th,
2238
tr > td {
2339
padding: 8px;
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
<!DOCTYPE html>
2+
<html lang="en">
3+
<head>
4+
<title>CSS Classes Example</title>
5+
<link rel="stylesheet" href="{{ static("css/style.css") }}" />
6+
</head>
7+
<body>
8+
<h2>Sorting with CSS Classes (Jinja2)</h2>
9+
<p>
10+
This example demonstrates using CSS classes for sort indicators instead of HTML entities.
11+
When a column is sorted, the anchor tag receives a CSS class (<code>sorted-asc</code> or <code>sorted-desc</code>)
12+
which can be styled with CSS pseudo-elements.
13+
</p>
14+
<p>
15+
<strong>Configuration:</strong> Set <code>SORTING_CSS_CLASS_ASC</code> and <code>SORTING_CSS_CLASS_DESC</code>
16+
in your Django settings.
17+
</p>
18+
<table>
19+
<thead>
20+
<tr>
21+
<th>{{ sorting_anchor(request, "id", "File ID") }}</th>
22+
<th>{{ sorting_anchor(request, "filename", "Filename") }}</th>
23+
<th>{{ sorting_anchor(request, "created_on", "Date") }}</th>
24+
<th>{{ sorting_anchor(request, "size", "Size") }}</th>
25+
<th>{{ sorting_anchor(request, "order", "Order") }}</th>
26+
<th>{{ sorting_anchor(request, "is_secret", "Secret?") }}</th>
27+
</tr>
28+
</thead>
29+
<tbody>
30+
{% for secret_file in sort_queryset(request, secret_files) %}
31+
<tr>
32+
<td>{{ secret_file.id }}</td>
33+
<td>{{ secret_file.filename }}</td>
34+
<td>{{ secret_file.size }}</td>
35+
<td>{{ secret_file.created_on }}</td>
36+
<td>{{ secret_file.order }}</td>
37+
<td>{{ secret_file.is_secret|yesno() }}</td>
38+
</tr>
39+
{% endfor %}
40+
</tbody>
41+
</table>
42+
<p><a href="/">Back to home</a></p>
43+
</body>
44+
</html>

src/testproj/testproj/testapp/templates/home.html

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,9 @@ <h2>Test views with Django template</h2>
2020
<li>
2121
<a href="{% url 'nulls_last' %}">Nulls last</a>
2222
</li>
23+
<li>
24+
<a href="{% url 'css_classes' %}">CSS classes for sorting indicators</a>
25+
</li>
2326
</ul>
2427
<h2>Test views with Jinja template</h2>
2528
<ul>
@@ -35,6 +38,9 @@ <h2>Test views with Jinja template</h2>
3538
<li>
3639
<a href="{% url 'jinja_nulls_last' %}">Nulls last</a>
3740
</li>
41+
<li>
42+
<a href="{% url 'jinja_css_classes' %}">CSS classes for sorting indicators</a>
43+
</li>
3844
</ul>
3945
</body>
4046
</html>
Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
{% load static sorting_tags %}
2+
<!DOCTYPE html>
3+
<html lang="en">
4+
<head>
5+
<title>CSS Classes Example</title>
6+
<link rel="stylesheet" href="{% static 'css/style.css' %}" />
7+
</head>
8+
<body>
9+
<h2>Sorting with CSS Classes (Django)</h2>
10+
<p>
11+
This example demonstrates using CSS classes for sort indicators instead of HTML entities.
12+
When a column is sorted, the anchor tag receives a CSS class (<code>sorted-asc</code> or <code>sorted-desc</code>)
13+
which can be styled with CSS pseudo-elements.
14+
</p>
15+
<p>
16+
<strong>Configuration:</strong> Set <code>SORTING_CSS_CLASS_ASC</code> and <code>SORTING_CSS_CLASS_DESC</code>
17+
in your Django settings.
18+
</p>
19+
{% autosort secret_files %}
20+
<table>
21+
<thead>
22+
<tr>
23+
<th>{% anchor id "ID" %}</th>
24+
<th>{% anchor filename "Filename" %}</th>
25+
<th>{% anchor created_on "Date" %}</th>
26+
<th>{% anchor size "Size" %}</th>
27+
<th>{% anchor order "Order" %}</th>
28+
<th>{% anchor is_secret "Secret?" %}</th>
29+
</tr>
30+
</thead>
31+
<tbody>
32+
{% for secret_file in secret_files %}
33+
<tr>
34+
<td>{{ secret_file.id }}</td>
35+
<td>{{ secret_file.filename }}</td>
36+
<td>{{ secret_file.created_on }}</td>
37+
<td>{{ secret_file.size }}</td>
38+
<td>{{ secret_file.order }}</td>
39+
<td>{{ secret_file.is_secret|yesno }}</td>
40+
</tr>
41+
{% endfor %}
42+
</tbody>
43+
</table>
44+
<p><a href="{% url 'home' %}">Back to home</a></p>
45+
</body>
46+
</html>

src/testproj/testproj/testapp/views.py

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,3 +57,19 @@ def jinja_secret_list_nulls_last(request):
5757
"secret_list_nulls_last.jinja2",
5858
{"secret_files": models.SecretFile.objects.all()},
5959
)
60+
61+
62+
def secret_list_css_classes(request):
63+
return render(
64+
request,
65+
"secret_list_css_classes.html",
66+
{"secret_files": models.SecretFile.objects.all()},
67+
)
68+
69+
70+
def jinja_secret_list_css_classes(request):
71+
return render(
72+
request,
73+
"secret_list_css_classes.jinja2",
74+
{"secret_files": models.SecretFile.objects.all()},
75+
)

src/testproj/testproj/urls.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,5 +25,11 @@
2525
views.jinja_secret_list_nulls_last,
2626
name="jinja_nulls_last",
2727
),
28+
path("css-classes", views.secret_list_css_classes, name="css_classes"),
29+
path(
30+
"jinja/css-classes",
31+
views.jinja_secret_list_css_classes,
32+
name="jinja_css_classes",
33+
),
2834
path("admin/", admin.site.urls),
2935
]

src/testproj/uv.lock

Lines changed: 2 additions & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/webstack_django_sorting/common.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,9 @@
22
Common to Django tags (sorting_tags) and Jinja2 globals (jinja2_globals)
33
"""
44

5+
from collections.abc import Sequence
56
from operator import attrgetter
6-
from typing import Any, Literal, Sequence
7+
from typing import Any, Literal
78

89
from django.db.models import F, QuerySet
910
from django.http import Http404, HttpRequest

0 commit comments

Comments
 (0)