如何集成Elasticsearch到django restful
集成ES到django restful服務(wù)端項目
如果直接在Django項目直接編寫代碼作為ElasticSearch的客戶端,比較復(fù)雜,所以借助第三方包Haystack來對接ELasticSearch的客戶端。而且使用了Haystack后,以后你換其他的全文搜索服務(wù)器時,也不用修改Django項目已經(jīng)寫好的代碼。
安裝haystack
Haystack ,Django ,Elasticsearch 三者之間的關(guān)系是:
- Haystack 作為 Django 的一個插件,提供了一個 Django 應(yīng)用接口來實現(xiàn)搜索功能。
- Elasticsearch 作為 Haystack 支持的搜索引擎之一,可以被 Haystack 用來作為后端搜索引擎來存儲和檢索數(shù)據(jù)。
- 當(dāng)你在 Django 項目中使用 Haystack 并選擇 Elasticsearch 作為搜索引擎時,Haystack 會作為中間層,讓你能夠通過 Django 的視圖和模板來操作 Elasticsearch,實現(xiàn)全文搜索的功能。
簡單來說,Haystack 為 Django 提供了搜索功能的抽象層,而 Elasticsearch 是這個抽象層背后的具體實現(xiàn)之一。通過 Haystack,你可以在 Django 項目中輕松地實現(xiàn)強(qiáng)大的搜索功能。
haystack是django的開源搜索框架,能夠結(jié)合目前市面上大部分的搜索引擎用于實現(xiàn)自定義搜索功能,特別是全文搜索。
haystack支持多種搜索引擎,不僅僅是 jieba ,whoosh,使用solr、elasticsearch等搜索,也可通過haystack,而且直接切換引擎即可,甚至無需修改搜索代碼。中文分詞最好的就是jieba和elasticsearch+ik。
github: https://github.com/rhblind/drf-haystack
# python操作elasticsearch的模塊,注意對應(yīng)版本,類似pymysql pip install -U elasticsearch==7.13.4 # django開發(fā)的haystack的模塊,務(wù)必先安裝drf`-haystack,接著才安裝django-haystack。因為drf-haystack不支持es7 pip install -U drf-haystack pip install -U django-haystack
基本使用
安裝配置
文檔:https://drf-haystack.readthedocs.io/en/latest/01_intro.html#examples
INSTALLED_APPS = [
# 必須在自己創(chuàng)建的子應(yīng)用前面
'haystack',
# 自己創(chuàng)建的子應(yīng)用
]
# haystack連接elasticsearch的配置信息
HAYSTACK_CONNECTIONS = {
'default': {
# haystack操作es的核心模塊
'ENGINE': 'haystack.backends.elasticsearch7_backend.Elasticsearch7SearchEngine',
# es服務(wù)端地址
'URL': 'http://127.0.0.1:9200/',
# es索引倉庫
'INDEX_NAME': 'haystack',
},
}
# 當(dāng)mysqlORM操作數(shù)據(jù)庫改變時,自動更新es的索引,否則es的索引會找不到新增的數(shù)據(jù)
HAYSTACK_SIGNAL_PROCESSOR = 'haystack.signals.RealtimeSignalProcessor'索引模型
在courses子應(yīng)用下創(chuàng)建search_indexes.py,用于設(shè)置es的索引模型。注意,索引模型的文件名必須是search_indexes。
- 類名必須為需要檢索的Model_name+Index
- 每個索引里面必須有且只能有一個字段為 document=True,這代表haystack 和搜索引擎將使用此字段的內(nèi)容作為索引進(jìn)行檢索(primary field)。其他的字段只是附屬的屬性,方便調(diào)用,并不作為檢索數(shù)據(jù)。
- 如果使用一個字段設(shè)置了document=True,則一般約定此字段名為text,這是在SearchIndex類里面一貫的命名,以防止后臺混亂,當(dāng)然名字你也可以隨便改,不過不建議改。
- haystack提供了use_template=True在text字段,這樣就允許我們使用數(shù)據(jù)模板去建立搜索引擎索引的文件,說得通俗點就是索引里面需要存放一些什么東西
- text字段用于構(gòu)造索引,只不過具體構(gòu)造索引的值寫在另一個文件內(nèi)。
- id、title、digest、content、image_url等字段用于以索引查詢到的返回內(nèi)容。
- get_model方法用于指明建立索引的對應(yīng)模型。
- index_queryset方法用于返回建立索引的數(shù)據(jù)查詢集。
from haystack import indexes
from .models import Course
class CourseIndex(indexes.SearchIndex, indexes.Indexable):
# 全文索引[可以根據(jù)配置,可以包括多個字段索引]
# document=True 表示當(dāng)前字段為全文索引
# use_template=True 表示接下來haystack需要加載一個固定路徑的html模板文件,讓text與其他索引字段綁定映射關(guān)系
text = indexes.CharField(document=True, use_template=True)
# 普通索引[單字段,只能提供單個字段值的搜索,所以此處的聲明更主要是為了提供給上面的text全文索引使用的]
# es索引名 = indexes.索引數(shù)據(jù)類型(model_attr="ORM中的字段名")
id = indexes.IntegerField(model_attr="id")
name = indexes.CharField(model_attr="name")
description = indexes.CharField(model_attr="description")
teacher = indexes.CharField(model_attr="teacher__name")
course_cover = indexes.CharField(model_attr="course_cover")
get_level_display=indexes.CharField(model_attr="get_level_display")
students=indexes.IntegerField(model_attr="students")
get_status_display=indexes.CharField(model_attr="get_status_display")
lessons=indexes.IntegerField(model_attr="lessons")
pub_lessons=indexes.IntegerField(model_attr="pub_lessons")
price=indexes.DecimalField(model_attr="price")
discount=indexes.CharField(model_attr="discount_json")
orders=indexes.IntegerField(model_attr="orders")
# 指定與當(dāng)前es索引模型對接的mysql的ORM模型
def get_model(self):
return Course
# 當(dāng)用戶搜索es索引時,對應(yīng)的提供的mysql數(shù)據(jù)集有哪些?
def index_queryset(self, using=None):
return self.get_model().objects.filter(is_deleted=False,is_show=True)ORM模型中新增discount_json字段方法
courses.models,代碼:
import json
class Course(BaseModel):
course_type = (
(0, '付費(fèi)購買'),
(1, '會員專享'),
(2, '學(xué)位課程'),
)
level_choices = (
(0, '初級'),
(1, '中級'),
(2, '高級'),
)
status_choices = (
(0, '上線'),
(1, '下線'),
(2, '預(yù)上線'),
)
# course_cover = models.ImageField(upload_to="course/cover", max_length=255, verbose_name="封面圖片", blank=True, null=True)
course_cover = StdImageField(variations={
'thumb_1080x608': (1080, 608), # 高清圖
'thumb_540x304': (540, 304), # 中等比例,
'thumb_108x61': (108, 61, True), # 小圖(第三個參數(shù)表示保持圖片質(zhì)量),
}, max_length=255, delete_orphans=True, upload_to="course/cover", null=True, verbose_name="封面圖片",blank=True)
course_video = models.FileField(upload_to="course/video", max_length=255, verbose_name="封面視頻", blank=True, null=True)
course_type = models.SmallIntegerField(choices=course_type,default=0, verbose_name="付費(fèi)類型")
level = models.SmallIntegerField(choices=level_choices, default=1, verbose_name="難度等級")
description = RichTextUploadingField(null=True, blank=True, verbose_name="詳情介紹")
pub_date = models.DateField(auto_now_add=True, verbose_name="發(fā)布日期")
period = models.IntegerField(default=7, verbose_name="建議學(xué)習(xí)周期(day)")
attachment_path = models.FileField(max_length=1000, blank=True, null=True, verbose_name="課件路徑")
attachment_link = models.CharField(max_length=1000, blank=True, null=True, verbose_name="課件鏈接")
status = models.SmallIntegerField(choices=status_choices, default=0, verbose_name="課程狀態(tài)")
students = models.IntegerField(default=0, verbose_name="學(xué)習(xí)人數(shù)")
lessons = models.IntegerField(default=0, verbose_name="總課時數(shù)量")
pub_lessons = models.IntegerField(default=0, verbose_name="已更新課時數(shù)量")
price = models.DecimalField(max_digits=10,decimal_places=2, verbose_name="課程原價",default=0)
recomment_home_hot = models.BooleanField(default=False, verbose_name="是否推薦到首頁新課欄目")
recomment_home_top = models.BooleanField(default=False, verbose_name="是否推薦到首頁必學(xué)欄目")
direction = models.ForeignKey("CourseDirection", related_name="course_list", on_delete=models.DO_NOTHING, null=True, blank=True, db_constraint=False, verbose_name="學(xué)習(xí)方向")
category = models.ForeignKey("CourseCategory", related_name="course_list", on_delete=models.DO_NOTHING, null=True, blank=True, db_constraint=False, verbose_name="課程分類")
teacher = models.ForeignKey("Teacher", related_name="course_list", on_delete=models.DO_NOTHING, null=True, blank=True, db_constraint=False, verbose_name="授課老師")
class Meta:
db_table = "fg_course_info"
verbose_name = "課程信息"
verbose_name_plural = verbose_name
def __str__(self):
return "%s" % self.name
def course_cover_small(self):
if self.course_cover:
return mark_safe(f'<img style="border-radius: 0%;" src="{self.course_cover.thumb_108x61.url}">')
return ""
course_cover_small.short_description = "封面圖片(108x61)"
course_cover_small.allow_tags = True
course_cover_small.admin_order_field = "course_cover"
def course_cover_medium(self):
if self.course_cover:
return mark_safe(f'<img style="border-radius: 0%;" src="{self.course_cover.thumb_540x304.url}">')
return ""
course_cover_medium.short_description = "封面圖片(540x304)"
course_cover_medium.allow_tags = True
course_cover_medium.admin_order_field = "course_cover"
def course_cover_large(self):
if self.course_cover:
return mark_safe(f'<img style="border-radius: 0%;" src="{self.course_cover.thumb_1080x608.url}">')
return ""
course_cover_large.short_description = "封面圖片(1080x608)"
course_cover_large.allow_tags = True
course_cover_large.admin_order_field = "course_cover"
@property
def discount(self):
# todo 將來通過計算獲取當(dāng)前課程的折扣優(yōu)惠相關(guān)的信息
import random
return {
"type": ["限時優(yōu)惠","限時減免"].pop(random.randint(0,1)), # 優(yōu)惠類型
"expire": random.randint(100000, 1200000), # 優(yōu)惠倒計時
"price": float(self.price - random.randint(1,10) * 10), # 優(yōu)惠價格
}
def discount_json(self):
# 必須轉(zhuǎn)成字符串才能保存到es中。所以該方法提供給es使用的。
return json.dumps(self.discount)全文索引字段模板
全文索引模板必須先配置django項目中的TEMPLATES模板引擎路徑,而且全文索引模板的路徑必須是模板目錄下的search/indexes/子應(yīng)用目錄名/模型類名_text.txt。否則報錯。settings.dev,代碼:
TEMPLATES = [
{
'BACKEND': 'django.template.backends.django.DjangoTemplates',
'DIRS': [
BASE_DIR / "templates", # BASE_DIR 是apps的父級目錄,是主應(yīng)用目錄,templates需要手動創(chuàng)建
],
'APP_DIRS': True,
'OPTIONS': {
'context_processors': [
'django.template.context_processors.debug',
'django.template.context_processors.request',
'django.contrib.auth.context_processors.auth',
'django.contrib.messages.context_processors.messages',
],
},
},
]創(chuàng)建全文索引字段的html模板,在HTML模板中采用django的模板語法,綁定text與其他es單字段索引的映射關(guān)系。
注意:course_text.txt 中course就是ORM模型類名小寫,text就是es索引模型類中的全文索引字段名。
templates/search/indexes/courses/course_text.txt。代碼:
{{ object.name }}
{{ object.description }}
{{ object.teacher.name }}
{{ object.category.name }}
{{ object.diretion.name }}object表示當(dāng)前orm的模型對應(yīng)。


