Your Web News in One Place

Help Webnuz

Referal links:

Sign up for GreenGeeks web hosting
May 21, 2021 10:06 pm GMT

Elastic Search Django

Requirements:

Project Setup:

$ 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

project directory should look like:

 dj_elastic main  **init**.py  asgi.py  settings.py  urls.py  wsgi.py manage.py commons searches

Main app /url.py

from django.contrib import adminfrom django.urls import pathfrom django.urls.conf import includeurlpatterns = [    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"

Great, finished with basic setups....
Next, lets create models. Navigate to commons/models.py

# commons/models.pyfrom django.db import modelsfrom autoslug import AutoSlugFieldfrom django.contrib.auth.models import Userfrom 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.nameclass 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.nameclass 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}"``

Here, we:

  • Created a models ConfigChoiceCategory and ConfigChoice, where configchoice has relation with ConfigChoiceCategory.
  • And we have Address Model too

Register models to 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)

So, let's navigate to searches app and create models for hotels.

#searches/models.pyfrom commons.models import Address, ConfigChoicefrom django.db import modelsfrom django.utils.translation import gettext_lazy as _from autoslug import AutoSlugFielddef 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 = Trueclass 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.nameclass 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.nameclass 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.valueclass 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}"

Registering models to admin.py

from django.contrib import adminfrom .models import (    Hotel,    HotelImage,    HotelSpecifications,    HotelSpecificationValue,    HotelType,    HotelAddress,)class HotelSpecificationInline(admin.TabularInline):    model = [email protected](HotelType)class HotelTypeAdmin(admin.ModelAdmin):    inlines = [        HotelSpecificationInline,    ]class HotelImageInline(admin.TabularInline):    model = HotelImageclass HotelSpecificationValueInline(admin.TabularInline):    model = [email protected](Hotel)class HotelAdmin(admin.ModelAdmin):    inlines = [HotelSpecificationValueInline, HotelImageInline]admin.site.register(HotelAddress)

create a file search_indexes.py inside searches app.

#searches/search_indexes.pyfrom django.utils import timezonefrom haystack import indexesfrom .models import Hotel, HotelAddress, HotelImage, HotelSpecificationValueclass 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 Hotelclass HotelSpecIndex(indexes.SearchIndex, indexes.Indexable):    text = indexes.CharField(document=True, use_template=True)    value = indexes.CharField(model_attr="value")    def get_model(self):        return HotelSpecificationValueclass 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 HotelImageclass 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
  • Creates a unique SearchIndex for each type of Model you wish to index, though you can reuse the same SearchIndex between different models if you take care in doing so and your field names are very standardized.
  • To build a SearchIndex, all thats necessary is to subclass both indexes.SearchIndex & indexes.Indexable, define the fields you want to store data with and define a get_model method.

Serialization and views:

#searches/serializers.pyfrom drf_haystack.serializers import HaystackSerializerfrom .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.pyfrom .serializers import AggregateSerializerfrom rest_framework.mixins import ListModelMixinfrom drf_haystack.generics import HaystackGenericAPIViewclass AggregateSearchViewSet(ListModelMixin, HaystackGenericAPIView):    serializer_class = AggregateSerializer    def get(self, request, *args, **kwargs):        return self.list(request, *args, **kwargs)

so you can create a each class of serializers for each models Like this.

# searches/urls.pyfrom django.urls import pathfrom .views import AggregateSearchViewSeturlpatterns = [path("hotels/search/", AggregateSearchViewSet.as_view())]

Create a templates directory inside searches app.
Templates folder will look like this:

templates     search         indexes             searches                 hotel_text.txt                 hoteladdress_text.txt                 hotelimage_text.txt                 hotelspecificationvalue_text.txt

Finally migrate your apps, createsuperuser and add some hotels data using django admin panels.

Simply run

./manage.py rebuild_index.

Youll get some totals of how many models were processed and placed in the index.

Query time!

Now that we have a view wired up, we can start using it. By default, the HaystackGenericAPIView class is set up to use the HaystackFilter. This is the most basic filter included and can do basic search by querying any of the field included in the fields attribute on the Serializer.

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"        }    ]

GitHub logo lyamaa / elastic_search_dj_example

Examples on Django + haystack + elastic search


Original Link: https://dev.to/lymaa/elastic-search-django-g85

Share this article:    Share on Facebook
View Full Article

Dev To

An online community for sharing and discovering great ideas, having debates, and making friends

More About this Source Visit Dev To