【问题标题】:Django Haystack indexing is not working for many to many field in modelDjango Haystack 索引不适用于模型中的多对多字段
【发布时间】:2013-10-31 12:05:47
【问题描述】:

我在我们的 django 应用程序中使用 haystack 进行搜索,搜索工作得很好。但是我遇到了实时搜索的问题。对于实时搜索,我使用的是 haystack 的默认 RealTimeSignalProcessor(haystack.signals.RealtimeSignalProcessor)。我的模型中包含一个多对多字段。当仅针对这个多对多字段更改数据时,实时信号处理器似乎没有正确更新索引数据。更新多对多数据后,我得到错误的搜索结果。

它在手动运行rebuild_index 命令后工作。我认为rebuild_index 正在工作,因为它先进行清理,然后再构建索引数据。

有人可以提出一些解决问题的方法吗?

顺便说一下,下面是围绕它的代码。

型号:

class Message_forum(models.Model):
      message = models.ForeignKey(Message)
      tags = models.ManyToManyField(Tag, blank=True, null=True) #this is many to many field

search_index.py:

class Message_forumIndex(indexes.SearchIndex, indexes.Indexable):
    text = indexes.EdgeNgramField(document=True, use_template=True)
    message = indexes.CharField(model_attr='message', null=True)
    tags = indexes.CharField(model_attr='tags', null=True)

    def get_model(self):
        return Message_forum

    def index_queryset(self, using=None):
        return self.get_model().objects.all()

    def prepare_tags(self, obj):
        return [tag.tag for tag in obj.tags.all()]

索引模板:

{{ object.tags.tag }}

settings.py:

HAYSTACK_SIGNAL_PROCESSOR = 'haystack.signals.RealtimeSignalProcessor'

我正在使用最新版本的 haystack 和 whoosh 作为后端。

