直接用 django-elasticsearch-dsl 而不是手写 requests,因为它自动处理模型与 ES 的结构同步(字段映射、信号监听、批量刷新、错误重试),而手写易遗漏细节;需独立部署 ES 并正确配置连接。

为什么直接用 django-elasticsearch-dsl 而不是手写 requests 调用?
因为 Django 模型和 Elasticsearch 索引之间需要保持结构同步(比如字段映射、更新触发、删除级联),手写 HTTP 请求容易漏掉信号监听、批量刷新、错误重试等细节,django-elasticsearch-dsl 就是专为这个场景设计的——它把模型字段自动映射成 ES 的 Text、Keyword、Date 类型,并通过 Django 信号(post_save、post_delete)自动同步数据。
注意:它不替代 ES 服务本身,只是封装了索引定义和同步逻辑。你仍需独立部署 Elasticsearch(7.x 或 8.x),并确保 ELASTICSEARCH_DSL 配置指向正确地址。
- 安装命令:
pip install django-elasticsearch-dsl - 必须在
INSTALLED_APPS中添加'django_elasticsearch_dsl' - ES 服务未启动时,
python manage.py rebuild_index会直接报错ConnectionError: Connection refused,别等到上线才发现
如何定义一个支持中文分词的 Document 类?
Django 模型本身不感知分词器,分词逻辑全在 Document 类里声明。中文搜索效果差,90% 是因为没指定 analyzer —— 默认用的是英文标准分词器,对“机器学习”会切成“机”“器”“学”“习”,完全不可用。
推荐用 ik_smart(轻量)或 ik_max_word(全切分),前提是你的 ES 已安装 IK 分词插件。否则所有中文字段都会变成单字切分。
立即学习“Python免费学习笔记(深入)”;
- 在
documents.py中定义:from django_elasticsearch_dsl import Document, fields from django_elasticsearch_dsl.registries import registry from .models import Article @registry.register_document @Document( index='articles', settings={'number_of_shards': 1, 'number_of_replicas': 0}, auto_refresh=False ) class ArticleDocument(Document): title = fields.TextField(analyzer='ik_max_word') content = fields.TextField(analyzer='ik_max_word') pub_date = fields.DateField() class Index: name = 'articles' class Django: model = Article fields = ['id'] -
auto_refresh=False是性能关键:避免每次 save 都触发 ES 刷新,适合批量导入;线上可设为True保证实时性,但会拖慢写入 - 字段名必须与模型字段一致(如模型有
title,这里也得叫title),否则同步时该字段为空
rebuild_index 报 IndexAlreadyExistsException 怎么办?
这是 ES 的正常保护机制:索引已存在且 mapping 不兼容(比如你改了某个字段类型,从 text 改成 keyword),ES 拒绝覆盖。Django 命令不会自动删旧索引再建新索引,必须手动干预。
- 先确认当前索引状态:
curl -X GET "http://localhost:9200/articles" - 强制删除旧索引:
curl -X DELETE "http://localhost:9200/articles" - 再运行:
python manage.py rebuild_index --force(--force只跳过确认提示,不解决 mapping 冲突) - 开发环境建议加
--delete参数:python manage.py rebuild_index --delete,它会先删索引再重建,省去手动 curl
搜索结果怎么按相关度排序又支持分页?
ArticleDocument.search() 返回的是 ES 的 Search 对象,不是 Django QuerySet,不能直接用 .filter() 或 .order_by()。排序和分页都得用 ES 原生语法,且要注意:ES 默认只返回前 10 条,from/size 深度分页(比如第 100 页)性能极差,超过 10000 条必须用 search_after。
- 基础相关度排序(无需额外参数):
ArticleDocument.search().query('multi_match', query='python django', fields=['title^3', 'content']) - 按时间倒序 + 相关度打分:
.sort('-pub_date', '_score') - 分页(仅适用于前 100 页):
.extra(from_=20, size=10)表示跳过前 20 条取 10 条 - 深分页替代方案:记录上一页最后一条的
pub_date和id,用.extra(search_after=[last_pub_date, last_id])
别忘了把 ES 返回的 Hit 对象转成 Django 模型实例(如果需要 ORM 功能):[hit.to_dict() for hit in search] 或用 hit._meta.django_id 查库。


















