-
Notifications
You must be signed in to change notification settings - Fork 129
Registering models
For your models to appear in django-watson search results, you first have to register them. A good place to do this is in your application's AppConfig:
from django.apps import AppConfig
from watson import search as watson
class YourAppConfig(AppConfig):
name = "your_app"
def ready(self):
YourModel = self.get_model("YourModel")
watson.register(YourModel)This will tell django-watson to index all CharFields and TextFields if they can't be null (without null=True) in your model. You can also provide fields and exclude parameters to control exactly which fields are indexed.
watson.register(YourModel, fields=("field1", "field2",))
watson.register(YourModel, exclude=("field1", "field2",))Please note that this approach will return all objects in your search results. To return only a subset of objects, or implement filtering defined by a custom model manager, please read the next section.
It's quite common to have some model instances in your database that shouldn't appear in search results, yet still exist in your database. For example, a blog post might be scheduled to be published next week, so shouldn't appear right now.
You can specify a subset of model instances to appear in search results by registering a filtered queryset of models:
watson.register(YourModel.objects.filter(is_published=True))The above code will ensure that no models where is_published == False will appear in your search results.
If you defined a custom manager on YourModel.objects that implements custom filtering, you need to explicitly register a queryset for django-watson to take the filtering into account:
watson.register(YourModel.objects.all())Note: This can slow down your searches a little, depending on the database, but it's probably not worth worrying about.
It's sometimes a good idea to include additional information in the results. You can specify additional fields to be stored in django-watson using the store parameter:
watson.register(YourModel, store=("publication_date", "thumbnail"))You can then access these additional fields in your search results as follows:
for result in watson.search("Search terms"):
print(result.meta["publication_date"])
print(result.meta["thumbnail"])It's also possible to specify model methods in the list of stored fields. These will be executed and the results stored whenever a registered model is created or updated.
By default, django-watson will place extra weighting in search results on the __str__ representation of the model, and treat all other indexed model fields as equal.
You can control the weighting of fields in your model by registering a your model with a subclass of SearchAdapter:
class YourSearchAdapter(watson.SearchAdapter):
def get_title(self, obj):
"""
Returns the title of this search result. This is given high priority in search result ranking.
You can access the title of the search result as `result.title` in your search results.
The default implementation returns `str(obj)`.
"""
return obj.title
def get_description(self, obj):
"""
Returns the description of this search result. This is given medium priority in search result ranking.
You can access the description of the search result as `result.description` in your search results. Since
this should contains a short description of the search result, it's excellent for providing a summary
in your search results.
The default implementation returns `u""`.
"""
return obj.description
def get_content(self, obj):
"""
Returns the content of this search result. This is given low priority in search result ranking.
You can access the content of the search result as `result.content` in your search results, although
this field generally contains a big mess of search data so is less suitable for frontend display.
The default implementation returns all the registered fields in your model joined together.
"""
return obj.content
watson.register(YourModel, YourSearchAdapter)Whenever you add a new model to django-watson, or make a change to the way a model is registered, you need to re-run the manage.py buildwatson command. This will ensure that your search indexes are up to date.
django-watson normally takes care of updating the search indexes automatically, so you only need to worry about running this command when you update your model registration code or if you perform bulk creates, updates or deletes which is something that django-watson cannot support.
In order to include related models when indexing a model, you'll need to add a few extra configuration steps to let django-watson know to defer its update until all related models are updated too.
The simplest solution is to add watson.middleware.SearchContextMiddleware to your MIDDLEWARE setting. Consider also setting "ATOMIC_REQUESTS": True in your Django settings file to update search indexes in the same transaction as model changes.
Alternatively, you can use the watson.update_index() context manager to mark up sections of your code where you're updating models registered to follow a foreign key.
# As a context manager.
with watson.update_index():
my_model.save()
# As a decorator.
@watson.update_index()
def save_my_model(my_model):
my_model.save()Sometimes you might need to manually update a model instance's search index. There is a simple API call to achieve this:
watson.default_search_engine.update_obj_index(obj)For example, consider the following models:
class Person(models.Model):
name = models.CharField(max_length=100)
def __unicode__(self):
return self.name
class Pet(models.Model):
name = models.CharField(max_length=100)
owner = models.ForeignKey(Person)
def __unicode__(self):
return self.name
watson.register(Pet, fields=("name", "owner"))When you save a Pet instance, it will index across the owner foreign key to include the name of the Person instance. However, if you save the Person instance independently, the search index for the corresponding Pet will not be updated.
The solution is to set up a post_save signal handler, to automatically update the Pet search index when a Person is saved.
from django.db.models.signals import post_save
def update_pet_index(instance, **kwargs):
for pet in instance.pet_set.all():
watson.default_search_engine.update_obj_index(pet)
post_save.connect(update_pet_index, Person)Sometimes, you might be updating a model in a way that won't impact the search indices (for example, just changing a non-indexed field). Ordinarily, the slight overhead introduced by django-watson's index update won't matter, but during bulk data loading or performance-critical code it can be useful to disable unnecessary updates.
Important: Only disable search index updates when the fields being changes are not indexed by django-watson. Disabling search index updates carelessly can result in stale results appearing in your searches.
# As a context manager.
with watson.skip_index_update():
my_model.save(update_fields=("something_not_indexed",))
# As a decorator.
@watson.skip_index_update()
def save_my_model(my_model):
my_model.save(update_fields=("something_not_indexed",))