home / tils / til

Menu
  • GraphQL API

til: django_postgresql-full-text-search-admin.md

This data as json

path topic title url body html shot created created_utc updated updated_utc shot_hash slug
django_postgresql-full-text-search-admin.md django PostgreSQL full-text search in the Django Admin https://github.com/simonw/til/blob/main/django/postgresql-full-text-search-admin.md Django 3.1 introduces PostgreSQL `search_type="websearch"` - which gives you search with advanced operators like `"phrase search" -excluding`. James Turk [wrote about this here](https://jamesturk.net/posts/websearch-in-django-31/), and it's also in [my weeknotes](https://simonwillison.net/2020/Jul/23/datasette-copyable-datasette-insert-api/). I decided to add it to my Django Admin interface. It was _really easy_ using the `get_search_results()` model admin method, [documented here](https://docs.djangoproject.com/en/3.0/ref/contrib/admin/#django.contrib.admin.ModelAdmin.get_search_results). My models already have a `search_document` full-text search column, as described in [Implementing faceted search with Django and PostgreSQL](https://simonwillison.net/2017/Oct/5/django-postgresql-faceted-search/). So all I needed to add to my `ModelAdmin` subclasses was this: ```python def get_search_results(self, request, queryset, search_term): if not search_term: return super().get_search_results( request, queryset, search_term ) query = SearchQuery(search_term, search_type="websearch") rank = SearchRank(F("search_document"), query) queryset = ( queryset .annotate(rank=rank) .filter(search_document=query) .order_by("-rank") ) return queryset, False ``` Here's [the full implementation](https://github.com/simonw/simonwillisonblog/blob/6c0de9f9976ef831fe92106be662d77dfe80b32a/blog/admin.py) for my personal blog. <p>Django 3.1 introduces PostgreSQL <code>search_type="websearch"</code> - which gives you search with advanced operators like <code>"phrase search" -excluding</code>. James Turk <a href="https://jamesturk.net/posts/websearch-in-django-31/" rel="nofollow">wrote about this here</a>, and it's also in <a href="https://simonwillison.net/2020/Jul/23/datasette-copyable-datasette-insert-api/" rel="nofollow">my weeknotes</a>.</p> <p>I decided to add it to my Django Admin interface. It was <em>really easy</em> using the <code>get_search_results()</code> model admin method, <a href="https://docs.djangoproject.com/en/3.0/ref/contrib/admin/#django.contrib.admin.ModelAdmin.get_search_results" rel="nofollow">documented here</a>.</p> <p>My models already have a <code>search_document</code> full-text search column, as described in <a href="https://simonwillison.net/2017/Oct/5/django-postgresql-faceted-search/" rel="nofollow">Implementing faceted search with Django and PostgreSQL</a>. So all I needed to add to my <code>ModelAdmin</code> subclasses was this:</p> <div class="highlight highlight-source-python"><pre> <span class="pl-k">def</span> <span class="pl-en">get_search_results</span>(<span class="pl-s1">self</span>, <span class="pl-s1">request</span>, <span class="pl-s1">queryset</span>, <span class="pl-s1">search_term</span>): <span class="pl-k">if</span> <span class="pl-c1">not</span> <span class="pl-s1">search_term</span>: <span class="pl-k">return</span> <span class="pl-en">super</span>().<span class="pl-en">get_search_results</span>( <span class="pl-s1">request</span>, <span class="pl-s1">queryset</span>, <span class="pl-s1">search_term</span> ) <span class="pl-s1">query</span> <span class="pl-c1">=</span> <span class="pl-v">SearchQuery</span>(<span class="pl-s1">search_term</span>, <span class="pl-s1">search_type</span><span class="pl-c1">=</span><span class="pl-s">"websearch"</span>) <span class="pl-s1">rank</span> <span class="pl-c1">=</span> <span class="pl-v">SearchRank</span>(<span class="pl-v">F</span>(<span class="pl-s">"search_document"</span>), <span class="pl-s1">query</span>) <span class="pl-s1">queryset</span> <span class="pl-c1">=</span> ( <span class="pl-s1">queryset</span> .<span class="pl-en">annotate</span>(<span class="pl-s1">rank</span><span class="pl-c1">=</span><span class="pl-s1">rank</span>) .<span class="pl-en">filter</span>(<span class="pl-s1">search_document</span><span class="pl-c1">=</span><span class="pl-s1">query</span>) .<span class="pl-en">order_by</span>(<span class="pl-s">"-rank"</span>) ) <span class="pl-k">return</span> <span class="pl-s1">queryset</span>, <span class="pl-c1">False</span></pre></div> <p>Here's <a href="https://github.com/simonw/simonwillisonblog/blob/6c0de9f9976ef831fe92106be662d77dfe80b32a/blog/admin.py">the full implementation</a> for my personal blog.</p> <Binary: 81,294 bytes> 2020-07-25T15:36:17-07:00 2020-07-25T22:36:17+00:00 2020-07-25T15:36:17-07:00 2020-07-25T22:36:17+00:00 8239440a6854c5c8b57e7d7f3ca75098 postgresql-full-text-search-admin
Powered by Datasette · How this site works · Code of conduct