如何基于 ManyToManyField 过滤 Wagtail 中的搜索结果?

核心转储错误

我有一个定义Event模型的 Wagtail 站点这些事件有多个事件发起者,它们由模型ManyToManyField上的a 关联EventSponsor

class Event(index.Indexed, ClusterableModel):

    title       = models.CharField(max_length=255)
    start_date  = models.DateTimeField()
    end_date    = models.DateTimeField(null=True, blank=True)
    description = RichTextField(blank=True)

    search_fields = [
        index.SearchField('title', partial_match=True, boost=2.0),
        index.SearchField('description'),
        index.RelatedFields('sponsors', [
            index.SearchField('name', partial_match=True)
        ]),

        index.FilterField('end_date'),
        index.FilterField('sponsors'),
    ]

class EventSponsor(index.Indexed, models.Model):

    sponsor_id = models.IntegerField()
    name = models.CharField(max_length=255)
    url = models.URLField(blank=True)

    events = models.ManyToManyField(Event, related_name='sponsors')

    search_fields = [
        index.SearchField('name', partial_match=True),
    ]

除此之外,我的 Wagtail 服务器上的不同站点根据一组特定于该站点的选定事件赞助商在其日历中包含事件。

因此,为每个站点构建日历列表​​查询集如下所示:

def get_events_for_current_site(request, listing):
    try:
        event_sponsor_settings = EventSponsorSettings.objects.get(site=request.site)
    except EventSponsorSettings.DoesNotExist:
        # If there's no EventSponsorSettings for this Site, return an empty QuerySet. This shouldn't really ever happen.
        return Event.objects.none()

    # Return the selected Events in decending order of start date.
    query = Event.objects.filter(sponsors__in=event_sponsor_settings.selected_event_sponsors)
    if listing == 'upcoming_events':
        return query.order_by('start_date').filter(end_date__gte=timezone.now())
    else:
        return query.order_by('-start_date').filter(end_date__lt=timezone.now())

event_sponsor_settings.selected_event_sponsors是一个EventSponsor对象列表此查询集适用于列表页面。

我需要每个站点上的搜索功能(使用 Elasticsearch 后端)以仅包含将出现在当前站点日历上的事件。因此,我希望我的基本查询集与日历页面使用的查询集相同(或至少进行相同的过滤)。所以我的事件搜索代码基本上调用:

backend.search(search_query, get_events_for_current_site())

但是,我遇到了两个问题:

1)如果我使用index.FilterField('sponsors')in Event.search_fields,运行时会出现此错误manage.py update_index

Traceback (most recent call last):
  File "./manage.py", line 33, in <module>
    execute_from_command_line(argv)
  File "/multitenant-ve/lib/python2.7/site-packages/django/core/management/__init__.py", line 353, in execute_from_command_line
    utility.execute()
  File "/multitenant-ve/lib/python2.7/site-packages/django/core/management/__init__.py", line 345, in execute
    self.fetch_command(subcommand).run_from_argv(self.argv)
  File "/multitenant-ve/lib/python2.7/site-packages/django/core/management/base.py", line 348, in run_from_argv
    self.execute(*args, **cmd_options)
  File "/multitenant-ve/lib/python2.7/site-packages/django/core/management/base.py", line 399, in execute
    output = self.handle(*args, **options)
  File "/multitenant-ve/src/wagtail/wagtail/wagtailsearch/management/commands/update_index.py", line 120, in handle
    self.update_backend(backend_name, schema_only=options.get('schema_only', False))
  File "/multitenant-ve/src/wagtail/wagtail/wagtailsearch/management/commands/update_index.py", line 77, in update_backend
    index.add_model(model)
  File "/multitenant-ve/src/wagtail/wagtail/wagtailsearch/backends/elasticsearch.py", line 536, in add_model
    index=self.name, doc_type=mapping.get_document_type(), body=mapping.get_mapping()
  File "/multitenant-ve/src/wagtail/wagtail/wagtailsearch/backends/elasticsearch.py", line 137, in get_mapping
    self.get_field_mapping(field) for field in self.model.get_search_fields()
  File "/multitenant-ve/src/wagtail/wagtail/wagtailsearch/backends/elasticsearch.py", line 137, in <genexpr>
    self.get_field_mapping(field) for field in self.model.get_search_fields()
  File "/multitenant-ve/src/wagtail/wagtail/wagtailsearch/backends/elasticsearch.py", line 119, in get_field_mapping
    return self.get_field_column_name(field), mapping
  File "/multitenant-ve/src/wagtail/wagtail/wagtailsearch/backends/elasticsearch.py", line 72, in get_field_column_name
    return field.get_attname(self.model) + '_filter'
  File "/multitenant-ve/src/wagtail/wagtail/wagtailsearch/index.py", line 178, in get_attname
    return field.attname
