The standard way to create a Haystack search index is in a search_indexes.py
file, with a class per model to index, like so:
class FooIndex(indexes.Indexable):
def get_model(self):
return models.Foo
However, I have a lot of models that all behave similarly and I want to index them (as well as registering in other ways).
For example, given this models.py
code:
class BaseModel(models.model):
class Meta:
abstract = True
# some shared fields
class Foo(BaseModel):
pass # some different fields
class Bar(BaseModel):
pass
This code works:
def register_base_model(sub_class, *args, **kwargs):
#other shared actions here
admin.site.register(sub_class,BaseAdmin) # BaseAdmin defined elsewhere
register_base_model(models.Foo)
But this won't:
def register_base_model(sub_class, *args, **kwargs):
#other shared actions here
admin.site.register(sub_class,BaseAdmin) # BaseAdmin defined elsewhere
class SubclassedIndex(indexes.SearchIndex,indexes.Indexable):
def get_model(self):
return sub_class
register_base_model(models.Foo)
Because the class SubclassedIndex
is only in the scope of register_base_model
. Haystack 1.x used to have a register
/unregister
API, but this is now gone, and while the new method is mostly easier, I need a similar behaviour.
How can I programmatically register a HaystackIndex without relying on the automagical way it operates now?
This can be done in at least one way, albeit in a very hacky way.
Firstly, you must have an app with a search_indexes.py
module, and it must be imported where you programmatically add indexes:
from haystack import connections
from haystack.constants import DEFAULT_ALIAS
import my_app.search_indexes as search_index
for my_class in some_list_of_models:
search_index_class_name = "%s_%sSearchIndex"%(concept_class._meta.app_label,concept_class.__name__)
# Create a new subclass of search index (see below)
# And monkey-patch it in.
setattr(search_index, class_name, create(concept_class))
# Since we've added a new class, kill the index so it is rebuilt next time we access the index.
connections[DEFAULT_ALIAS]._index = None
Then you need a function to build the class
def create(cls):
class DynamicSearchIndex(indexes.SearchIndex, indexes.Indexable):
# What ever fields are needed go here
def get_model(self):
return cls
return SubclassedConceptIndex
As I found out, you need to make sure the templates exist in the right spots, and might want to add a try
/except
wrapper to catch errors when building classes.