【问题讨论】:

    标签: django search django-haystack whoosh


    【解决方案1】:

    我在研究干草堆的代码后发现了。

    在haystack默认的RealTimeSignalProcessor中,其连接每个应用模型的post_save和post_delete信号。现在在 post_save 和 post_delete 信号中调用 handle_save 方法。在这种方法中,haystack 正在验证发件人,在我的情况下,对于标签(多对多)字段,Message_forum_tag 模型作为发件人传递。现在这个模型的索引没有出现在我的 search_index 中,因为它不是我的应用程序模型,而是 django 生成的。因此,在 handle_save 方法中,它绕过了这个模型上的任何更改,因此它没有更新已更改对象的索引数据。

    所以我想出了两种不同的解决方案来解决这个问题。

    1. 我可以创建特定于我的模型 Message_forum 的自定义实时信号处理器,在这个设置方法中,我可以将 Message_forum 中每个多对多字段上的 m2mchanged 信号与 handle_save 连接起来。同时我可以将 Message_forum 作为发送者传递,这样 haystack 将通过验证(不完全是验证,但它试图获取它的索引 obj)并更新更改对象的索引数据。

    2. 另一种方法是确保每当更改任何多对多字段时,都会调用其父项的保存方法(此处为 Message_forum.save())。所以它总是会调用 post_save 信号,然后 haystack 会更新索引对象数据。

    花了大约 3 个小时才弄明白。希望这能帮助遇到同样问题的人。

    【讨论】:

    • 你能显示代码吗?我也有同样的问题。尝试了一些东西,但仍然没有功能工作。使用 django-rq 我得到了它,但如果它可以使用自己的功能,我不想使用第三方库来让它工作。
    • 我们已经通过上面提到的第二种方法解决了这个问题。如果它不适用于您的情况,那么您应该创建一个自定义实时信号处理器。我也试过第一种方法。但是那个代码现在不在我身边。但是你可以在这里阅读更多关于它的信息..django-haystack.readthedocs.org/en/latest/…
    【解决方案2】:

    我遇到了类似的问题,但我选择了 Nikhil 的 1 号和 2 号选项的混合体。

    对于一个名为 ContentItem 的模型和一个名为 categories 的 m2m 字段,我创建了一个自定义信号处理器来扩展基础信号处理器。

    所以我实现了一个从源代码复制的 setup(),但添加了以下行:

    models.signals.m2m_changed.connect(self.handle_save, sender=ContentItem.categories.through)
    

    并且对 teardown() 做了同样的事情,但使用了类似的断开连接线。我还扩展了 handle_save 并更改了行:

    index = self.connections[using].get_unified_index().get_index(sender)
    

    index = self.connections[using].get_unified_index().get_index(instance.__class__)
    

    这意味着此信号处理器正在监视管理表中从 ContentItem 到 Category 的 m2m 更改,但是当进行 m2m 更改时,将传递正确类的名称,即 ContentItem 而不是 ContentItem.categories.through。

    这似乎在大多数情况下都有效,但如果我删除一个类别,尽管删除了关系,但 m2m_changed 不会触发。 It looks like this might be a bug in django itself.

    所以我还在设置中添加了以下行(并断开连接以进行拆卸):

    models.signals.pre_delete.connect(self.handle_m2m_delete, sender=Category)
    

    并创建了handle_save (handle_m2m_delete) 的方法副本,该方法手动从直通表中删除了关系并保存了受影响的ContentItems(导致原始的handle_save 然后被触发)。这至少意味着我不必记住保存父级来更新代码中其他任何地方的索引。

    【讨论】:

    • 感谢您的解决方案,但您能否提供“handle_m2m_delete”的代码?就像一张图片,一些代码告诉 1000 个单词
    • @straykiwi 恐怕不行,我再也无法访问 repo
    【解决方案3】:

    我可以建议一个替代解决方案,它比尝试观察所有正确信号并最终使用必须了解所有 m2m 关系的信号处理器的复杂性更简单。

    看起来像这样:

    signals.py:

    from collections import OrderedDict
    
    from haystack.signals import RealtimeSignalProcessor
    
    
    class BatchingSignalProcessor(RealtimeSignalProcessor):
        """
        RealtimeSignalProcessor connects to Django model signals
        we store them locally for processing later - must call
        ``flush_changes`` from somewhere else (eg middleware)
        """
    
        # Haystack instantiates this as a singleton
    
        _change_list = OrderedDict()
    
        def _add_change(self, method, sender, instance):
            key = (sender, instance.pk)
            if key in self._change_list:
                del self._change_list[key]
            self._change_list[key] = (method, instance)
    
        def handle_save(self, sender, instance, created, raw, **kwargs):
            method = super(BatchingSignalProcessor, self).handle_save
            self._add_change(method, sender, instance)
    
        def handle_delete(self, sender, instance, **kwargs):
            method = super(BatchingSignalProcessor, self).handle_delete
            self._add_change(method, sender, instance)
    
        def flush_changes(self):
            while True:
                try:
                    (sender, pk), (method, instance) = self._change_list.popitem(last=False)
                except KeyError:
                    break
                else:
                    method(sender, instance)
    

    middleware.py:

    from haystack import signal_processor
    
    
    class HaystackBatchFlushMiddleware(object):
        """
        for use with our BatchingSignalProcessor
    
        this should be placed *at the top* of MIDDLEWARE_CLASSES
        (so that it runs last)
        """
        def process_response(self, request, response):
            try:
                signal_processor.flush_changes()
            except AttributeError:
                # (in case we're not using our expected signal_processor)
                pass
            return response
    

    settings.py:

    MIDDLEWARE_CLASSES = (
        'myproject.middleware.HaystackBatchFlushMiddleware',
        ...
    )
    
    HAYSTACK_SIGNAL_PROCESSOR = 'myproject.signals.BatchingSignalProcessor'
    

    我正在我的项目中尝试这个,似乎工作正常。我欢迎任何反馈或建议。

    【讨论】:

    • 谢谢,效果很好。请注意,为了与 Django 1.9 兼容,您需要使用 django-haystack 版本 >= 2.5.0,然后,由于 django-haystack 中的更改,您需要在 middleware.py 中稍微不同地使用信号处理器。 1.使用这个导入:from django.apps import apps,2.在try-except之前插入signal_processor = apps.get_app_config('haystack').signal_processor
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2015-09-21
    • 1970-01-01
    • 2011-10-03
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多