AttributeError: 'ManyToManyRel' object has no attribute 'attname'

2) 如果我取出index.FilterField('sponsors'),manage.py update_index可以工作,但是在搜索时出现错误:

Cannot filter search results with field "eventsponsor_id". Please add index.FilterField('eventsponsor_id') to Event.search_fields.

所以我尝试添加index.FilterField('eventsponsor_id'), bit 它在update_index:期间发出此警告Event.search_fields contains field 'eventsponsor_id' but it doesn't exist,并在搜索时导致此回溯:

Traceback:
File "/multitenant-ve/lib/python2.7/site-packages/django/core/handlers/base.py" in get_response
  174.                     response = self.process_exception_by_middleware(e, request)
File "/multitenant-ve/lib/python2.7/site-packages/django/core/handlers/base.py" in get_response
  172.                     response = response.render()
File "/multitenant-ve/lib/python2.7/site-packages/django/template/response.py" in render
  160.             self.content = self.rendered_content
File "/multitenant-ve/lib/python2.7/site-packages/django/template/response.py" in rendered_content
  137.         content = template.render(context, self._request)
File "/multitenant-ve/lib/python2.7/site-packages/django/template/backends/django.py" in render
  95.             return self.template.render(context)
File "/multitenant-ve/lib/python2.7/site-packages/django/template/base.py" in render
  206.                     return self._render(context)
File "/multitenant-ve/lib/python2.7/site-packages/django/template/base.py" in _render
  197.         return self.nodelist.render(context)
File "/multitenant-ve/lib/python2.7/site-packages/django/template/base.py" in render
  992.                 bit = node.render_annotated(context)
File "/multitenant-ve/lib/python2.7/site-packages/django/template/base.py" in render_annotated
  959.             return self.render(context)
File "/multitenant-ve/lib/python2.7/site-packages/django/template/loader_tags.py" in render
  173.         return compiled_parent._render(context)
File "/multitenant-ve/lib/python2.7/site-packages/django/template/base.py" in _render
  197.         return self.nodelist.render(context)
File "/multitenant-ve/lib/python2.7/site-packages/django/template/base.py" in render
  992.                 bit = node.render_annotated(context)
File "/multitenant-ve/lib/python2.7/site-packages/django/template/base.py" in render_annotated
  959.             return self.render(context)
File "/multitenant-ve/lib/python2.7/site-packages/django/template/loader_tags.py" in render
  173.         return compiled_parent._render(context)
File "/multitenant-ve/lib/python2.7/site-packages/django/template/base.py" in _render
  197.         return self.nodelist.render(context)
File "/multitenant-ve/lib/python2.7/site-packages/django/template/base.py" in render
  992.                 bit = node.render_annotated(context)
File "/multitenant-ve/lib/python2.7/site-packages/django/template/base.py" in render_annotated
  959.             return self.render(context)
File "/multitenant-ve/lib/python2.7/site-packages/django/template/loader_tags.py" in render
  69.                 result = block.nodelist.render(context)
File "/multitenant-ve/lib/python2.7/site-packages/django/template/base.py" in render
  992.                 bit = node.render_annotated(context)
File "/multitenant-ve/lib/python2.7/site-packages/django/template/base.py" in render_annotated
  959.             return self.render(context)
File "/multitenant-ve/lib/python2.7/site-packages/django/template/defaulttags.py" in render
  220.                     nodelist.append(node.render_annotated(context))
File "/multitenant-ve/lib/python2.7/site-packages/django/template/base.py" in render_annotated
  959.             return self.render(context)
File "/multitenant-ve/lib/python2.7/site-packages/django/template/defaulttags.py" in render
  325.             if match:
File "/multitenant-ve/src/wagtail/wagtail/wagtailsearch/backends/base.py" in __len__
  174.         return len(self.results())
File "/multitenant-ve/src/wagtail/wagtail/wagtailsearch/backends/base.py" in results
  137.             self._results_cache = self._do_search()
File "/multitenant-ve/src/wagtail/wagtail/wagtailsearch/backends/elasticsearch.py" in _do_search
  452.         hits = self.backend.es.search(**params)
File "/multitenant-ve/lib/python2.7/site-packages/elasticsearch/client/utils.py" in _wrapped
  69.             return func(*args, params=params, **kwargs)
