Haystack introduced several backward-incompatible changes in the process of moving from the 1.X series to the 2.X series. These were done to clean up the API, to support new features & to clean up problems in 1.X. At a high level, they consisted of:
- The removal of
SearchSite
&haystack.site
. - The removal of
handle_registrations
&autodiscover
. - The addition of multiple index support.
- The addition of
SignalProcessors
& the removal ofRealTimeSearchIndex
. - The removal/renaming of various settings.
This guide will help you make the changes needed to be compatible with Haystack 2.X.
Most prominently, the old way of specifying a backend & its settings has changed to support the multiple index feature. A complete Haystack 1.X example might look like:
HAYSTACK_SEARCH_ENGINE = 'solr'
HAYSTACK_SOLR_URL = 'http://localhost:9001/solr/default'
HAYSTACK_SOLR_TIMEOUT = 60 * 5
HAYSTACK_INCLUDE_SPELLING = True
HAYSTACK_BATCH_SIZE = 100
# Or...
HAYSTACK_SEARCH_ENGINE = 'whoosh'
HAYSTACK_WHOOSH_PATH = '/home/search/whoosh_index'
HAYSTACK_WHOOSH_STORAGE = 'file'
HAYSTACK_WHOOSH_POST_LIMIT = 128 * 1024 * 1024
HAYSTACK_INCLUDE_SPELLING = True
HAYSTACK_BATCH_SIZE = 100
# Or...
HAYSTACK_SEARCH_ENGINE = 'xapian'
HAYSTACK_XAPIAN_PATH = '/home/search/xapian_index'
HAYSTACK_INCLUDE_SPELLING = True
HAYSTACK_BATCH_SIZE = 100
In Haystack 2.X, you can now supply as many backends as you like, so all of the above settings can now be active at the same time. A translated set of settings would look like:
HAYSTACK_CONNECTIONS = {
'default': {
'ENGINE': 'haystack.backends.solr_backend.SolrEngine',
'URL': 'http://localhost:9001/solr/default',
'TIMEOUT': 60 * 5,
'INCLUDE_SPELLING': True,
'BATCH_SIZE': 100,
},
'autocomplete': {
'ENGINE': 'haystack.backends.whoosh_backend.WhooshEngine',
'PATH': '/home/search/whoosh_index',
'STORAGE': 'file',
'POST_LIMIT': 128 * 1024 * 1024,
'INCLUDE_SPELLING': True,
'BATCH_SIZE': 100,
},
'slave': {
'ENGINE': 'xapian_backend.XapianEngine',
'PATH': '/home/search/xapian_index',
'INCLUDE_SPELLING': True,
'BATCH_SIZE': 100,
},
}
You are required to have at least one connection listed within HAYSTACK_CONNECTIONS
, it must be named default
& it must have a valid ENGINE
within it. Bare minimum looks like:
HAYSTACK_CONNECTIONS = {
'default': {
'ENGINE': 'haystack.backends.simple_backend.SimpleEngine'
}
}
The key for each backend is an identifier you use to describe the backend within your app. You should refer to the ref-multiple_index
documentation for more information on using the new multiple indexes & routing features.
Also note that the ENGINE
setting has changed from a lowercase "short name" of the engine to a full path to a new Engine
class within the backend. Available options are:
haystack.backends.solr_backend.SolrEngine
haystack.backends.whoosh_backend.WhooshEngine
haystack.backends.simple_backend.SimpleEngine
Additionally, the following settings were outright removed & will generate an exception if found:
HAYSTACK_SITECONF
- Remove this setting & the file it pointed to.HAYSTACK_ENABLE_REGISTRATIONS
HAYSTACK_INCLUDE_SPELLING
The dummy
backend was outright removed from Haystack, as it served very little use after the simple
(pure-ORM-powered) backend was introduced.
If you wrote a custom backend, please refer to the "Custom Backends" section below.
The other major changes affect the SearchIndex
class. As the concept of haystack.site
& SearchSite
are gone, you'll need to modify your indexes.
A Haystack 1.X index might've looked like:
import datetime
from haystack.indexes import *
from haystack import site
from myapp.models import Note
class NoteIndex(SearchIndex):
text = CharField(document=True, use_template=True)
author = CharField(model_attr='user')
pub_date = DateTimeField(model_attr='pub_date')
def get_queryset(self):
"""Used when the entire index for model is updated."""
return Note.objects.filter(pub_date__lte=datetime.datetime.now())
site.register(Note, NoteIndex)
A converted Haystack 2.X index should look like:
import datetime
from haystack import indexes
from myapp.models import Note
class NoteIndex(indexes.SearchIndex, indexes.Indexable):
text = indexes.CharField(document=True, use_template=True)
author = indexes.CharField(model_attr='user')
pub_date = indexes.DateTimeField(model_attr='pub_date')
def get_model(self):
return Note
def index_queryset(self, using=None):
"""Used when the entire index for model is updated."""
return self.get_model().objects.filter(pub_date__lte=datetime.datetime.now())
Note the import on site
& the registration statements are gone. Newly added are is the NoteIndex.get_model
method. This is a required method & should simply return the Model
class the index is for.
There's also a new, additional class added to the class
definition. The indexes.Indexable
class is a simple mixin that serves to identify the classes Haystack should automatically discover & use. If you have a custom base class (say QueuedSearchIndex
) that other indexes inherit from, simply leave the indexes.Indexable
off that declaration & Haystack won't try to use it.
Additionally, the name of the document=True
field is now enforced to be text
across all indexes. If you need it named something else, you should set the HAYSTACK_DOCUMENT_FIELD
setting. For example:
HAYSTACK_DOCUMENT_FIELD = 'pink_polka_dot'
Finally, the index_queryset
method should supplant the get_queryset
method. This was present in the Haystack 1.2.X series (with a deprecation warning in 1.2.4+) but has been removed in Haystack v2.
Finally, if you were unregistering other indexes before, you should make use of the new EXCLUDED_INDEXES
setting available in each backend's settings. It should be a list of strings that contain the Python import path to the indexes that should not be loaded & used. For example:
HAYSTACK_CONNECTIONS = {
'default': {
'ENGINE': 'haystack.backends.solr_backend.SolrEngine',
'URL': 'http://localhost:9001/solr/default',
'EXCLUDED_INDEXES': [
# Imagine that these indexes exist. They don't.
'django.contrib.auth.search_indexes.UserIndex',
'third_party_blog_app.search_indexes.EntryIndex',
]
}
}
This allows for reliable swapping of the index that handles a model without relying on correct import order.
Use of the haystack.indexes.RealTimeSearchIndex
is no longer valid. It has been removed in favor of RealtimeSignalProcessor
. To migrate, first change the inheritance of all your RealTimeSearchIndex
subclasses to use SearchIndex
instead:
# Old.
class MySearchIndex(indexes.RealTimeSearchIndex, indexes.Indexable):
# ...
# New.
class MySearchIndex(indexes.SearchIndex, indexes.Indexable):
# ...
Then update your settings to enable use of the RealtimeSignalProcessor
:
HAYSTACK_SIGNAL_PROCESSOR = 'haystack.signals.RealtimeSignalProcessor'
For most basic uses of Haystack, this is all that is necessary to work with Haystack 2.X. You should rebuild your index if needed & test your new setup.
If you were manually swapping the SearchQuery
or SearchBackend
being used by SearchQuerySet
in the past, it's now preferable to simply setup another connection & use the SearchQuerySet.using
method to select that connection instead.
Also, if you were manually instantiating SearchBackend
or SearchQuery
, it's now preferable to rely on the connection's engine to return the right thing. For example:
from haystack import connections
backend = connections['default'].get_backend()
query = connections['default'].get_query()
If you had written a custom SearchBackend
and/or custom SearchQuery
, there's a little more work needed to be Haystack 2.X compatible.
You should, but don't have to, rename your SearchBackend
& SearchQuery
classes to be more descriptive/less collide-y. For example, solr_backend.SearchBackend
became solr_backend.SolrSearchBackend
. This prevents non-namespaced imports from stomping on each other.
You need to add a new class to your backend, subclassing BaseEngine
. This allows specifying what backend
& query
should be used on a connection with less duplication/naming trickery. It goes at the bottom of the file (so that the classes are defined above it) and should look like:
from haystack.backends import BaseEngine
from haystack.backends.solr_backend import SolrSearchQuery
# Code then...
class MyCustomSolrEngine(BaseEngine):
# Use our custom backend.
backend = MySolrBackend
# Use the built-in Solr query.
query = SolrSearchQuery
Your HAYSTACK_CONNECTIONS['default']['ENGINE']
should then point to the full Python import path to your new BaseEngine
subclass.
Finally, you will likely have to adjust the SearchBackend.__init__
& SearchQuery.__init__
, as they have changed significantly. Please refer to the commits for those backends.