索引序列化器
courses.serializers,代碼:
from drf_haystack.serializers import HaystackSerializer
from .search_indexes import CourseIndex
from django.conf import settings
class CourseIndexHaystackSerializer(HaystackSerializer):
"""課程搜索的序列化器"""
class Meta:
index_classes = [CourseIndex]
fields = ["text", "id", "name", "course_cover", "get_level_display", "students", "get_status_display", "pub_lessons", "price", "discount", "orders"]
def to_representation(self, instance):
"""用于指定返回數(shù)據(jù)的字段的"""
# 課程的圖片,在這里通過elasticsearch提供的,所以不會提供圖片地址左邊的域名的。因此在這里手動拼接
instance.course_cover = f'//{settings.OSS_BUCKET_NAME}.{settings.OSS_ENDPOINT}/uploads/{instance.course_cover}'
return super().to_representation(instance)全文搜索的索引視圖
from drf_haystack.viewsets import HaystackViewSet
from drf_haystack.filters import HaystackFilter
from .serializers import CourseIndexHaystackSerializer
from .models import Course
class CourseSearchViewSet(HaystackViewSet):
"""課程信息全文搜索視圖類"""
# 指定本次搜索的最終真實數(shù)據(jù)的保存模型
index_models = [Course]
serializer_class = CourseIndexHaystackSerializer
filter_backends = [OrderingFilter, HaystackFilter]
ordering_fields = ('id', 'students', 'orders')
pagination_class = CourseListPageNumberPagination路由
from django.urls import path,re_path
from . import views
from rest_framework import routers
router = routers.DefaultRouter()
# 注冊全文搜索到視圖集中生成url路由信息
router.register("search", views.CourseSearchViewSet, basename="course-search")
urlpatterns = [
path("directions/", views.CourseDirectionListAPIView.as_view()),
re_path("^categories/(?P<direction>\d+)/$", views.CourseCategoryListAPIView.as_view()),
re_path("^(?P<direction>\d+)/(?P<category>\d+)/$", views.CourseListAPIView.as_view()),
] + router.urls手動構(gòu)建es索引
因為此前mysql中已經(jīng)有了部分的數(shù)據(jù),而這部分?jǐn)?shù)據(jù)在es中是沒有創(chuàng)建索引。所以需要先把之前的數(shù)據(jù)同步生成全文索引。在終端下執(zhí)行以下命令
# 重建索引 python manage.py rebuild_index # 更新索引 # python manage.py update_index --age=<num_hours> # 刪除索引 # python manage.py clear_index
訪問
http://api.fuguang.cn:8000/courses/search/?text=入門
http://api.fuguang.cn:8000/courses/search/?text=李老師