File "/multitenant-ve/lib/python2.7/site-packages/elasticsearch/client/__init__.py" in search
  531.             doc_type, '_search'), params=params, body=body)
File "/multitenant-ve/lib/python2.7/site-packages/elasticsearch/transport.py" in perform_request
  273.             body = self.serializer.dumps(body)
File "/multitenant-ve/lib/python2.7/site-packages/elasticsearch/serializer.py" in dumps
  47.             raise SerializationError(data, e)

Exception Type: SerializationError at /search
Exception Value: ({u'query': {u'filtered': {u'filter': {u'and': [{u'prefix': {u'content_type': u'event'}}, {'and': [{u'terms': {u'eventsponsor_id_filter': [<EventSponsor: Division of Geological and Planetary Sciences (9003)>]}}, {u'range': {u'end_date_filter': {'gte': datetime.datetime(2017, 3, 29, 0, 42, 7, 462939, tzinfo=<UTC>)}}}]}]}, u'query': {u'multi_match': {u'query': u'geo', u'fields': [u'_all', u'_partials']}}}}}, TypeError("Unable to serialize <EventSponsor: Division of Geological and Planetary Sciences (9003)> (type: <class 'templated_cms.models.events.EventSponsor'>)",))

所以,我尝试将查询集更改get_events_for_current_site()Event.objects.filter(sponsors__id__in=[s.id for s in event_sponsor_settings.selected_event_sponsors])

这修复了错误......但我根本没有得到任何搜索结果。

