Clean up search backend classes

This commit is contained in:
jeremystretch 2022-10-21 09:24:06 -04:00
parent 7ebffa85f7
commit ac33eb71ee

View File

@ -1,5 +1,4 @@
from collections import defaultdict from collections import defaultdict
from importlib import import_module
from django.conf import settings from django.conf import settings
from django.contrib.contenttypes.models import ContentType from django.contrib.contenttypes.models import ContentType
@ -20,18 +19,17 @@ MAX_RESULTS = 1000
class SearchBackend: class SearchBackend:
"""A search engine capable of performing multi-table searches.""" """
_search_choice_options = tuple() Base class for search backends. Subclasses must extend the `cache()`, `remove()`, and `clear()` methods below.
"""
def __init__(self): _object_types = None
# Connect handlers to the appropriate model signals
post_save.connect(self.caching_handler)
post_delete.connect(self.removal_handler)
def get_object_types(self): def get_object_types(self):
"""Return the set of choices for individual object types, organized by category.""" """
if not self._search_choice_options: Return a list of all registered object types, organized by category, suitable for populating a form's
ChoiceField.
"""
if not self._object_types:
# Organize choices by category # Organize choices by category
categories = defaultdict(dict) categories = defaultdict(dict)
@ -45,9 +43,9 @@ class SearchBackend:
*[(category, list(choices.items())) for category, choices in categories.items()] *[(category, list(choices.items())) for category, choices in categories.items()]
) )
self._search_choice_options = results self._object_types = results
return self._search_choice_options return self._object_types
def search(self, value, user=None, object_types=None, lookup=DEFAULT_LOOKUP_TYPE): def search(self, value, user=None, object_types=None, lookup=DEFAULT_LOOKUP_TYPE):
""" """
@ -55,36 +53,31 @@ class SearchBackend:
""" """
raise NotImplementedError raise NotImplementedError
@classmethod def caching_handler(self, sender, instance, **kwargs):
def caching_handler(cls, sender, instance, **kwargs):
""" """
Receiver for the post_save signal, responsible for caching object creation/changes. Receiver for the post_save signal, responsible for caching object creation/changes.
""" """
cls.cache(instance) self.cache(instance)
@classmethod def removal_handler(self, sender, instance, **kwargs):
def removal_handler(cls, sender, instance, **kwargs):
""" """
Receiver for the post_delete signal, responsible for caching object deletion. Receiver for the post_delete signal, responsible for caching object deletion.
""" """
cls.remove(instance) self.remove(instance)
@classmethod def cache(self, instances, indexer=None, remove_existing=True):
def cache(cls, instance, indexer=None):
""" """
Create or update the cached representation of an instance. Create or update the cached representation of an instance.
""" """
raise NotImplementedError raise NotImplementedError
@classmethod def remove(self, instance):
def remove(cls, instance):
""" """
Delete any cached representation of an instance. Delete any cached representation of an instance.
""" """
raise NotImplementedError raise NotImplementedError
@classmethod def clear(self, object_types=None):
def clear(cls, instance):
""" """
Delete *all* cached data. Delete *all* cached data.
""" """
@ -143,8 +136,7 @@ class CachedValueSearchBackend(SearchBackend):
r for r in results if r.object is not None r for r in results if r.object is not None
] ]
@classmethod def cache(self, instances, indexer=None, remove_existing=True):
def cache(cls, instances, indexer=None, remove_existing=True):
content_type = None content_type = None
custom_fields = None custom_fields = None
@ -172,7 +164,7 @@ class CachedValueSearchBackend(SearchBackend):
# Wipe out any previously cached values for the object # Wipe out any previously cached values for the object
if remove_existing: if remove_existing:
cls.remove(instance) self.remove(instance)
# Generate cache data # Generate cache data
for field in indexer.to_cache(instance, custom_fields=custom_fields): for field in indexer.to_cache(instance, custom_fields=custom_fields):
@ -198,8 +190,7 @@ class CachedValueSearchBackend(SearchBackend):
return counter return counter
@classmethod def remove(self, instance):
def remove(cls, instance):
# Avoid attempting to query for non-cacheable objects # Avoid attempting to query for non-cacheable objects
try: try:
get_indexer(instance) get_indexer(instance)
@ -212,8 +203,7 @@ class CachedValueSearchBackend(SearchBackend):
# Call _raw_delete() on the queryset to avoid first loading instances into memory # Call _raw_delete() on the queryset to avoid first loading instances into memory
return qs._raw_delete(using=qs.db) return qs._raw_delete(using=qs.db)
@classmethod def clear(self, object_types=None):
def clear(cls, object_types=None):
qs = CachedValue.objects.all() qs = CachedValue.objects.all()
if object_types: if object_types:
qs = qs.filter(object_type__in=object_types) qs = qs.filter(object_type__in=object_types)
@ -240,3 +230,7 @@ def get_backend():
search_backend = get_backend() search_backend = get_backend()
# Connect handlers to the appropriate model signals
post_save.connect(search_backend.caching_handler)
post_delete.connect(search_backend.removal_handler)