Skip to content

Commit b8484c5

Browse files
author
Simon Willison
committed
1 parent 776a562 commit b8484c5

File tree

4 files changed

+105
-9
lines changed

4 files changed

+105
-9
lines changed

blog/templatetags/blog_tags.py

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,3 +49,19 @@ def page_href(context, page):
4949
del query_dict['page']
5050
query_dict['page'] = page
5151
return '?' + query_dict.urlencode()
52+
53+
54+
@register.simple_tag(takes_context=True)
55+
def add_qsarg(context, name, value):
56+
query_dict = context['request'].GET.copy()
57+
query_dict.appendlist(name, value)
58+
return '?' + query_dict.urlencode()
59+
60+
61+
@register.simple_tag(takes_context=True)
62+
def remove_qsarg(context, name, value):
63+
query_dict = context['request'].GET.copy()
64+
query_dict.setlist(name, [
65+
v for v in query_dict.getlist(name) if v != value
66+
])
67+
return '?' + query_dict.urlencode()

blog/views.py

Lines changed: 44 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -394,13 +394,13 @@ def search(request):
394394

395395
def search_results(request, q):
396396
start = time.time()
397+
397398
query = SearchQuery(q)
398399
rank_annotation = SearchRank(models.F('search_document'), query)
399-
filter_kwargs = {
400-
'search_document': query
401-
}
402-
tags = request.GET.getlist('tag')
403-
exclude_tags = request.GET.getlist('exclude.tag')
400+
401+
selected_tags = request.GET.getlist('tag')
402+
excluded_tags = request.GET.getlist('exclude.tag')
403+
selected_type = request.GET.get('type', '')
404404

405405
values = ('pk', 'type', 'created', 'rank')
406406

@@ -411,26 +411,56 @@ def make_queryset(klass, type_name):
411411
)
412412
if q:
413413
qs = qs.filter(search_document=query)
414-
for tag in tags:
414+
for tag in selected_tags:
415415
qs = qs.filter(tags__tag=tag)
416-
for exclude_tag in exclude_tags:
416+
for exclude_tag in excluded_tags:
417417
qs = qs.exclude(tags__tag=exclude_tag)
418-
return qs.values(*values).order_by()
418+
return qs.order_by()
419419

420420
# Start with a .none() queryset just so we can union stuff onto it
421421
qs = Entry.objects.annotate(
422422
rank=rank_annotation,
423423
type=models.Value('empty', output_field=models.CharField())
424424
).values(*values).none()
425425

426+
type_counts_raw = {}
427+
tag_counts_raw = {}
428+
426429
for klass, type_name in (
427430
(Entry, 'entry'),
428431
(Blogmark, 'blogmark'),
429432
(Quotation, 'quotation'),
430433
):
431-
qs = qs.union(make_queryset(klass, type_name))
434+
if selected_type and selected_type != type_name:
435+
continue
436+
klass_qs = make_queryset(klass, type_name)
437+
type_count = klass_qs.count()
438+
if type_count:
439+
type_counts_raw[type_name] = type_count
440+
for tag, count in Tag.objects.filter(**{
441+
'%s__in' % type_name: klass_qs
442+
}).annotate(
443+
n=models.Count('tag')
444+
).values_list('tag', 'n'):
445+
tag_counts_raw[tag] = tag_counts_raw.get(tag, 0) + count
446+
qs = qs.union(klass_qs.values(*values))
432447
qs = qs.order_by('-rank')
433448

449+
type_counts = sorted(
450+
[
451+
{'type': type_name, 'n': value}
452+
for type_name, value in type_counts_raw.items()
453+
],
454+
key=lambda t: t['n'], reverse=True
455+
)
456+
tag_counts = sorted(
457+
[
458+
{'tag': tag, 'n': value}
459+
for tag, value in tag_counts_raw.items()
460+
],
461+
key=lambda t: t['n'], reverse=True
462+
)[:40]
463+
434464
paginator = Paginator(qs, 30)
435465
page_number = request.GET.get('page') or '1'
436466
try:
@@ -454,6 +484,11 @@ def make_queryset(klass, type_name):
454484
'total': paginator.count,
455485
'page': page,
456486
'duration': end - start,
487+
'type_counts': type_counts,
488+
'tag_counts': tag_counts,
489+
'selected_tags': selected_tags,
490+
'excluded_tags': excluded_tags,
491+
'selected_type': selected_type,
457492
})
458493

459494

static/css/all.css

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -722,6 +722,17 @@ div.help p {
722722
background-color: #733b96;
723723
color: white;
724724
}
725+
div p.search-selections {
726+
margin-top: 0.5em;
727+
font-size: 0.8em;
728+
}
729+
a.selected-tag {
730+
border: 1px solid #666;
731+
text-decoration: none;
732+
padding: 2px 5px;
733+
background-color: rgba(115, 60, 150, 0.28);
734+
color: black;
735+
}
725736

726737
@media (max-width: 600px) {
727738
div#secondary {

templates/search.html

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,19 @@ <h2>Search{% if q %} for “{{ q }}”{% endif %}</h2>
1212
<input type="search" class="search-input" name="q" value="{{ q }}" style="width: 80%">
1313
<input type="submit" class="search-submit" value="Search">
1414
</form>
15+
16+
{% if selected_tags or selected_type %}
17+
<p class="search-selections">
18+
Filters:
19+
{% if selected_type %}
20+
<a class="selected-tag" href="{% remove_qsarg "type" selected_type %}">Type: {{ selected_type }} <strong>&#x00D7;</strong></a>
21+
{% endif %}
22+
{% for selected_tag in selected_tags %}
23+
<a class="selected-tag" href="{% remove_qsarg "tag" selected_tag %}">{{ selected_tag }} <strong>&#x00D7;</strong></a>
24+
{% endfor %}
25+
</p>
26+
{% endif %}
27+
1528
<br>
1629

1730
{% if total %}
@@ -21,3 +34,24 @@ <h2>Search{% if q %} for “{{ q }}”{% endif %}</h2>
2134
{% endif %}
2235

2336
{% endblock %}
37+
38+
{% block secondary %}
39+
<div class="metabox">
40+
{% if type_counts %}
41+
<h3>Types</h3>
42+
<ul>
43+
{% for t in type_counts %}
44+
<li><a href="{% add_qsarg "type" t.type %}">{{ t.type }}</a> {{ t.n }}</a></li>
45+
{% endfor %}
46+
</ul>
47+
{% endif %}
48+
{% if tag_counts %}
49+
<h3>Tags</h3>
50+
<ul>
51+
{% for t in tag_counts %}
52+
<li><a href="{% add_qsarg "tag" t.tag %}">{{ t.tag }}</a> {{ t.n }}</a></li>
53+
{% endfor %}
54+
</ul>
55+
{% endif %}
56+
</div>
57+
{% endblock %}

0 commit comments

Comments
 (0)