- Джанго
- Эластичный поиск Установить (Требуемая версия 6)
- DRF HAYSTACK
- Поэзия Установить или вы можете использовать PIP или Pipenv
$ mkdir dj_elastic && cd dj_elastic $ python3 -m venv env $ source env/bin/activate $ poetry init $ poetry add django djangorestframework django-autoslug black isort $ poetry add django-haystack drf-haystack $ poetry add elasticsearch==^5.0 $ django-admin.py startproject main $ python manage.py startapp searches $ python manage.py startapp commons
Справочник проекта должен выглядеть как:
── dj_elastic ├── main │ ├── **init**.py │ ├── asgi.py │ ├── settings.py │ ├── urls.py │ └── wsgi.py ├── manage.py └── commons └── searches
Основное приложение/url.py
from django.contrib import admin from django.urls import path from django.urls.conf import include urlpatterns = [ path("admin/", admin.site.urls), path("api/v1/", include("searches.urls")), ]
main/settings.py
INSTALLED_APPS = [ "searches", "commons", "haystack", "rest_framework", ] TEMPLATES = [ { "BACKEND": "django.template.backends.django.DjangoTemplates", "DIRS": [BASE_DIR / "templates"], "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", ], }, }, ] HAYSTACK_CONNECTIONS = { "default": { "ENGINE": "haystack.backends.elasticsearch5_backend.Elasticsearch5SearchEngine", "URL": "http://127.0.0.1:9200/", "INDEX_NAME": "haystack", }, } HAYSTACK_SIGNAL_PROCESSOR = "haystack.signals.RealtimeSignalProcessor"
👏🏻 Отлично, закончено с основными настройками …. Далее давайте создадим модели. Перейдите к Commons/Models.py
# commons/models.py from django.db import models from autoslug import AutoSlugField from django.contrib.auth.models import User from django.utils.translation import gettext_lazy as _ def slugify(value): return value.replace(" ", "-").lower() class ConfigChoiceCategory(models.Model): name = models.CharField( _("Config Choice Category Name"), help_text=_("Required and Unique"), max_length=255, unique=True, ) slug = AutoSlugField( verbose_name=_("Config Choice Category Slug"), populate_from="name", slugify=slugify, ) entered_by = models.ForeignKey(User, blank=True, on_delete=models.CASCADE) is_active = models.BooleanField(default=True) class Meta: verbose_name = _("Config Choice Category") verbose_name_plural = _(" Config Choice Categories") def __str__(self): return self.name class ConfigChoice(models.Model): name = models.CharField( _("Config Choice Name"), help_text=_("Required and Unique"), max_length=255, unique=True, ) description = models.TextField() slug = AutoSlugField( verbose_name=_("Config Choice Slug"), populate_from="name", slugify=slugify, ) config_choice_category = models.ForeignKey( ConfigChoiceCategory, on_delete=models.CASCADE ) entered_by = models.ForeignKey(User, on_delete=models.CASCADE) class Meta: verbose_name = _("Config Choice") verbose_name_plural = _("Config Choices") def __str__(self) -> str: return self.name class Address(models.Model): street_1 = models.CharField(max_length=200) street_2 = models.CharField(max_length=200, null=True, blank=True) city = models.CharField(max_length=100) state = models.CharField(max_length=100) zip_code = models.CharField(max_length=100) country = models.CharField(max_length=50) latitude = models.FloatField() longitude = models.FloatField() def __str__(self): return f"{self.street_1}, {self.city}, {self.state}, {self.country}"``
Мы тут:
- Создал модели Configchoicecategory и Configchoice , где configchoice имеет отношение с Configchoicecategory Анкет
- И у нас есть Адресная модель слишком
Зарегистрировать модели на admin.py
from django.contrib import admin # Register your models here. from .models import ( Address, ConfigChoice, ConfigChoiceCategory, ) admin.site.register(ConfigChoiceCategory) admin.site.register(ConfigChoice) admin.site.register(Address)
Итак, давайте перейдем к Поиск Приложение и создайте модели для отелей.
#searches/models.py from commons.models import Address, ConfigChoice from django.db import models from django.utils.translation import gettext_lazy as _ from autoslug import AutoSlugField def slugify(value): return value.replace(" ", "-").lower() class CoreModel(models.Model): created = models.DateTimeField(auto_now_add=True) modified = models.DateTimeField(auto_now=True) class Meta: abstract = True class HotelType(models.Model): name = models.CharField(_("Hotel Types Name"), max_length=255) class Meta: verbose_name = _("Hotel Type") verbose_name_plural = _("Hotel Types") def __str__(self) -> str: return self.name class HotelSpecifications(models.Model): hotel_type = models.ForeignKey(HotelType, on_delete=models.RESTRICT) name = models.CharField(_("Hotel Spec Name"), max_length=255) class Meta: verbose_name = _("Hotel Specification") verbose_name_plural = _("Hotel Specifications") def __str__(self) -> str: return f"{self.name}" class Hotel(CoreModel): name = models.CharField(_("Hotel Name"), max_length=50) description = models.TextField(_("Hotel Descriptions"), default="") hotel_type = models.ForeignKey(HotelType, on_delete=models.CASCADE) slug = AutoSlugField( verbose_name=_("Hotel Slug"), populate_from="name", slugify=slugify, ) is_active = models.BooleanField(default=True) config_choice = models.ForeignKey(ConfigChoice, on_delete=models.RESTRICT) class Meta: verbose_name = _("Hotel") verbose_name_plural = _("Hotels") def get_absolute_url(self): return f"/{self.slug}/" def __str__(self) -> str: return self.name class HotelSpecificationValue(models.Model): hotel = models.ForeignKey(Hotel, on_delete=models.CASCADE) specification = models.ForeignKey(HotelSpecifications, on_delete=models.RESTRICT) value = models.CharField( _("Value"), max_length=255, help_text=_("Hotel specification value (maximum of 255 words"), ) class Meta: verbose_name = _("Hotel Specification Value") verbose_name_plural = _("Hotel Specification Values") def __str__(self): return self.value class HotelImage(CoreModel): hotel = models.ForeignKey( Hotel, on_delete=models.CASCADE, related_name="hotel_image" ) image_urls = models.URLField( _("Hotel Image URLs"), help_text=_("Images Urls"), ) caption = models.CharField( verbose_name=_("Alternative text"), help_text=_("Please add alturnative text"), max_length=255, null=True, blank=True, ) is_feature = models.BooleanField(default=False) class Meta: verbose_name = _("Hotel Image") verbose_name_plural = _("Hotel Images") class HotelAddress(models.Model): hotel = models.ForeignKey( Hotel, on_delete=models.CASCADE, related_name="hotel_address" ) address = models.ForeignKey(Address, on_delete=models.CASCADE) def __str__(self): return f"{self.hotel.name} {self.address.city}"
Регистрация моделей в admin.py
from django.contrib import admin from .models import ( Hotel, HotelImage, HotelSpecifications, HotelSpecificationValue, HotelType, HotelAddress, ) class HotelSpecificationInline(admin.TabularInline): model = HotelSpecifications @admin.register(HotelType) class HotelTypeAdmin(admin.ModelAdmin): inlines = [ HotelSpecificationInline, ] class HotelImageInline(admin.TabularInline): model = HotelImage class HotelSpecificationValueInline(admin.TabularInline): model = HotelSpecificationValue @admin.register(Hotel) class HotelAdmin(admin.ModelAdmin): inlines = [HotelSpecificationValueInline, HotelImageInline] admin.site.register(HotelAddress)
Создайте файл search_indexes.py Inside Searchs App.
#searches/search_indexes.py from django.utils import timezone from haystack import indexes from .models import Hotel, HotelAddress, HotelImage, HotelSpecificationValue class HotelIndex(indexes.SearchIndex, indexes.Indexable): text = indexes.CharField(document=True, use_template=True) name = indexes.CharField(model_attr="name") hotel_type = indexes.CharField(model_attr="hotel_type") config_choice = indexes.CharField(model_attr="config_choice") autocomplete = indexes.EdgeNgramField() @staticmethod def prepare_autocomplete(obj): return " ".join((obj.name, obj.hotel_type.name, obj.config_choice.name)) def get_model(self): return Hotel class HotelSpecIndex(indexes.SearchIndex, indexes.Indexable): text = indexes.CharField(document=True, use_template=True) value = indexes.CharField(model_attr="value") def get_model(self): return HotelSpecificationValue class HotelImageIndex(indexes.SearchIndex, indexes.Indexable): text = indexes.CharField(document=True, use_template=True) image_urls = indexes.CharField(model_attr="image_urls") caption = indexes.CharField(model_attr="caption") def get_model(self): return HotelImage class HotelAddressIndex(indexes.SearchIndex, indexes.Indexable): text = indexes.CharField(document=True, use_template=True) address = indexes.CharField(model_attr="address") def get_model(self): return HotelAddress
- Создает уникальный SearchIndex Для каждого типа модели вы хотите индексировать, хотя вы можете повторно использовать один и тот же SearchIndex между различными моделями, если вы позаботитесь при этом, и имена ваших поля очень стандартизированы.
- Построить SearchIndex , все, что необходимо, – это подкласс оба индексы. SearchIndex & индексы. Индексируемый , Определите поля, с которыми вы хотите сохранить данные, и определите метод GET_MODEL.
Сериализация и взгляды:
#searches/serializers.py from drf_haystack.serializers import HaystackSerializer from .search_indexes import ( HotelIndex, HotelSpecIndex, HotelImageIndex, HotelAddressIndex, ) class AggregateSerializer(HaystackSerializer): class Meta: index_classes = [HotelIndex, HotelSpecIndex, HotelImageIndex, HotelAddressIndex] fields = [ "name", "hotel", "config_choice", "value", "image_urls", "caption", "address", "autocomplete", ]
# searches/serializers.py from .serializers import AggregateSerializer from rest_framework.mixins import ListModelMixin from drf_haystack.generics import HaystackGenericAPIView class AggregateSearchViewSet(ListModelMixin, HaystackGenericAPIView): serializer_class = AggregateSerializer def get(self, request, *args, **kwargs): return self.list(request, *args, **kwargs)
Таким образом, вы можете создать каждый класс сериалов для каждой модели Как это Анкет
# searches/urls.py from django.urls import path from .views import AggregateSearchViewSet urlpatterns = [ path("hotels/search/", AggregateSearchViewSet.as_view()) ]
Создайте каталог шаблонов внутри приложения Searches. Папка шаблонов будет выглядеть так:
templates ├── search ├── indexes ├── searches ├── hotel_text.txt ├── hoteladdress_text.txt ├── hotelimage_text.txt ├── hotelspecificationvalue_text.txt
Наконец мигрировать Ваши приложения, CreateSuperuser и добавьте данные о отелях, используя панели администратора Django.
Просто беги ./manage.py Rebuild_index.
Вы получите некоторые итоги того, сколько моделей было обработано и помещено в индекс.
Теперь, когда у нас есть представление, мы можем начать его использовать. По умолчанию класс Haystackgenericapiview настроен на использование Haystackfilter. Это самый базовый фильтр, включенный и может выполнять базовый поиск, запрашивая любое поле, включенное в атрибут поля на сериализаторе.
http://127.0.0.1:8000/api/v1/hotels/search/ [ { "image_urls": "https://images.moviesanywhere.com/8ccb2868a61ac0612d780eb3b18e5220/6fda2dc9-a774-4ba6-9e80-679accfcc8ed.jpg?h=375&resize=fit&w=250", "caption": "img" }, { "name": "Transylvania Hotal", "config_choice": "Active", "autocomplete": "Transylvania Hotal 3 Star Active" }, { "value": "12 AD" }, { "value": "Monsters Hotel" }, { "address": "US" }, { "value": "12 AD" }, { "value": "gogogog" }, { "image_urls": "https://images.moviesanywhere.com/8ccb2868a61ac0612d780eb3b18e5220/6fda2dc9-a774-4ba6-9e80-679accfcc8ed.jpg?h=375&resize=fit&w=250", "caption": "img" }, { "value": "lONG LONG TIME AGO" }, { "name": "demo", "config_choice": "Active", "autocomplete": "demo 3 Star Active" }, { "value": "lONG LONG TIME AGO" } ] http://127.0.0.1:8000/api/v1/hotels/search/?name="demo" "results": [ { "name": "demo", "config_choice": "Active", "autocomplete": "demo 3 Star Active" } ]
lyamaa/elastic_search_dj_example
Примеры Django + Hay Stack + Elastic Search
Оригинал: “https://dev.to/lymaa/elastic-search-django-g85”