我完全不知道如何处理这个问题。:(

核心转储错误

对于那些将来遇到这个问题的人,这是我最终为解决这个问题所做的(代码已被减少到最低限度以显示我使用的机制):

class Event(index.Indexed, ClusterableModel):

    title       = models.CharField(max_length=255)
    start_date  = models.DateTimeField()
    end_date    = models.DateTimeField(null=True, blank=True)
    description = RichTextField(blank=True)
    lecture_series = models.ForeignKey(
        'this_app.LectureSeries', null=True, blank=True, related_name='events', 
        on_delete=models.SET_NULL
    )

    search_fields = [
        ...
        # We use a Filterfield on lecture_series here because we apparently can't do it 
        # on lecture_series_id for whatever reason. This means we need to filter Events
        # on their lecture_series directly on all querysets that will get used as a 
        # search filter.
        index.FilterField('lecture_series'),
        # We can't filter directly on a ManyToMany relationship, so we need to be a bit
        # creative. This uses the sponsor_id() method defined below to add our 
        # EventSponsors' sponsor_ids to the search index.
        index.FilterField('sponsor_id'),
    ]

    def sponsor_id(self):
        """
        Adds all of our EventSponsors' sponsor_ids to the search filter list.
        """
        return list(self.sponsors.all().values_list('sponsor_id', flat=True))


class EventSponsor(index.Indexed, models.Model):

    sponsor_id = models.IntegerField()
    name = models.CharField(max_length=255)

    events = models.ManyToManyField(Event, related_name='sponsors')

    search_fields = [
        index.SearchField('name', partial_match=True),
    ]


class LectureSeries(index.Indexed, models.Model):

    lecture_series_id = models.IntegerField(unique=True)
    name = models.CharField(max_length=255)

    search_fields = [
        index.SearchField('name', partial_match=True),
    ]


def get_base_events_queryset_for_site(site):
    """
    Returns the base queryset object from which all Event listings for a spcified Site
    must be derived.
    This function filters the list of Event objects down to just those that the Site's
    admins have chosen to display.
    """
    try:
        settings = EventListingSettings.objects.get(site=site)
    except EventListingSettings.DoesNotExist:
        # If there's no EventListingSettings for this Site, return an empty QuerySet.
        return Event.objects.none()

    # We need to do the sponsors via their sponsor_ids because searches can't be filtered
    # directly on a ManyToMany relationship.
    sponsor_ids = [sponsor.sponsor_id for sponsor in settings.event_sponsors.all()]

    # We need to split these up for Sites which import either no LectureSeries or no 
    # EventSponsors. Listings will get dupes, and searches will crash if we don't.
    if settings.lecture_series.exists() and sponsor_ids:
        queryset = Event.objects.filter(
            Q(sponsors__sponsor_id__in=sponsor_ids) | 
            Q(lecture_series__in=settings.lecture_series.all())
        )
    elif sponsor_ids:
        queryset = event_model.objects.filter(sponsors__sponsor_id__in=sponsor_ids)
    else:
        queryset = event_model.objects.filter(lecture_series__in=settings.lecture_series.all())

    return queryset

如您所见,常规外键可以正常用于过滤搜索,但多对多关系需要一些特殊的 ID 列表代码才能构建可转换为 ElasticSearch 查询的查询集。

本文收集自互联网,转载请注明来源。

如有侵权,请联系[email protected] 删除。

编辑于
0

我来说两句

0条评论
登录后参与评论

相关文章

来自分类Dev

Django如何基于ManyToManyField进行过滤?

来自分类Dev

如何在具有ManyToManyField的Django模型中过滤查询集?

来自分类Dev

如何在Elasticsearch中基于重复项的频率过滤结果

来自分类Dev

在搜索结果页面中显示过滤的结果

来自分类Dev

通过ManyToManyField Django过滤对象

来自分类Dev

如何使用 LINQ MVC 基于 AND 条件应用搜索过滤

来自分类Dev

如何仅在Laravel中获得一个搜索查询过滤器的结果

来自分类Dev

在ManyToManyField中的B之间搜索A

来自分类Dev

如何限制搜索“文本输入”结果以仅返回所选“类别”过滤器下拉列表中的结果

来自分类Dev

保存时基于 Django 模型中的 ManyToManyField 更新字段

来自分类Dev

基于数字过滤结果集

来自分类Dev

从UISearchController的过滤搜索结果中删除

来自分类Dev

在Powershell中根据CSV过滤搜索结果

来自分类Dev

搜索查询以在React中过滤结果

来自分类Dev

如何在redash中基于计数结果插入查询过滤器

来自分类Dev

如何在ManyToManyField上使用过滤器获取对象

来自分类Dev

如何基于biopython中的位置列表过滤对齐列?

来自分类Dev

如何在Django中基于子项过滤父项

来自分类Dev

如何基于子数组长度在angularJS中过滤数组

来自分类Dev

如何在MySQL中基于count的输出过滤输出?

来自分类Dev

如何在PySpark中基于数组值进行过滤?

来自分类Dev

如何基于R中的特定模式过滤DataFrame?

来自分类Dev

如何基于列名的向量过滤R中的数据帧?

来自分类Dev

如何在React中基于下拉值过滤Bootstrap表

来自分类Dev

如何在mongodb中基于输入数组过滤数据?

来自分类Dev

如何基于nativescript vue中的props值过滤数组?

来自分类Dev

如何基于Python中的其他多个列表过滤列表?

来自分类Dev

如何基于Javascript中的嵌套值过滤数组

来自分类Dev

如何基于多个索引过滤csv文件中的行?

Related 相关文章

  1. 1

    Django如何基于ManyToManyField进行过滤?

  2. 2

    如何在具有ManyToManyField的Django模型中过滤查询集?

  3. 3

    如何在Elasticsearch中基于重复项的频率过滤结果

  4. 4

    在搜索结果页面中显示过滤的结果

  5. 5

    通过ManyToManyField Django过滤对象

  6. 6

    如何使用 LINQ MVC 基于 AND 条件应用搜索过滤

  7. 7

    如何仅在Laravel中获得一个搜索查询过滤器的结果

  8. 8

    在ManyToManyField中的B之间搜索A

  9. 9

    如何限制搜索“文本输入”结果以仅返回所选“类别”过滤器下拉列表中的结果

  10. 10

    保存时基于 Django 模型中的 ManyToManyField 更新字段

  11. 11

    基于数字过滤结果集

  12. 12

    从UISearchController的过滤搜索结果中删除

  13. 13

    在Powershell中根据CSV过滤搜索结果

  14. 14

    搜索查询以在React中过滤结果

  15. 15

    如何在redash中基于计数结果插入查询过滤器

  16. 16

    如何在ManyToManyField上使用过滤器获取对象

  17. 17

    如何基于biopython中的位置列表过滤对齐列?

  18. 18

    如何在Django中基于子项过滤父项

  19. 19

    如何基于子数组长度在angularJS中过滤数组

  20. 20

    如何在MySQL中基于count的输出过滤输出?

  21. 21

    如何在PySpark中基于数组值进行过滤?

  22. 22

    如何基于R中的特定模式过滤DataFrame?

  23. 23

    如何基于列名的向量过滤R中的数据帧?

  24. 24

    如何在React中基于下拉值过滤Bootstrap表

  25. 25

    如何在mongodb中基于输入数组过滤数据?

  26. 26

    如何基于nativescript vue中的props值过滤数组?

  27. 27

    如何基于Python中的其他多个列表过滤列表?

  28. 28

    如何基于Javascript中的嵌套值过滤数组

  29. 29

    如何基于多个索引过滤csv文件中的行?

热门标签

归档