到此這篇關(guān)于集成Elasticsearch到django restful的文章就介紹到這了,更多相關(guān)Elasticsearch集成django restful內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
聯(lián)邦學(xué)習(xí)論文解讀分散數(shù)據(jù)的深層網(wǎng)絡(luò)通信
這篇文章主要為大家介紹了論文解讀分散數(shù)據(jù)的深層網(wǎng)絡(luò)通信有效學(xué)習(xí),有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2022-05-05
HarmonyOS系統(tǒng)利用AVPlayer開發(fā)視頻播放功能
AVPlayer是HarmonyOS提供的一個高級音視頻播放接口,整合流媒體解析、本地資源處理、媒體解封裝、視頻解碼和渲染等功能,本文將詳細(xì)介紹如何在HarmonyOS系統(tǒng)中利用功能強(qiáng)大的AVPlayer來開發(fā)視頻播放功能,通過一個完整的示例程序,帶您從零到一實現(xiàn)端到端的視頻播放解決方案2025-04-04
如何在本地部署DeepSeek大模型實現(xiàn)聯(lián)網(wǎng)增強(qiáng)的AI應(yīng)用
本文介紹了如何使用Microsoft?SemanticKernel框架結(jié)合DeepSeek本地模型和自定義搜索技能,構(gòu)建一個具備聯(lián)網(wǎng)增強(qiáng)能力的智能應(yīng)用,文章還提供了一個應(yīng)用場景示例,說明如何在本地部署DeepSeek大模型并實現(xiàn)聯(lián)網(wǎng)增強(qiáng)的AI應(yīng)用,感興趣的朋友一起看看吧2025-02-02
在Windows系統(tǒng)上安裝Cygwin搭建Swoole測試環(huán)境的圖文教程
這篇文章主要介紹了在Windows系統(tǒng)上安裝Cygwin搭建Swoole測試環(huán)境的方法,本文通過圖文并茂的形式給大家介紹的非常詳細(xì),對大家的學(xué)習(xí)或工作具有一定的參考借鑒價值,需要的朋友可以參考下2020-05-05
趁熱打鐵!HTTPGet與HTTPPost的區(qū)別詳解
這篇文章主要介紹了趁熱打鐵!HTTPGet與HTTPPost的區(qū)別詳解,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2020-10-10
git push & git pull 推送/拉取分支的具體使用
這篇文章主要介紹了git push & git pull 推送/拉取分支的具體使用,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2020-08-08

