在 wagtail 中索引自定义 Django 模型字段

indexing custom Django model fields in wagtail

我们正在使用 Django MarkupField 来存储 Markdown 文本,而且效果很好。

然而,当我们尝试在 Wagtail 中索引这些字段时,我们从 Elasticsearch 中收到序列化错误,如下所示:

File "/usr/local/lib/python3.5/dist-packages/wagtail/wagtailsearch/management/commands/update_index.py", line 120, in handle
  self.update_backend(backend_name, schema_only=options.get('schema_only', False))
File "/usr/local/lib/python3.5/dist-packages/wagtail/wagtailsearch/management/commands/update_index.py", line 87, in update_backend
  index.add_items(model, chunk)
File "/usr/local/lib/python3.5/dist-packages/wagtail/wagtailsearch/backends/elasticsearch.py", line 579, in add_items
  bulk(self.es, actions)
File "/usr/local/lib/python3.5/dist-packages/elasticsearch/helpers/__init__.py", line 195, in bulk
  for ok, item in streaming_bulk(client, actions, **kwargs):
File "/usr/local/lib/python3.5/dist-packages/elasticsearch/helpers/__init__.py", line 162, in streaming_bulk
  for bulk_actions in _chunk_actions(actions, chunk_size, max_chunk_bytes, client.transport.serializer):
File "/usr/local/lib/python3.5/dist-packages/elasticsearch/helpers/__init__.py", line 61, in _chunk_actions
  data = serializer.dumps(data)
File "/usr/local/lib/python3.5/dist-packages/elasticsearch/serializer.py", line 50, in dumps
  raise SerializationError(data, e)
elasticsearch.exceptions.SerializationError: ({'_partials': [<markupfield.fields.Markup object at 0x7faa6e238e80>, <markupfield.fields.Markup object at 0x7faa6dbc4da0>], 'pk': '1', 'research_interests': <markupfield.fields.Markup object at 0x7faa6e238e80>, 'bio': <markupfield.fields.Markup object at 0x7faa6dbc4da0>}, TypeError("Unable to serialize <markupfield.fields.Markup object at 0x7faa6e238e80> (type: <class 'markupfield.fields.Markup'>)",))

一种解决方法是索引 return field.raw 的可调用项,但随后我们必须为模型中的每个 Markdown 字段 属性 编写一个这样的可调用项。我认为我们可以通过使用 get_searchable_content(value) 方法扩展字段 属性(即替换 MarkupField 的 django-markupfield Markup class)来解决这个问题但序列化错误仍然存​​在。

有人对在 Wagtail + elasticsearch 中索引自定义 Django 字段有任何提示吗?

有几种方法可以做到这一点。最好的办法是在 elasticsearch-dsl 中创建您自己的字段,例如参见 (0),并将其用于(反)序列化。另一种选择是创建自己的 JSONSerializer (1) 子类,并将其作为 serializer=MyJSONSerializer() 传递到 Elasticsearch 构造函数中,它可以处理 markupfield.fields.Markup 对象。

0 - https://github.com/elastic/elasticsearch-dsl-py/blob/master/test_elasticsearch_dsl/test_document.py#L49-L58 1 - https://github.com/elastic/elasticsearch-py/blob/master/elasticsearch/serializer.py#L24

我把 get_searchable_content 放在了错误的地方,我认为 Markup class 需要它,但它需要放在 Django 模型 Field class 本身。然后 Wagtail 将提取适当的值以在 elasticsearch(或任何其他搜索后端)中建立索引。

最直接的解决方案是使用自定义字段 class 扩展 MarkupField 并添加 get_searchable_content(self, value) that delegates its implementation to MarkupField.get_prep_value