djangodjango-haystack

add fields to SearchIndex dynamically (django-haystack)


Currently I see two possibilities, with using locals() and using setattr() after definition of SearchIndex class:

1.

class TestIndex(indexes.SearchIndex, indexes.Indexable):
    for name in fields:
        locals()["attr_%s" % name] = DescriptionSearchField(boost=3, **{"d_attr": name, "name": "attr"})
        locals()["slug_%s" % name] = DescriptionSearchField(**{"d_attr": name, "name": "slug"})

2.


class TestIndex(indexes.SearchIndex, indexes.Indexable):
    pass

for name in fields:
    setattr(JobTestIndex, "attr_%s" % name, DescriptionSearchField(boost=3, **{"d_attr": name, "name": "attr"}))
    setattr(JobTestIndex, "slug_%s" % name, DescriptionSearchField(**{"d_attr": name, "name": "slug"}))

I am using Django-haystack with ElasticSearch.

I know that using locals() is not the best practice.

What then is the correct approach to my issue?

Any suggestions are greatly appreciated. Thanks.


Solution

  • I decided to overwrite __new__() method of DeclarativeMetaclass, that is the metaclass of SearchIndex.

    So the part of the code now looks like this:

    
    from haystack import indexes
    
    from django.utils import six
    
    from search_fields import DescriptionSearchField
    
    
    class ChildDeclarativeMetaclass(indexes.DeclarativeMetaclass):
        def __new__(cls, name, bases, attrs):
            for f in reversed(attrs.pop("_extra_fields")):
                attrs.update(f)
            return super(ChildDeclarativeMetaclass, cls).__new__(cls, name, bases, attrs)
    
    
    class OpenTaskIndex(six.with_metaclass(ChildDeclarativeMetaclass, indexes.SearchIndex, indexes.Indexable)):
        text = indexes.CharField(document=True, use_template=True, template_name='search/indexes/job/open_task_text.txt')
       
        _extra_fields = tuple((
            ("title_%s" % lang[0], DescriptionSearchField(boost=3, **{"d_lang": lang[0], "d_attr": "title"})),
            ("slug_%s" % lang[0], DescriptionSearchField(**{"d_lang": lang[0], "d_attr": "slug"})),
            ("text_%s" % lang[0], DescriptionSearchField(**{"d_lang": lang[0], "d_attr": "text_description"})))
            for lang in settings.LANGUAGES
        )
    

    If you setting fields up after the class creation. They are not added to the attrs["fields"] in DeclarativeMetaclass.