<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Lyamaa</title>
    <description>The latest articles on DEV Community by Lyamaa (@lyamaa).</description>
    <link>https://dev.to/lyamaa</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F476003%2Fb2409ab6-1f5e-4981-86fa-389b7611ff11.jpeg</url>
      <title>DEV Community: Lyamaa</title>
      <link>https://dev.to/lyamaa</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/lyamaa"/>
    <language>en</language>
    <item>
      <title>Algolia For Django</title>
      <dc:creator>Lyamaa</dc:creator>
      <pubDate>Sat, 11 Jun 2022 20:38:29 +0000</pubDate>
      <link>https://dev.to/lyamaa/algolia-for-django-1mf2</link>
      <guid>https://dev.to/lyamaa/algolia-for-django-1mf2</guid>
      <description>&lt;p&gt;&lt;a href="https://www.algolia.com/"&gt;&lt;strong&gt;Algolia&lt;/strong&gt;&lt;/a&gt; is a hosted search engine, offering full-text, numerical, and faceted search, capable of delivering real-time results from the first keystroke. Algolia's powerful API lets you quickly and seamlessly implement search within your websites and mobile applications. Our search API powers billions of queries for thousands of companies every month, delivering relevant results in under 100ms anywhere in the world.&lt;/p&gt;

&lt;h3&gt;
  
  
  Resources
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://www.algolia.com/doc/framework-integration/django/setup/?client=python"&gt;Algolia Django&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/algolia/algoliasearch-django"&gt;Algolia Repo&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Well&lt;/strong&gt; before continuing, i believe most of you are fimailiar with &lt;code&gt;django&lt;/code&gt;, and won't be explaining much of it.&lt;/p&gt;

&lt;p&gt;Demo:&lt;br&gt;
&lt;/p&gt;
&lt;div class="crayons-card c-embed text-styles text-styles--secondary"&gt;
    &lt;a href="https://algolia-search-frontend.vercel.app/" rel="noopener noreferrer"&gt;
      algolia-search-frontend.vercel.app
    &lt;/a&gt;
&lt;/div&gt;


&lt;h3&gt;
  
  
  Setup
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;create a project or clone from &lt;a href="https://github.com/lyamaa/algolia_search_backends"&gt;github&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;create Virtualenv.
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;virtualenv venv
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;ul&gt;
&lt;li&gt;create requirements.txt
&lt;/li&gt;
&lt;/ul&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Django==4.0.5
django-taggit==3.0.0
django-treebeard==4.5.1
djangorestframework==3.13.1
algoliasearch-django==2.0.0
django-environ==0.8.1
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;ul&gt;
&lt;li&gt;create a new app &lt;code&gt;algolia_search&lt;/code&gt; in the project
&lt;/li&gt;
&lt;/ul&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;python manage.py startapp algolia_search
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;ul&gt;
&lt;li&gt;create &lt;code&gt;.env&lt;/code&gt; file
&lt;/li&gt;
&lt;/ul&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;DJANGO_SUPERUSER_PASSWORD=root
DJANGO_SUPERUSER_USERNAME=root
DJANGO_SUPERUSER_EMAIL=root@root.com

APPLICATION_ID=
ADMIN_API_KEY=
SEARCH_ONLY_API_KEY=
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;h3&gt;
  
  
  Config Settings
&lt;/h3&gt;

&lt;p&gt;configure the &lt;code&gt;settings.py&lt;/code&gt; file&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import os
import environ
from pathlib import Path

# Build paths inside the project like this: BASE_DIR / 'subdir'.
BASE_DIR = Path(__file__).resolve().parent.parent

env = environ.Env()
environ.Env.read_env(os.path.join(BASE_DIR, ".env"))

INSTALLED_APPS = [

    # *** 3rd party apps ***
    "rest_framework",
    "rest_framework.authtoken",
    "treebeard",
    "taggit",
    "algoliasearch_django",
    # ** apps **
    "algolia_search
]

ALGOLIA = {
    "APPLICATION_ID": os.environ.get("APPLICATION_ID"),
    "API_KEY": os.environ.get("ADMIN_API_KEY"),
    "SEARCH_API_KEY": os.environ.get("SEARCH_ONLY_API_KEY"),
}

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;h3&gt;
  
  
  Creating Models
&lt;/h3&gt;


&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# algolia_search/models.py

from this import s
from unicodedata import category
from django.db import models
from django.db.models import Manager, QuerySet
from taggit.managers import TaggableManager
from treebeard.mp_tree import MP_Node

from django.contrib.auth import get_user_model

User = get_user_model()


class TimeStamp(models.Model):
    created_at = models.DateTimeField(auto_now_add=True)
    update_at = models.DateTimeField(auto_now=True)

    class Meta:
        abstract = True


class Category(MP_Node):
    class Meta:
        db_table = "categories"
        verbose_name_plural = "Categories"

    name = models.CharField(max_length=30)

    node_order_by = ["name"]

    def __str__(self):
        return f"Category: {self.name}"


class ArticleQuerySet(QuerySet):
    def update(self, **kwargs):
        super(ArticleQuerySet, self).update(**kwargs)


class CustomManager(Manager):
    def get_queryset(self):
        return ArticleQuerySet(self.model, using=self._db)


class ArticleLike(TimeStamp):
    class Meta:
        db_table = "article_like"
        unique_together = ("user", "article")

    user = models.ForeignKey(User, on_delete=models.CASCADE)
    article = models.ForeignKey(
        "Article", on_delete=models.CASCADE, related_name="likes_article"
    )


class Article(TimeStamp):
    class Meta:
        db_table = "articles"

    category = models.ForeignKey(Category, on_delete=models.SET_NULL, null=True)
    title = models.CharField(max_length=255)
    description = models.TextField(null=True, blank=True)
    is_published = models.BooleanField(default=False)
    tags = TaggableManager()
    likes = models.ManyToManyField(
        User, related_name="user_likes_article", through=ArticleLike, blank=True
    )

    objects = CustomManager()

    def __str__(self):
        return self.title

    def is_published_indexing(self):
        return self.is_published == True

    @property
    def likes_count(self):
        return int(self.likes.count())

    @property
    def tags_indexing(self):
        return [tag.name for tag in self.tags.all()]

    @property
    def category_indexing(self):
        return list(self.category.get_descendants())
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;h3&gt;
  
  
  Modify &lt;code&gt;admin.py&lt;/code&gt;
&lt;/h3&gt;


&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;from django.contrib import admin
from django.contrib.auth.admin import UserAdmin
from django.contrib.auth.models import User
from django.db import models
from django.utils.safestring import mark_safe
from treebeard.admin import TreeAdmin
from treebeard.forms import movenodeform_factory

from algolia_search.models import Article, Category, ArticleLike


class UserAdmin(UserAdmin):
    model = User
    list_display = (
        "username",
        "email",
        "is_staff",
        "is_active",
    )
    list_filters = (
        "username",
        "email",
        "is_staff",
        "is_active",
    )
    fieldsets = (
        (None, {"fields": ("username", "email", "password")}),
        (
            "Permissions",
            {
                "fields": (
                    "is_staff",
                    (
                        "is_active",
                        "is_superuser",
                    ),
                )
            },
        ),
        ("Important dates", {"fields": ("last_login", "date_joined")}),
        ("Advanced options", {"classes": ("collapse",), "fields": ("groups",)}),
    )
    add_fieldsets = (
        (
            None,
            {
                "classes": ("wide",),
                "fields": (
                    "username",
                    "email",
                    "password1",
                    "password2",
                    "is_staff",
                    "is_active",
                    "is_superuser",
                    "groups",
                ),
            },
        ),
    )


admin.site.unregister(User)
admin.site.register(User, UserAdmin)


class CategoryAdmin(TreeAdmin):
    form = movenodeform_factory(Category)


admin.site.register(Category, CategoryAdmin)


class ArticleLikeInline(admin.TabularInline):
    model = ArticleLike
    extra = 0


class ArticleAdmin(admin.ModelAdmin):
    list_display = [
        "id",
        "title",
        "get_tags",
        "likes_count",
        "is_published",
    ]
    search_fields = ["id", "title"]

    def get_tags(self, article):
        tags = [str(tag) for tag in article.tags.all()]
        return ", ".join(tags)

    inlines = [
        ArticleLikeInline,
    ]


admin.site.register(Article, ArticleAdmin)

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;h3&gt;
  
  
  Create &lt;code&gt;index.py&lt;/code&gt; file.
&lt;/h3&gt;


&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# algolia_search/index.py

import algoliasearch_django as algoliasearch
from algoliasearch_django import AlgoliaIndex

from .models import Article


class ArticleIndex(AlgoliaIndex):
    """Index for Article model"""

    # ** fields to index if is_published is True
    name = "article"
    should_index = "is_published_indexing"
    fields = (
        "title",
        "description",
        "is_published",
        "tags_indexing",
        "category_indexing",
        "likes_count",
    )
    settings = {
        "searchableAttributes": [
            "title",
            "description",
            "tags_indexing",
            "category_indexing",
        ],
        "attributesForFaceting": [
            "title",
            "tags_indexing",
            "description",
            "category_indexing",
        ],
        "queryType": "prefixAll",
        # ** custom ranking rules with like_count
        "customRanking": [
            "desc(likes_count)",
        ],
        "advancedSyntax": True,
        "highlightPreTag": "&amp;lt;mark&amp;gt;",
        "highlightPostTag": "&amp;lt;/mark&amp;gt;",
        "hitsPerPage": 15,
    }

    index_name = "articles"

algoliasearch.register(Article, ArticleIndex)

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;ul&gt;
&lt;li&gt;Above we created &lt;code&gt;ArticleIndex&lt;/code&gt; class for indexing.
&lt;/li&gt;
&lt;/ul&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt; should_index = "is_published_indexing"
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;ul&gt;
&lt;li&gt;here &lt;code&gt;should_index&lt;/code&gt; attribute helps to index &lt;code&gt;Article&lt;/code&gt; object which &lt;code&gt;is_published&lt;/code&gt; is true. So, if it is false no objects are indexed.
&lt;/li&gt;
&lt;/ul&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;fields = (
        "title",
        "description",
        "is_published",
        "category_indexing",
        "tags_indexing",
        "likes_count",
    )
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;ul&gt;
&lt;li&gt;Simply, it is just defining out fields from &lt;code&gt;article&lt;/code&gt; models.
&lt;/li&gt;
&lt;/ul&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;settings = {
        "searchableAttributes": [
            "title",
            "description",
            "tags_indexing",
            "category_indexing",
        ],
        "attributesForFaceting": [
            "title",
            "tags_indexing",
            "description",
            "category_indexing",
        ],
        "queryType": "prefixAll",
        # ** custom ranking rules with like_count
        "customRanking": [
            "desc(likes_count)",
        ],
        "advancedSyntax": True,
        "highlightPreTag": "&amp;lt;mark&amp;gt;",
        "highlightPostTag": "&amp;lt;/mark&amp;gt;",
        "hitsPerPage": 15,
    }
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;in setting options:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;searchableAttributes&lt;/strong&gt;: The complete list of attributes used for searching.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;attributesForFaceting&lt;/strong&gt;: The complete list of attributes that will be used for faceting.

&lt;ul&gt;
&lt;li&gt;to turn an attribute into a facet&lt;/li&gt;
&lt;li&gt;to make any string attribute filterable.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;queryType&lt;/strong&gt;: Controls if and how query words are interpreted as prefixes.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;prefixAll&lt;/strong&gt;: All query words are interpreted as prefixes. This option is not recommended, as it tends to yield counterintuitive results and has a negative impact on performance.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;prefixLast&lt;/strong&gt;: Only the last word is interpreted as a prefix (default behavior).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;prefixNone&lt;/strong&gt;: No query word is interpreted as a prefix. This option is not recommended, especially in an instant search setup, as the user will have to type the entire word(s) before getting any relevant results.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;customRanking&lt;/strong&gt;: Specifies the custom ranking criterion.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Modifiers:#&lt;/li&gt;
&lt;li&gt;asc: Sort by increasing value of the attribute.&lt;/li&gt;
&lt;li&gt;desc: Sort by decreasing value of the attribute.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;blockquote&gt;
&lt;p&gt;Note: we have our custom rank according to like count of &lt;code&gt;articles&lt;/code&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;advancedSyntax&lt;/strong&gt;: Enables the advanced query syntax.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;This advanced syntax brings two additional features:&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Phrase query&lt;/strong&gt;: a specific sequence of terms that must be matched next to one another. A phrase query needs to be surrounded by double quotes ("). For example, the query "search engine" only returns a record if it contains “search engine” exactly in at least one attribute.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;blockquote&gt;
&lt;p&gt;Note: Typo tolerance is disabled inside the phrase (i.e. within the quotes).&lt;/p&gt;
&lt;/blockquote&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Prohibit operator&lt;/strong&gt;: excludes records that contain a specific term. To exclude a term, you need to prefix it with a minus (-). The engine only interprets the minus (-) as a prohibit operator when you place it at the start of a word. A minus (-) within double quotes (") is not treated as a prohibit operator.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;highlightPreTag&lt;/strong&gt;: The HTML string to insert before the highlighted parts in all highlight and snippet results.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;highlightPostTag&lt;/strong&gt;: The HTML string to insert after the highlighted parts in all highlight and snippet results.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;hitsPerPage&lt;/strong&gt;:  is a widget that displays a menu of options to change the number of results per page.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Finally, register &lt;code&gt;Article&lt;/code&gt;, &lt;code&gt;ArticleIndex&lt;/code&gt;.&lt;/p&gt;
&lt;h3&gt;
  
  
  Creating Custom Commands.
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;create dir &lt;code&gt;algolia_search/management/commands&lt;/code&gt; &amp;amp; copy from following &lt;a href="https://github.com/lyamaa/algolia_search_backends/tree/main/2.final/src/algolia_search/management/commands"&gt;links&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;After creating then run the following commands
&lt;/li&gt;
&lt;/ul&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ python manage.py migrate
$ python manage.py makemigrations
$ python manage.py seed_superuser
$ python manage.py seed_users
$ python manage.py seed_articles
$ python manage.py seed_likes
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;ALgolia Dashboard&lt;/strong&gt;:
&lt;/h3&gt;

&lt;p&gt;so we are going to create index and query suggestion:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;index&lt;/strong&gt;: indexing a list of article objects.&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;query suggestion&lt;/strong&gt;: retrieves articles matching the query and displays a preview&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Login to &lt;a href="https://www.algolia.com/users/sign_in"&gt;Algolia&lt;/a&gt; with your desired accounts.&lt;/li&gt;
&lt;li&gt;create new application&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--1QQOMEaU--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/8a1dpogqdxdh2905jyzg.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--1QQOMEaU--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/8a1dpogqdxdh2905jyzg.png" alt="algolia-11" width="880" height="496"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Select Algolia Package&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--mCdbnDdd--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/i0xw82823noiyv0mwn3u.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--mCdbnDdd--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/i0xw82823noiyv0mwn3u.png" alt="algolia-10" width="880" height="488"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;-select a region, suitable for you&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--eho5qnfu--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/wjfawmds4taeyx2tkg4l.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--eho5qnfu--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/wjfawmds4taeyx2tkg4l.png" alt="algolia-9" width="880" height="508"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;click on &lt;code&gt;Review Application Details&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;click on &lt;code&gt;check boxes&lt;/code&gt;and &lt;code&gt;create application&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;create index &lt;code&gt;articles&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--EMy5Tseu--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/y02dv6yy26fg82f6czu9.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--EMy5Tseu--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/y02dv6yy26fg82f6czu9.png" alt="algolia-8" width="880" height="452"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Return to Dashboard and click on &lt;code&gt;API keys&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--vB73Yyrp--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/7kjhg7zg8db5rqi8uryx.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--vB73Yyrp--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/7kjhg7zg8db5rqi8uryx.png" alt="algolia-7" width="880" height="495"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Copy &lt;code&gt;keys&lt;/code&gt; from &lt;code&gt;Application ID&lt;/code&gt;, &lt;code&gt;Search-only API Key&lt;/code&gt;, and &lt;code&gt;Admin API Key and paste it to&lt;/code&gt;.env` file&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--hJRDH8jS--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/xbgq9z4ilkx0io540drm.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--hJRDH8jS--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/xbgq9z4ilkx0io540drm.png" alt="algolia-6" width="880" height="495"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Run commands python to index your data from backends:
&lt;code&gt;&lt;/code&gt;&lt;code&gt;
$ python manage.py algolia_reindex
&lt;/code&gt;&lt;code&gt;&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Go to home &lt;code&gt;Dashboard&lt;/code&gt; and click on articles.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--xTdWOnH1--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/cxi74774qffdpdje9iyv.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--xTdWOnH1--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/cxi74774qffdpdje9iyv.png" alt="algolia-5" width="880" height="470"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;List of datas are displayed as shown below
&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--HvtoGFsQ--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/rekqs74qufo7b8z2ggbt.png" alt="algolia-11" width="880" height="495"&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;
  
  
  Create &lt;code&gt;query suggestions&lt;/code&gt;
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;click on &lt;code&gt;query suggestions&lt;/code&gt; tab.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--rQBJBkJk--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/gi0roctrv0w8vb6fklcz.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--rQBJBkJk--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/gi0roctrv0w8vb6fklcz.png" alt="algolia-4" width="880" height="495"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;click button to create &lt;code&gt;query suggestions&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--5mNbzqZM--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/zp13ymrbmdz03uuiyonb.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--5mNbzqZM--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/zp13ymrbmdz03uuiyonb.png" alt="algolia-3" width="880" height="495"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--8NcUjf7k--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/5mv1y7g3ptwqzmbo7alh.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--8NcUjf7k--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/5mv1y7g3ptwqzmbo7alh.png" alt="algolia-2" width="880" height="495"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Accept and continue, wait for a while until it creates your &lt;code&gt;suggestion index&lt;/code&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;click to Edit Categories button and add the following categories attributes as shown in image.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--_FybIH7h--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/szrae59h748svutjntop.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--_FybIH7h--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/szrae59h748svutjntop.png" alt="algolia-one" width="880" height="495"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;click to save button and wait for a while until it re-index.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;
  
  
  Frontend Integration
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://www.algolia.com/doc/framework-integration/django/search/?client=python"&gt;Algolia&lt;/a&gt; suggest to use  &lt;a href="https://www.algolia.com/doc/guides/building-search-ui/what-is-instantsearch/js/"&gt;InstantSearch.js&lt;/a&gt; library to build your search interface and perform search queries directly from the end-user browser without going through your server.&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Orginal code: &lt;a href="https://codesandbox.io/s/github/algolia/autocomplete/tree/next/examples/react-instantsearch-hooks"&gt;Autocomplete with React InstantSearch Hooks example&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;well, i had made some changes from original src code.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;


&lt;h2&gt;
  
  
  &lt;div class="ltag-github-readme-tag"&gt;
  &lt;div class="readme-overview"&gt;
    &lt;h2&gt;
      &lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--566lAguM--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev.to/assets/github-logo-5a155e1f9a670af7944dd5e12375bc76ed542ea80224905ecaf878b9157cdefc.svg" alt="GitHub logo"&gt;
      &lt;a href="https://github.com/lyamaa"&gt;
        lyamaa
      &lt;/a&gt; / &lt;a href="https://github.com/lyamaa/algolia-search-frontend"&gt;
        algolia-search-frontend
      &lt;/a&gt;
    &lt;/h2&gt;
    &lt;h3&gt;
      ALgolia search in React
    &lt;/h3&gt;
  &lt;/div&gt;
&lt;/div&gt;

&lt;/h2&gt;


&lt;h3&gt;
  
  
  Final Words
&lt;/h3&gt;

&lt;p&gt;Well, i found &lt;strong&gt;algolia&lt;/strong&gt; to be simple and easy to configure than elastic search. ES is much more of writing code than algolia and bit more of complex.&lt;/p&gt;

&lt;p&gt;Read comparision of &lt;strong&gt;ES&lt;/strong&gt; and &lt;strong&gt;algolia&lt;/strong&gt; : &lt;a href="https://www.algolia.com/compare-algolia-vs-elasticsearch/"&gt;Algolia VS Elasticsearch&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;till then Bye bye 👋👋👋...&lt;/p&gt;




&lt;h2&gt;
  
  
  &lt;div class="ltag-github-readme-tag"&gt;
  &lt;div class="readme-overview"&gt;
    &lt;h2&gt;
      &lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--566lAguM--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev.to/assets/github-logo-5a155e1f9a670af7944dd5e12375bc76ed542ea80224905ecaf878b9157cdefc.svg" alt="GitHub logo"&gt;
      &lt;a href="https://github.com/lyamaa"&gt;
        lyamaa
      &lt;/a&gt; / &lt;a href="https://github.com/lyamaa/algolia_search_backends"&gt;
        algolia_search_backends
      &lt;/a&gt;
    &lt;/h2&gt;
    &lt;h3&gt;
      
    &lt;/h3&gt;
  &lt;/div&gt;
&lt;/div&gt;

&lt;/h2&gt;

</description>
      <category>django</category>
      <category>algolia</category>
      <category>react</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>⚖️Scaling Django⚖️</title>
      <dc:creator>Lyamaa</dc:creator>
      <pubDate>Tue, 29 Jun 2021 16:56:44 +0000</pubDate>
      <link>https://dev.to/lyamaa/scaling-django-3jep</link>
      <guid>https://dev.to/lyamaa/scaling-django-3jep</guid>
      <description>&lt;h2&gt;
  
  
  Why Scaling?
&lt;/h2&gt;

&lt;p&gt;The potential of your application to cope with increasing numbers of users simultaneously interacting with it. Ultimately, you want it to grow and be able to handle more and more requests per minute (RPMs). There are a number of factors that play a part in ensuring scalability, and it’s worth taking each of them into consideration.&lt;/p&gt;

&lt;h2&gt;
  
  
  Contents
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;Nginx&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;Caching&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Requirements:
&lt;/h2&gt;

&lt;p&gt;Well, i am using &lt;a href="https://docs.docker.com/get-docker/" rel="noopener noreferrer"&gt;&lt;code&gt;Docker&lt;/code&gt;&lt;/a&gt; to wrap up all my necessary tools and django apps on docker-container. Of-course you can ignore docker but have to install required tools independentely, it all up to you how you go through to it.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Well i am not going through with much details and explanation, please help yourself.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://www.django-rest-framework.org/" rel="noopener noreferrer"&gt;&lt;code&gt;django-rest-framework&lt;/code&gt;&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://docs.nginx.com/nginx/admin-guide/installing-nginx/installing-nginx-open-source/" rel="noopener noreferrer"&gt;&lt;code&gt;Nginx&lt;/code&gt;&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://redis.io/topics/quickstart" rel="noopener noreferrer"&gt;&lt;code&gt;Redis&lt;/code&gt;&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.postgresql.org/download/" rel="noopener noreferrer"&gt;&lt;code&gt;Postgres&lt;/code&gt;&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://python-poetry.org/docs/" rel="noopener noreferrer"&gt;&lt;code&gt;Poetry&lt;/code&gt;&lt;/a&gt; (an alternative for &lt;code&gt;pip&lt;/code&gt; or &lt;code&gt;pipenv&lt;/code&gt;)&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Quikstart
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Feeling lazy?&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;clone the repo: &lt;a href="https://github.com/lyamaa/scaling-django/tree/boilerplate" rel="noopener noreferrer"&gt;&lt;code&gt;boilerplate&lt;/code&gt;&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;and run below command
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ python3 -m venv env # create virtual environment
$ source env/bin/activate 
$ poetry install # make sure you have install poetry on your machine
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;




&lt;p&gt;OR&lt;/p&gt;




&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ mkdir scale &amp;amp;&amp;amp; cd scale
$ python3 -m venv env # create virtual environment
$ source env/bin/activate
$ poetry init # poetry initialization and generates *.toml file
$ poetry add djangorestframework psycopg2-binary Faker 
django-redis gunicorn
$ djang-admin startproject config .
$ python manage.py startapp products
$ touch Dockerfile
$ touch docker-compose.yml
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;h3&gt;
  
  
  Project structure:
&lt;/h3&gt;


&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;─── scale
    ├── config
    │ ├── **init**.py
    │ ├── asgi.py
    │ ├── settings
    │ │ ├── **init**.py
    │ │ ├──base.py
    │ │ ├──dev.py
    │ │ ├──prod.py
    │ ├── urls.py
    │ └── wsgi.py
    ├── manage.py
    └── products
    └── .env
    └── manage.py
    └── docker-compose.yml
    └── Dockerfile

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;blockquote&gt;
&lt;p&gt;note: above structure i have breakdown settings into &lt;code&gt;base.py&lt;/code&gt;,  &lt;code&gt;prod.py&lt;/code&gt;, &lt;code&gt;dev.py&lt;/code&gt;. Help yourself to break down, or you can get from here &lt;a href="https://github.com/lyamaa/scaling-django/tree/boilerplate" rel="noopener noreferrer"&gt;&lt;code&gt;boilerplate&lt;/code&gt;&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Let's start with docker.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;Dockerfile&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;FROM python:3.8.5-alpine

# prevents Python from generating .pyc files in the container
ENV PYTHONDONTWRITEBYTECODE 1
# Turns off buffering for easier container logging
ENV PYTHONUNBUFFERED 1

RUN \
    apk add --no-cache curl

# install psycopg2 dependencies
RUN apk update \
    &amp;amp;&amp;amp; apk add postgresql-dev gcc python3-dev musl-dev


# Install poetry
RUN pip install -U pip \
    &amp;amp;&amp;amp; curl -sSL https://raw.githubusercontent.com/python-poetry/poetry/master/get-poetry.py | python
ENV PATH="${PATH}:/root/.poetry/bin"


RUN mkdir /code
RUN mkdir /code/staticfiles
RUN mkdir /code/mediafiles

WORKDIR /code
COPY . /code

RUN poetry config virtualenvs.create false \
    &amp;amp;&amp;amp; poetry install --no-interaction --no-ansi

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;&lt;code&gt;docker-compose.yaml&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;version: "3.9"

services:
  scale:
    restart: always
    build: .
    command: python manage.py runserver 0.0.0.0
    volumes:
      - .:/code
    ports:
      - 8000:8000
    env_file:
      - ./.env
    depends_on:
      - db
  db:
    image: "postgres:11"
    volumes:
      - postgres_data:/var/lib/postgresql/data/
    ports:
      - 54322:5432
    environment:
      - POSTGRES_USER=scale
      - POSTGRES_PASSWORD=scale
      - POSTGRES_DB=scale

volumes:
  postgres_data:
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;Above we create &lt;code&gt;Dockerfile&lt;/code&gt; and &lt;code&gt;docker-compose.yaml&lt;/code&gt; file.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;we used alpine based image&lt;/li&gt;
&lt;li&gt;installed dependencies for &lt;code&gt;postgres&lt;/code&gt; and &lt;code&gt;poetry&lt;/code&gt; setup&lt;/li&gt;
&lt;li&gt;create service name &lt;code&gt;scale&lt;/code&gt; and &lt;code&gt;db&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Run the command:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;docker-compose up
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;you will get some error &lt;code&gt;database does not exist&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;let's create a database:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ docker container ls
CONTAINER ID   IMAGE         COMMAND                  CREATED          STATUS          PORTS                                         NAMES

78ac4d15bcd8   postgres:11   "docker-entrypoint.s…"   2 hours ago      Up 31 seconds   0.0.0.0:54322-&amp;gt;5432/tcp, :::54322-&amp;gt;5432/tcp   scale_db_1
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;copy &lt;code&gt;CONTAINER ID&lt;/code&gt; value&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt; $ docker exec -it 78ac4d15bcd8 bash
 :/#
 :/# psql --username=postgres
 psql (11.12 (Debian 11.12-1.pgdg90+1))
 Type "help" for help.

 postgres=# CREATE DATABASE scale;
 postgres=# CREATE USER scale WITH PASSWORD 'scale';
 postgres=# ALTER ROLE scale SET client_encoding TO 'utf8';
 postgres=# ALTER ROLE scale SET default_transaction_isolation TO 'read committed';
 postgres=# ALTER ROLE scale SET timezone TO 'UTC';
 postgres=# ALTER ROLE scale SUPERUSER;
 postgres=# GRANT ALL PRIVILEGES ON DATABASE scale TO scale;
 postgres=# \q
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;make sure your &lt;code&gt;settings/dev.py&lt;/code&gt; have config like this or your given credentials and change your &lt;code&gt;host&lt;/code&gt; &lt;code&gt;localhost&lt;/code&gt; to &lt;code&gt;db&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;from config.settings import BASE_DIR

DATABASES = {
    "default": {
        "ENGINE": "django.db.backends.postgresql_psycopg2",
        "ATOMIC_REQUESTS": True,
        "NAME": "scale",
        "USER": "scale",
        "PASSWORD": "scale",
        "HOST": "db",
        "PORT": "5432",
    }
}

# REDIS CONFIG
CACHES = {
    "default": {
        "BACKEND": "django_redis.cache.RedisCache",
        "LOCATION": "redis://redis:6379/0",
        "OPTIONS": {"CLIENT_CLASS": "django_redis.client.DefaultClient"},
    }
}

STATIC_URL = '/static/'
STATIC_ROOT = BASE_DIR.parent / "staticfiles"  # for collect static

MEDIA_ROOT = BASE_DIR.parent / "media"
MEDIA_URL = "/media/"


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;h3&gt;
  
  
  Nginx Setup &lt;a&gt;&lt;/a&gt;
&lt;/h3&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;a href="https://www.nginx.com/faq/what-is-nginx-how-different-is-it-from-e-g-apache/" rel="noopener noreferrer"&gt;&lt;code&gt;What is Nginx?&lt;/code&gt;&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Next, we setup &lt;code&gt;redis&lt;/code&gt; and &lt;code&gt;nginx&lt;/code&gt; and &lt;code&gt;gunicorn&lt;/code&gt; on docker:&lt;br&gt;
&lt;code&gt;docker-compose.yaml&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;version: "3.9"

services:
  scale:
    restart: always
    build: .
    command: gunicorn config.wsgi:application --bind 0.0.0.0:8000
    volumes:
      - .:/code
      - static_volume:/code/staticfiles
      - media_volume:/code/mediafiles
    expose:
      - 8000
    env_file:
      - ./.env
    depends_on:
      - db
      - redis
  db:
    image: "postgres:11"
    volumes:
      - postgres_data:/var/lib/postgresql/data/
    ports:
      - 54322:5432
    environment:
      - POSTGRES_USER=scale
      - POSTGRES_PASSWORD=scale
      - POSTGRES_DB=scale
  redis:
    image: redis
    ports:
      - 63799:6379
    restart: on-failure

  nginx:
    build: ./nginx
    restart: always
    volumes:
      - static_volume:/code/staticfiles
      - media_volume:/code/mediafiles
    ports:
      - 2000:80
    depends_on:
      - scale

volumes:
  postgres_data:
  static_volume:
  media_volume:
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;so, above we add two services &lt;code&gt;redis&lt;/code&gt; and &lt;code&gt;nginx&lt;/code&gt; and initialze &lt;code&gt;gunicorn&lt;/code&gt; instead of our regular command. Next we create a &lt;code&gt;nginx&lt;/code&gt; dir on root project with &lt;code&gt;Dockerfile&lt;/code&gt; &amp;amp; &lt;code&gt;nginx.conf&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;&lt;code&gt;nginx/Dockerfile&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;FROM nginx:latest

RUN rm /etc/nginx/conf.d/default.conf
COPY nginx.conf /etc/nginx/conf.d
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;&lt;code&gt;nginx/nginx.conf&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;upstream core {
    server scale:8000;
}

server {

    listen 80;

    location / {
        proxy_pass http://core;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header Host $host;
        proxy_redirect off;
        client_max_body_size 100M;
    }

     location /staticfiles/ {
        alias /code/staticfiles/;
    }
      location /mediafiles/ {
        alias /code/mediafiles/;
    }

}

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;Above, we created a &lt;code&gt;Dockerfile&lt;/code&gt; which will build our &lt;code&gt;nginx&lt;/code&gt; image and &lt;code&gt;nginx.conf&lt;/code&gt; where we are serving our app and serving static and media files.&lt;/p&gt;

&lt;p&gt;let's run &lt;code&gt;docker-compose&lt;/code&gt; file.&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;docker-compose up --build
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;Navigate this link to browser &lt;code&gt;http://localhost:2000/&lt;/code&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Note: Above &lt;code&gt;docker-compose.yaml&lt;/code&gt; file on &lt;code&gt;nginx&lt;/code&gt; service we initiated &lt;code&gt;port: 2000:80&lt;/code&gt;.&lt;br&gt;
so our server will run on port 2000.&lt;/p&gt;
&lt;/blockquote&gt;


&lt;h2&gt;
  
  
  &lt;a href="https://en.wikipedia.org/wiki/Cache_(computing)" rel="noopener noreferrer"&gt;&lt;code&gt;Caching&lt;/code&gt;&lt;/a&gt; Products &lt;a&gt;&lt;/a&gt;
&lt;/h2&gt;

&lt;p&gt;First lets try without caching.&lt;/p&gt;

&lt;p&gt;Now, let's create a &lt;code&gt;model&lt;/code&gt; for our &lt;code&gt;products&lt;/code&gt; app.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;products/models.py&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;from django.db import models
from django.utils.translation import gettext_lazy as _


class Category(models.Model):
    name = models.CharField(_("Category Name"), max_length=255, unique=True)
    description = models.TextField(null=True)

    class Meta:
        ordering = ("name",)
        verbose_name = _("Category")
        verbose_name_plural = _("Categories")

    def __str__(self) -&amp;gt; str:
        return self.name


class Product(models.Model):
    name = models.CharField(_("Product Name"), max_length=255)
    category = models.ForeignKey(
        Category, on_delete=models.DO_NOTHING)

    description = models.TextField()
    price = models.DecimalField(decimal_places=2, max_digits=10)
    quantity = models.IntegerField(default=0)
    discount = models.DecimalField(decimal_places=2, max_digits=10)
    image = models.URLField(max_length=255)

    class Meta:
        ordering = ("id",)
        verbose_name = _("Product")
        verbose_name_plural = _("Products")

    def __str__(self):
        return self.name
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;so further moving forward let's create a dummy data using custom commands.&lt;br&gt;
create a management directory inside products app.&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;── products
│── management
│ │── **init**.py
│ │── commands
│ │ │── **init**.py
│ │ │── category_seed.py
│ │ │── product_seed.py

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;&lt;code&gt;category_seed.py&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;from django.core.management import BaseCommand
from django.db import connections
from django.db.utils import OperationalError
from products.models import Category

from faker import Faker


class Command(BaseCommand):
    def handle(self, *args, **kwargs):
        faker = Faker()

        for _ in range(30):
            Category.objects.create(
                name=faker.name(),
                description=faker.text(200)
            )



&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;&lt;code&gt;product_seed.py&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;from django.core.management import BaseCommand
from django.db import connections
from django.db.utils import OperationalError
from products.models import Category, Product
from random import randrange, randint

from faker import Faker


class Command(BaseCommand):
    def handle(self, *args, **kwargs):
        faker = Faker()

        for _ in range(5000):
            price = randrange(10, 100)
            quantity = randrange(1, 5)
            cat_id = randint(1, 30)
            category = Category.objects.get(id=cat)
            Product.objects.create(
                name=faker.name(),
                category=category,
                description=faker.text(200),
                price=price,
                discount=100,
                quantity=quantity,
                image=faker.image_url()
)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;so, i will create 5000 of products and 30 category&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ docker-compose exec scale sh
/code # python manage.py makemigrations
/code # python manage.py migrate
/code # python manage.py createsuperuser
/code # python manage.py collectstatic --no-input
/code # python manage.py category_seed
/code # python manage.py product_seed # takes while to create 5000 data
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;You can view data on pgadmin or admin dashboard if data are loaded or not.&lt;/p&gt;

&lt;p&gt;After creation of dummy data let's create a &lt;code&gt;serializers&lt;/code&gt; and &lt;code&gt;views&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;&lt;code&gt;serializers.py&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;from rest_framework import serializers

from .models import Product, Category


class CategorySerializers(serializers.ModelSerializer):
    class Meta:
        model = Category
        fields = "__all__"


class CategoryRelatedField(serializers.StringRelatedField):
    def to_representation(self, value):
        return CategorySerializers(value).data

    def to_internal_value(self, data):
        return data


class ProductSerializers(serializers.ModelSerializer):

    class Meta:
        model = Product
        fields = "__all__"


class ReadProductSerializer(serializers.ModelSerializer):

    category = serializers.StringRelatedField(read_only=True)
    # category = CategoryRelatedField()
    # category = CategorySerializers()

    class Meta:
        model = Product
        fields = "__all__"

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;&lt;code&gt;views.py&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;from products.models import Product
from rest_framework import (
    viewsets,
    status,
)

import time
from .serializers import ProductSerializers, ReadProductSerializer

from rest_framework.response import Response


class ProductViewSet(viewsets.ViewSet):

    def list(self, request):
        serializer = ReadProductSerializer(Category.objects.all(), many=True)
        return Response(serializer.data)

    def create(self, request):
        serializer = ProductSerializers(data=request.data)
        serializer.is_valid(raise_exception=True)
        serializer.save()
        return Response(
            serializer.data, status=status.HTTP_201_CREATED)

    def retrieve(self, request, pk=None,):
        products = Product.objects.get(id=pk)
        serializer = ReadProductSerializer(products)
        return Response(
            serializer.data
        )

    def update(self, request, pk=None):
        products = Product.objects.get(id=pk)
        serializer = ProductSerializers(
            instance=products, data=request.data, partial=True)
        serializer.is_valid(raise_exception=True)
        serializer.save()
        return Response(
            serializer.data, status=status.HTTP_202_ACCEPTED)

    def destroy(self, request, pk=None):
        products = Product.objects.get(id=pk)
        products.delete()
        return Response(
            status=status.HTTP_204_NO_CONTENT
        )

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;&lt;code&gt;urls.py&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;
from django.urls import path

from .views import ProductViewSet

urlpatterns = [
    path("product", ProductViewSet.as_view(
        {"get": "list", "post": "create"})),
    path(
        "product/&amp;lt;str:pk&amp;gt;",
        ProductViewSet.as_view(
            {"get": "retrieve", "put": "update", "delete": "destroy"}),
    ),
]

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;so, we created a view using&lt;code&gt;viewsets&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;let's try with postman using different serializers on viewsets to get lists of 5K data.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;http://localhost:2000/api/v1/products&lt;/code&gt;&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;serializers&lt;/th&gt;
&lt;th&gt;Time&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;ReadProductSerializer (stringrelatedfield)&lt;/td&gt;
&lt;td&gt;6.42s&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;ReadProductSerializer (CategoryRelatedFeild)&lt;/td&gt;
&lt;td&gt;7.05s&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;ReadProductSerializer (Nested)&lt;/td&gt;
&lt;td&gt;6.49s&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;ReadProductSerializer (PrimaryKeyRelatedField)&lt;/td&gt;
&lt;td&gt;681 ms&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;ReadProductSerializer (without any)&lt;/td&gt;
&lt;td&gt;674ms&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;blockquote&gt;
&lt;p&gt;Note: response time may varies depending on your system.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Lets get data by using caching:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;views.py&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;from rest_framework.views import APIView
from products.models import Category, Product
from rest_framework import (
    viewsets,
    status,
)
from rest_framework.pagination import PageNumberPagination
import time
from .serializers import CategorySerializers, ProductSerializers, ReadProductSerializer

from rest_framework.response import Response

from django.core.cache import cache

class ProductListApiView(APIView):

    def get(self, request):
        paginator = PageNumberPagination()
        paginator.page_size = 10

        # get products from cache if exists
        products = cache.get('products_data')

        #  if products does not exists on cache create it
        if not products:
            products = list(Product.objects.select_related('category'))
            cache.set('products_data', products, timeout=60 * 60)

        # paginating cache products
        result = paginator.paginate_queryset(products, request)

        serializer = ReadProductSerializer(result, many=True)
        return paginator.get_paginated_response(serializer.data)


class ProductViewSet(viewsets.ViewSet):

    def create(self, request):
        serializer = ProductSerializers(data=request.data)
        serializer.is_valid(raise_exception=True)
        serializer.save()

        # get cache of products
        #  if exists
        #  delete cache
        for key in cache.keys('*'):
            if 'products_data' in key:
                cache.delete(key)
        cache.delete("products_data")

        return Response(
            serializer.data, status=status.HTTP_201_CREATED)

    def retrieve(self, request, pk=None,):
        products = Product.objects.get(id=pk)
        serializer = ReadProductSerializer(products)

        return Response(
            serializer.data
        )

    def update(self, request, pk=None):
        products = Product.objects.get(id=pk)
        serializer = ProductSerializers(
            instance=products, data=request.data, partial=True)
        serializer.is_valid(raise_exception=True)
        serializer.save()
        for key in cache.keys('*'):
            if 'products_data' in key:
                cache.delete(key)
        cache.delete("products_data")
        return Response(
            serializer.data, status=status.HTTP_202_ACCEPTED)

    def destroy(self, request, pk=None):
        products = Product.objects.get(id=pk)
        products.delete()
        for key in cache.keys('*'):
            if 'products_data' in key:
                cache.delete(key)
        cache.delete("products_data")
        return Response(
            status=status.HTTP_204_NO_CONTENT
        )

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;so, i have created a seperate &lt;code&gt;APIView&lt;/code&gt; and remove &lt;code&gt;list&lt;/code&gt; function from &lt;code&gt;viewsets&lt;/code&gt;. Which will fetch data from cache and paginated view.&lt;br&gt;
change your &lt;code&gt;products/urls.py&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;
from django.urls import path

from .views import ProductListApiView, ProductViewSet

urlpatterns = [

    path('products', ProductListApiView.as_view()),

    path("product", ProductViewSet.as_view(
        {"post": "create"})),
    path(
        "product/&amp;lt;str:pk&amp;gt;",
        ProductViewSet.as_view(
            {"get": "retrieve", "put": "update", "delete": "destroy"}),
    ),
]

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;So, try it again with postman with different &lt;code&gt;serializers&lt;/code&gt;.&lt;br&gt;
you will get results between &lt;code&gt;90 to 200ms&lt;/code&gt; depending upon your machine.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Note: in above &lt;code&gt;apiview&lt;/code&gt; i have used &lt;code&gt;select_related&lt;/code&gt;. Try removing it and run again with postman, will find a different results.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;To learn more about queryset `i.e select_related, prefetch_related. click this link &lt;a href="https://adamj.eu/tech/2020/09/01/django-and-the-n-plus-one-queries-problem/" rel="noopener noreferrer"&gt;N+1 Queries Problem&lt;/a&gt;&lt;/p&gt;


&lt;div class="ltag-github-readme-tag"&gt;
  &lt;div class="readme-overview"&gt;
    &lt;h2&gt;
      &lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev.to%2Fassets%2Fgithub-logo-5a155e1f9a670af7944dd5e12375bc76ed542ea80224905ecaf878b9157cdefc.svg" alt="GitHub logo"&gt;
      &lt;a href="https://github.com/lyamaa" rel="noopener noreferrer"&gt;
        lyamaa
      &lt;/a&gt; / &lt;a href="https://github.com/lyamaa/scaling-django" rel="noopener noreferrer"&gt;
        scaling-django
      &lt;/a&gt;
    &lt;/h2&gt;
    &lt;h3&gt;
      
    &lt;/h3&gt;
  &lt;/div&gt;
&lt;/div&gt;



&lt;h2&gt;
  
  
  Final words:
&lt;/h2&gt;

&lt;p&gt;Still there are lots of rooms to improve, it depends how?, where?, for what?, how many?.&lt;/p&gt;

&lt;p&gt;Hope You guys liked it... chao 👋👋&lt;/p&gt;

</description>
      <category>django</category>
      <category>docker</category>
      <category>redis</category>
      <category>nginx</category>
    </item>
    <item>
      <title>Working with Geo🌎Django</title>
      <dc:creator>Lyamaa</dc:creator>
      <pubDate>Sat, 05 Jun 2021 05:21:25 +0000</pubDate>
      <link>https://dev.to/lyamaa/working-with-geo-django-32nd</link>
      <guid>https://dev.to/lyamaa/working-with-geo-django-32nd</guid>
      <description>&lt;p&gt;&lt;a href="https://docs.djangoproject.com/en/3.2/ref/contrib/gis/tutorial/" rel="noopener noreferrer"&gt;GeoDjango&lt;/a&gt; is a built-in application that is included as a contrib module in Django. It’s actually a complete framework itself that can also be used separately from Django. It provides a toolbox of utilities for building GIS web applications.&lt;/p&gt;

&lt;h3&gt;
  
  
  Purpose of this tutorial
&lt;/h3&gt;

&lt;p&gt;Nothing much fancy, here we are getting co-ordinates(Longitude, Latitude) of location by inputting names of country and city.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;Requirements&lt;/strong&gt; :
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://www.postgresql.org/" rel="noopener noreferrer"&gt;Postgres&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;
&lt;a href="http://postgis.net/" rel="noopener noreferrer"&gt;postgis&lt;/a&gt; extension need to be 
installed on postgres&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://www.qgis.org/en/site/" rel="noopener noreferrer"&gt;QGIS&lt;/a&gt; GeoDjango 
Dependencies or install Geospatial libraries &lt;a href="https://docs.djangoproject.com/en/3.2/ref/contrib/gis/install/geolibs/" rel="noopener noreferrer"&gt;GEOS, GDAL, and PROJ.4 &lt;/a&gt;,
&lt;/li&gt;
&lt;/ul&gt;

&lt;blockquote&gt;
&lt;p&gt;Note: above requirements need to be install on your machine seperately before continuing&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Project setup:
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;
$ mkdir dj_gis &amp;amp;&amp;amp; cd dj_gis
$ python3 -m venv env
$ source env/bin/activate
$ pip install django djangorestframework django-leaflet geopy psycopg2-binary 
$ django-admin.py startproject config
$ python manage.py startapp location

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;&lt;strong&gt;config/settings.py&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;#config/settings.py

INSTALLED_APPS = [
    "django.contrib.gis",
    "location",
    "rest_framework",
]

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;&lt;strong&gt;config/urls.py&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;#config/urls.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("location.urls")),
]

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;include &lt;strong&gt;url.py&lt;/strong&gt; file on &lt;strong&gt;location&lt;/strong&gt; app&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;#location/urls.py

from django.urls import path

urlpatterns = []
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;blockquote&gt;
&lt;p&gt;So we finished basic setups&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Let's create a model &lt;strong&gt;location/models.py&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;from django.db import models
from django.contrib.gis.db import models  # GeoDjango Model
from django.utils.translation import gettext_lazy as _


class Hotel(models.Model):
    name = models.CharField(max_length=255)
    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)
    location = models.PointField(null=True)  # Spatial Field
    created_at = models.DateTimeField(auto_now_add=True)
    updated_at = models.DateTimeField(auto_now=True)

    class Meta:
        verbose_name = _("Hotel")
        verbose_name_plural = _("Hotels")

    def __str__(self):
        return f"{self.street_1}, {self.city}, {self.state}, {self.country}"

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;blockquote&gt;
&lt;p&gt;here, we include a &lt;a href="https://docs.djangoproject.com/en/3.2/ref/contrib/gis/model-api/" rel="noopener noreferrer"&gt;pointfield&lt;/a&gt; spatial field of geo django model api. &lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;let's create a &lt;strong&gt;serializer&lt;/strong&gt; and &lt;strong&gt;views&lt;/strong&gt; for our model.&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# location/serializers.py

from location.models import Hotel
from rest_framework import serializers


class HotelSerializer(serializers.ModelSerializer):
    class Meta:
        model = Hotel

        fields = (
            "id",
            "name",
            "street_1",
            "street_2",
            "city",
            "state",
            "zip_code",
            "country",
            "location",
        )

        extra_kwargs = {"location": {"read_only": True}}    
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;&lt;strong&gt;location/views.py&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;from location.serializers import HotelSerializer
from django.shortcuts import render
from rest_framework import generics
from .models import Hotel

from django.contrib.gis.geos import Point
from geopy.geocoders import Nominatim

geolocator = Nominatim(user_agent="location")


class ListCreateGenericViews(generics.ListCreateAPIView):
    queryset = Hotel.objects.all()
    serializer_class = HotelSerializer

    def perform_create(self, serializer):
        street_1 = serializer.initial_data["street_1"]
        address = serializer.initial_data["city"]
        state = serializer.initial_data["state"]
        country = serializer.initial_data["city"]
        data = [street_1, address, state, country]
        " ".join(data)

        g = geolocator.geocode(data)
        lat = g.latitude
        lng = g.longitude
        pnt = Point(lng, lat)
        print(pnt)
        serializer.save(location=pnt)


class HotelUpdateRetreiveView(generics.RetrieveUpdateDestroyAPIView):
    queryset = Hotel.objects.all()
    serializer_class = HotelSerializer

    def perform_update(self, serializer):
        street_1 = serializer.initial_data["street_1"]
        address = serializer.initial_data["city"]
        state = serializer.initial_data["state"]
        country = serializer.initial_data["city"]
        data = [street_1, address, state, country]
        " ".join(data)

        g = geolocator.geocode(data)
        lat = g.latitude
        lng = g.longitude
        pnt = Point(lng, lat)
        print(pnt)
        serializer.save(location=pnt)

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;blockquote&gt;
&lt;p&gt;Note: above &lt;strong&gt;view&lt;/strong&gt; can be further refactor using &lt;strong&gt;viewsets&lt;/strong&gt; or your desired ones.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;so, &lt;a href="https://geopy.readthedocs.io/en/stable/" rel="noopener noreferrer"&gt;geopy&lt;/a&gt; library comes in handy, which is  is a Python client for several popular geocoding web services. geopy makes it easy for Python developers to locate the coordinates of addresses, cities, countries, and landmarks across the globe using third-party geocoders and other data sources.&lt;/p&gt;

&lt;p&gt;let's update our urls:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;#location/urls.py

from django.urls import path
from .views import HotelUpdateRetreiveView, ListCreateGenericViews

urlpatterns = [
    path("hotels", ListCreateGenericViews.as_view()),
    path(
        "hotels/&amp;lt;str:pk&amp;gt;",
        HotelUpdateRetreiveView.as_view(),
    ),
]
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;Creating a Database:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;sudo -u postgres psql

CREATE DATABASE locator;

CREATE USER locator WITH PASSWORD 'locator';

CREATE EXTENSION postgis;

ALTER ROLE locator SET client_encoding TO 'utf8';

ALTER ROLE locator SET default_transaction_isolation TO 'read committed';

ALTER ROLE locator SET timezone TO 'UTC';

ALTER ROLE locator SUPERUSER;

GRANT ALL PRIVILEGES ON DATABASE locator TO locator;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;let's make some changes on our &lt;strong&gt;settings.py&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;INSTALLED_APPS = [
    "django.contrib.admin",
    "django.contrib.auth",
    "django.contrib.contenttypes",
    "django.contrib.sessions",
    "django.contrib.messages",
    "django.contrib.staticfiles",
    "django.contrib.gis",
    "location",
    "rest_framework",
    "leaflet",
]

DATABASES = {
    "default": {
        "ENGINE": "django.contrib.gis.db.backends.postgis",
        "NAME": "locator",
        "USER": "locator",
        "PASSWORD": "locator",
        "HOST": "localhost",
        "PORT": "5432",
    }
}

LEAFLET_CONFIG = {
    # "SPATIAL_EXTENT": (5.0, 44.0, 7.5, 46),
    "DEFAULT_CENTER": (13.3888599 52.5170365), #set your corordinate
    "DEFAULT_ZOOM": 16,
    "MIN_ZOOM": 3,
    "MAX_ZOOM": 20,
    "DEFAULT_PRECISION": 6,
    "SCALE": "both",
    "ATTRIBUTION_PREFIX": "powered by me",
}

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;Registering model on admin&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;from django.contrib import admin
from leaflet.admin import LeafletGeoAdmin

from .models import Hotel


@admin.register(Hotel)
class HotelAdmin(LeafletGeoAdmin):
    list_display = ("id", "name", "location", "created_at", "updated_at")
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;blockquote&gt;
&lt;p&gt;so, we have added leaflet, leaflet_config and database.&lt;br&gt;
For more about Leaflet you can visit &lt;a href="https://django-leaflet.readthedocs.io/en/latest/templates.html" rel="noopener noreferrer"&gt;Read the docs&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;let's run our app:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;python manage.py makemigrations
python manage.py migrate
python manage.py createsuperuser
python manage.py runserver
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;here, what you get on admin panel.&lt;br&gt;
&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F0dfjvq878bzcl4xyrcvc.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F0dfjvq878bzcl4xyrcvc.png" alt="admin"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Let's add some data using browsable api or you can use postman too.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Try it out yourself for udate and delete&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Febhyxz09kjua2zif09r9.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Febhyxz09kjua2zif09r9.png" alt="browsable api"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;You can find the code in github repo.&lt;br&gt;
&lt;/p&gt;
&lt;div class="ltag-github-readme-tag"&gt;
  &lt;div class="readme-overview"&gt;
    &lt;h2&gt;
      &lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev.to%2Fassets%2Fgithub-logo-5a155e1f9a670af7944dd5e12375bc76ed542ea80224905ecaf878b9157cdefc.svg" alt="GitHub logo"&gt;
      &lt;a href="https://github.com/lyamaa" rel="noopener noreferrer"&gt;
        lyamaa
      &lt;/a&gt; / &lt;a href="https://github.com/lyamaa/dj_gis" rel="noopener noreferrer"&gt;
        dj_gis
      &lt;/a&gt;
    &lt;/h2&gt;
    &lt;h3&gt;
      Examples on GIS using django
    &lt;/h3&gt;
  &lt;/div&gt;
&lt;/div&gt;



</description>
      <category>django</category>
      <category>geodjango</category>
      <category>postgis</category>
      <category>leaflet</category>
    </item>
    <item>
      <title>Authenticate With Djoser</title>
      <dc:creator>Lyamaa</dc:creator>
      <pubDate>Mon, 24 May 2021 10:27:30 +0000</pubDate>
      <link>https://dev.to/lyamaa/authenticate-with-djoser-2kf7</link>
      <guid>https://dev.to/lyamaa/authenticate-with-djoser-2kf7</guid>
      <description>&lt;p&gt;&lt;strong&gt;REST&lt;/strong&gt; implementation of Django authentication system. djoser library provides a set of Django Rest Framework views to handle basic actions such as registration, login, logout, password reset and account activation. It works with custom user model.&lt;/p&gt;

&lt;h2&gt;
  
  
  Requirements:
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Django(Rest Framework)&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/mailhog/MailHog" rel="noopener noreferrer"&gt;MailHog&lt;/a&gt; (email testing tool for developers)&lt;/li&gt;
&lt;li&gt;&lt;a href="https://djoser.readthedocs.io/en/latest/getting_started.html" rel="noopener noreferrer"&gt;Djoser&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://drf-yasg.readthedocs.io/en/stable/readme.html#usage" rel="noopener noreferrer"&gt;DRF YASG&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Project Setup:
&lt;/h2&gt;

&lt;p&gt;create a virtual env.&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;python -m venv env

Activate
./env/Scripts/activate # for windows

source env/bin/activate # linux user
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;p&gt;Here i am using poetry you can use pip or pienv:&lt;br&gt;
poetry Setup:&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;

    poetry init
    poetry add django djangorestframework djoser djangorestframework_simplejwt django-cors-headers
    poetry add drf-yasg # for api docs


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;h2&gt;
  
  
  Creating project and apps:
&lt;/h2&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;

    django-admin startproject core
    python manage.py startapp accounts


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;h2&gt;
  
  
  Configuring &lt;strong&gt;settings.py&lt;/strong&gt;:
&lt;/h2&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;

# Installed Apps

DJANGO_APPS = [
"django.contrib.admin",
"django.contrib.auth",
"django.contrib.contenttypes",
"django.contrib.sessions",
"django.contrib.messages",
"django.contrib.staticfiles",
]

PROJECT_APPS = ["accounts"]

THIRD_PARTY_APPS = [
"rest_framework",
"drf_yasg",
"djoser",
"corsheaders",
"rest_framework_simplejwt",
"rest_framework_simplejwt.token_blacklist",
]

INSTALLED_APPS = DJANGO_APPS + PROJECT_APPS + THIRD_PARTY_APPS

MIDDLEWARE = [
"django.middleware.security.SecurityMiddleware",
"django.contrib.sessions.middleware.SessionMiddleware",
"corsheaders.middleware.CorsMiddleware", # middleware for cors-headers
"django.middleware.common.CommonMiddleware",
"django.middleware.csrf.CsrfViewMiddleware",
"django.contrib.auth.middleware.AuthenticationMiddleware",
"django.contrib.messages.middleware.MessageMiddleware",
"django.middleware.clickjacking.XFrameOptionsMiddleware",
]


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;h2&gt;
  
  
  let's create a custom user model:
&lt;/h2&gt;

&lt;p&gt;accounts/models.py&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;

from django.db import models
from django.contrib.auth.models import (
    AbstractBaseUser,
    PermissionsMixin,
    BaseUserManager,
)


class CustomUserManager(BaseUserManager):
    def create_user(self, email, username, password=None, **extra_fields):
        if not email:
            raise ValueError("User must have an email")
        email = self.normalize_email(email)
        user = self.model(email=email, username=username, **extra_fields)
        user.set_password(password)
        user.save(using=self._db)
        return user

    def create_superuser(self, username, email, password=None, **extra_fields):
        user = self.create_user(username, email, password=password, **extra_fields)
        user.is_active = True
        user.is_staff = True
        user.is_admin = True
        user.save(using=self._db)
        return user


class CustomUser(AbstractBaseUser, PermissionsMixin):
    email = models.EmailField(max_length=255, unique=True)
    username = models.CharField(max_length=255, unique=True)
    first_name = models.CharField(max_length=255)
    last_name = models.CharField(max_length=255)
    is_active = models.BooleanField(default=True)
    is_staff = models.BooleanField(default=False)
    is_admin = models.BooleanField(default=False)

    objects = CustomUserManager()

    USERNAME_FIELD = "email"
    REQUIRED_FIELDS = ["username", "first_name", "last_name"]

    def get_full_name(self):
        return f"{self.first_name} - {self.last_name}"

    def get_short_name(self):
        return self.username

    def has_perm(self, perm, obj=None):
        return True

    def has_module_perms(self, app_label):
        return True

    def __str__(self):
        return self.email


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;h2&gt;
  
  
  &lt;a href="https://github.com/sunscrapers/djoser/blob/master/djoser/serializers.py" rel="noopener noreferrer"&gt;Customizing Serializers&lt;/a&gt;
&lt;/h2&gt;

&lt;p&gt;accounts/serializers.py&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;

from djoser.serializers import UserCreateSerializer
from django.contrib.auth import get_user_model

User = get_user_model()


class UserCreateSerializer(UserCreateSerializer):
    class Meta(UserCreateSerializer.Meta):
        model = User
        fields = ("id", "email", "username", "first_name", "last_name", "password")



&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;h2&gt;
  
  
  Email setup: settings.py
&lt;/h2&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;

# EMAIL CONFIG
EMAIL_BACKEND = "django.core.mail.backends.smtp.EmailBackend"
EMAIL_HOST = "localhost"
EMAIL_PORT = "1025"
EMAIL_HOST_USER = ""
EMAIL_HOST_PASSWORD = ""
EMAIL_USE_TLS = False


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;h2&gt;
  
  
  Djoser JWT and CORS HEADERS: settings.py
&lt;/h2&gt;

&lt;p&gt;[Note: you won't need cors header unless you use any frontend framework or libraries]&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;

REST_FRAMEWORK = {
    "DEFAULT_PERMISSION_CLASSES": ["rest_framework.permissions.IsAuthenticated"],
    "DEFAULT_AUTHENTICATION_CLASSES": (
        "rest_framework_simplejwt.authentication.JWTAuthentication",
    ),
}


SIMPLE_JWT = {
    "AUTH_HEADER_TYPES": ("JWT",),
    "ACCESS_TOKEN_LIFETIME": timedelta(minutes=60),
    "REFRESH_TOKEN_LIFETIME": timedelta(days=1),
    "AUTH_TOKEN_CLASSES": ("rest_framework_simplejwt.tokens.AccessToken",),
}

# DJOSER CONFIG
DJOSER = {
    "LOGIN_FIELD": "email",
    "USER_CREATE_PASSWORD_RETYPE": True,
    "USERNAME_CHANGED_EMAIL_CONFIRMATION": True,
    "PASSWORD_CHANGED_EMAIL_CONFIRMATION": True,
    "SEND_CONFIRMATION_EMAIL": True,
    "SET_USERNAME_RETYPE": True,
    "SET_PASSWORD_RETYPE": True,
    "USERNAME_RESET_CONFIRM_URL": "password/reset/confirm/{uid}/{token}",
    "PASSWORD_RESET_CONFIRM_URL": "email/reset/confirm/{uid}/{token}",
    "ACTIVATION_URL": "activate/{uid}/{token}",
    "SEND_ACTIVATION_EMAIL": True,
    "SOCIAL_AUTH_TOKEN_STRATEGY": "djoser.social.token.jwt.TokenStrategy",
    "SOCIAL_AUTH_ALLOWED_REDIRECT_URIS": [
        "your redirect url",
        "your redirect url",
    ],
    "SERIALIZERS": {
        "user_create": "accounts.serializers.UserCreateSerializer",  # custom serializer
        "user": "djoser.serializers.UserSerializer",
        "current_user": "djoser.serializers.UserSerializer",
        "user_delete": "djoser.serializers.UserSerializer",
    },
}

# CORS HEADERS
CORS_ORIGIN_ALLOW_ALL = True
CORS_ALLOW_CREDENTIALS = True


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;h2&gt;
  
  
  URL CONFIG:
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;accounts/urls.py&lt;/li&gt;
&lt;/ul&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;

from django.urls import path


urlpatterns = []



&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;ul&gt;
&lt;li&gt;main/urls.py&lt;/li&gt;
&lt;/ul&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;

#main/urls.py

from django.contrib import admin
from django.urls import path
from django.urls.conf import include, re_path

# DRF YASG
from rest_framework import permissions
from drf_yasg.views import get_schema_view
from drf_yasg import openapi

schema_view = get_schema_view(
    openapi.Info(
        title="Djoser API",
        default_version="v1",
        description="REST implementation of Django authentication system. djoser library provides a set of Django Rest Framework views to handle basic actions such as registration, login, logout, password reset and account activation. It works with custom user model.",
        contact=openapi.Contact(email="contact@snippets.local"),
        license=openapi.License(name="BSD License"),
    ),
    public=True,
    permission_classes=(permissions.AllowAny,),
)

urlpatterns = [
    path("admin/", admin.site.urls),
    re_path(
        r"^api/v1/docs/$",
        schema_view.with_ui("swagger", cache_timeout=0),
        name="schema-swagger-ui",
    ),
    path("api/v1/", include("accounts.urls")),
    path("api/v1/", include("djoser.urls")),
    path("api/v1/", include("djoser.urls.jwt")),

]



&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;p&gt;Here we:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;include some url paths that djoser provides us.&lt;/li&gt;
&lt;li&gt;and config for API docs
&lt;a href="http://127.0.0.1:8000/api/v1/docs/" rel="noopener noreferrer"&gt;http://127.0.0.1:8000/api/v1/docs/&lt;/a&gt;
&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F8no7n6fquw0cvybnp24e.png" alt="Alt Text"&gt;
&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fres.cloudinary.com%2Fdazyxzm1e%2Fimage%2Fupload%2Fv1621847318%2Fapidocs1_udpte8.png" alt="Api docs"&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;here are some urls:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Register User
&lt;a href="http://127.0.0.1:8000/api/v1/users/" rel="noopener noreferrer"&gt;http://127.0.0.1:8000/api/v1/users/&lt;/a&gt;
&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fres.cloudinary.com%2Fdazyxzm1e%2Fimage%2Fupload%2Fv1621848797%2Fregister_r6b1rl.png" alt="Register"&gt;
&lt;/li&gt;
&lt;li&gt;User activation
&lt;a href="http://127.0.0.1:8000/api/v1/users/activation/" rel="noopener noreferrer"&gt;http://127.0.0.1:8000/api/v1/users/activation/&lt;/a&gt;
&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fres.cloudinary.com%2Fdazyxzm1e%2Fimage%2Fupload%2Fv1621848177%2FuseractivationMail_ffqkpk.png" alt="activation"&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Requires two parameters:&lt;br&gt;
  {&lt;br&gt;
    "uid" :"some uid",&lt;br&gt;
    "token": "some token"&lt;br&gt;&lt;br&gt;
 }&lt;br&gt;
  &lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fres.cloudinary.com%2Fdazyxzm1e%2Fimage%2Fupload%2Fv1621849039%2FuserActivation_g1giul.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fres.cloudinary.com%2Fdazyxzm1e%2Fimage%2Fupload%2Fv1621849039%2FuserActivation_g1giul.png" alt="postman"&gt;&lt;/a&gt;&lt;br&gt;
 Similarly you can work with below apis.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;User login
&lt;a href="http://127.0.0.1:8000/api/v1/jwt/create/" rel="noopener noreferrer"&gt;http://127.0.0.1:8000/api/v1/jwt/create/&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Reset Password
&lt;a href="http://127.0.0.1:8000/api/v1/users/reset_password/" rel="noopener noreferrer"&gt;http://127.0.0.1:8000/api/v1/users/reset_password/&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Reset Password Confirm
&lt;a href="http://127.0.0.1:8000/api/v1/users/reset_password_confirm/" rel="noopener noreferrer"&gt;http://127.0.0.1:8000/api/v1/users/reset_password_confirm/&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;User Me
&lt;a href="http://127.0.0.1:8000/api/v1/users/me/" rel="noopener noreferrer"&gt;http://127.0.0.1:8000/api/v1/users/me/&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;we can work with social-auth too but we require extra configs.&lt;br&gt;
here is the github link where i have work with &lt;a href="https://github.com/lyamaa/dj-auth-backend" rel="noopener noreferrer"&gt;social auth&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;overall we are done. 😌😌😌😌&lt;br&gt;
If you are stuck 👇👇👇. &lt;/p&gt;




&lt;div class="ltag-github-readme-tag"&gt;
  &lt;div class="readme-overview"&gt;
    &lt;h2&gt;
      &lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev.to%2Fassets%2Fgithub-logo-5a155e1f9a670af7944dd5e12375bc76ed542ea80224905ecaf878b9157cdefc.svg" alt="GitHub logo"&gt;
      &lt;a href="https://github.com/lyamaa" rel="noopener noreferrer"&gt;
        lyamaa
      &lt;/a&gt; / &lt;a href="https://github.com/lyamaa/dj_djoser" rel="noopener noreferrer"&gt;
        dj_djoser
      &lt;/a&gt;
    &lt;/h2&gt;
    &lt;h3&gt;
      REST implementation of Django authentication system. djoser library provides a set of Django Rest Framework views to handle basic actions such as registration, login, logout, password reset and account activation. It works with custom user model.
    &lt;/h3&gt;
  &lt;/div&gt;
&lt;/div&gt;






</description>
      <category>django</category>
      <category>djoser</category>
      <category>python</category>
      <category>jwt</category>
    </item>
    <item>
      <title>Elastic Search + Django</title>
      <dc:creator>Lyamaa</dc:creator>
      <pubDate>Fri, 21 May 2021 22:06:07 +0000</pubDate>
      <link>https://dev.to/lyamaa/elastic-search-django-g85</link>
      <guid>https://dev.to/lyamaa/elastic-search-django-g85</guid>
      <description>&lt;h1&gt;
  
  
  Requirements:
&lt;/h1&gt;

&lt;ul&gt;
&lt;li&gt;Django&lt;/li&gt;
&lt;li&gt;Elastic Search &lt;a href="https://www.elastic.co/downloads/past-releases/elasticsearch-7-0-0" rel="noopener noreferrer"&gt;Install&lt;/a&gt; (required version 7)&lt;/li&gt;
&lt;li&gt;&lt;a href="https://drf-haystack.readthedocs.io/en/latest/" rel="noopener noreferrer"&gt;Drf Haystack&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Poetry &lt;a href="https://python-poetry.org/docs/" rel="noopener noreferrer"&gt;Install&lt;/a&gt; or you can use pip or pipenv&lt;/li&gt;
&lt;/ul&gt;




&lt;h1&gt;
  
  
  Project Setup:
&lt;/h1&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ mkdir dj_elastic &amp;amp;&amp;amp; 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==^7.x.x
$ django-admin.py startproject main
$ python manage.py startapp searches
$ python manage.py startapp commons
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;project directory should look like:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;── dj_elastic
├── main
│ ├── **init**.py
│ ├── asgi.py
│ ├── settings.py
│ ├── urls.py
│ └── wsgi.py
├── manage.py
└── commons
└── searches
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;Main app /url.py&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;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")),
]

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;main/settings.py&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;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.elasticsearch7_backend.Elasticsearch7SearchEngine',
        "URL": "http://127.0.0.1:9200/",
        "INDEX_NAME": "haystack",
    },
}

HAYSTACK_SIGNAL_PROCESSOR = "haystack.signals.RealtimeSignalProcessor"

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;👏🏻 Great, finished with basic setups....&lt;br&gt;
Next, lets create models. Navigate to commons/models.py&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# 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) -&amp;gt; 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}"``

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;Here, we:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Created a models &lt;strong&gt;ConfigChoiceCategory&lt;/strong&gt; and &lt;strong&gt;ConfigChoice&lt;/strong&gt;, where &lt;strong&gt;configchoice&lt;/strong&gt; has relation with &lt;strong&gt;ConfigChoiceCategory&lt;/strong&gt;. &lt;/li&gt;
&lt;li&gt;And we have &lt;strong&gt;Address Model&lt;/strong&gt; too&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Register models to admin.py&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;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)


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;




&lt;p&gt;So, let's navigate to &lt;strong&gt;searches&lt;/strong&gt; app and create models for hotels.&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;
#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) -&amp;gt; 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) -&amp;gt; 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) -&amp;gt; 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}"

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;Registering models to admin.py&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;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)

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;create a file &lt;strong&gt;search_indexes.py&lt;/strong&gt; inside searches app.&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;#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
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;ul&gt;
&lt;li&gt;Creates a unique &lt;strong&gt;SearchIndex&lt;/strong&gt; 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.&lt;/li&gt;
&lt;li&gt;To build a &lt;strong&gt;SearchIndex&lt;/strong&gt;, all that’s necessary is to subclass both &lt;strong&gt;indexes.SearchIndex&lt;/strong&gt; &amp;amp; &lt;strong&gt;indexes.Indexable&lt;/strong&gt;, define the fields you want to store data with and define a get_model method.&lt;/li&gt;
&lt;/ul&gt;


&lt;h4&gt;
  
  
  Serialization and views:
&lt;/h4&gt;


&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;#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",
        ]

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# 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)

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;blockquote&gt;
&lt;p&gt;so you can create a each class of serializers for each models &lt;a href="https://drf-haystack.readthedocs.io/en/latest/01_intro.html" rel="noopener noreferrer"&gt;Like this&lt;/a&gt;.&lt;br&gt;
&lt;/p&gt;
&lt;/blockquote&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# searches/urls.py

from django.urls import path

from .views import AggregateSearchViewSet


urlpatterns = [
path("hotels/search/", AggregateSearchViewSet.as_view())
]

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;Create a templates directory inside searches app.&lt;br&gt;
Templates folder will look like this:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;templates
    ├── search
        ├── indexes
            ├── searches
                ├── hotel_text.txt
                ├── hoteladdress_text.txt
                ├── hotelimage_text.txt
                ├── hotelspecificationvalue_text.txt

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;Finally &lt;strong&gt;migrate&lt;/strong&gt; your apps, &lt;strong&gt;createsuperuser&lt;/strong&gt; and add some hotels data using django admin panels.&lt;/p&gt;

&lt;p&gt;Simply run&lt;br&gt;
&lt;br&gt;
 &lt;code&gt;./manage.py rebuild_index.&lt;/code&gt;&lt;br&gt;
&lt;br&gt;
 You’ll get some totals of how many models were processed and placed in the index.&lt;/p&gt;
&lt;h1&gt;
  
  
  Query time!
&lt;/h1&gt;

&lt;p&gt;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.&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;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&amp;amp;resize=fit&amp;amp;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&amp;amp;resize=fit&amp;amp;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"
        }
    ]
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;div class="ltag-github-readme-tag"&gt;
  &lt;div class="readme-overview"&gt;
    &lt;h2&gt;
      &lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev.to%2Fassets%2Fgithub-logo-5a155e1f9a670af7944dd5e12375bc76ed542ea80224905ecaf878b9157cdefc.svg" alt="GitHub logo"&gt;
      &lt;a href="https://github.com/lyamaa" rel="noopener noreferrer"&gt;
        lyamaa
      &lt;/a&gt; / &lt;a href="https://github.com/lyamaa/elastic_search_dj_example" rel="noopener noreferrer"&gt;
        elastic_search_dj_example
      &lt;/a&gt;
    &lt;/h2&gt;
    &lt;h3&gt;
      Examples on Django + haystack + elastic search
    &lt;/h3&gt;
  &lt;/div&gt;
&lt;/div&gt;



</description>
      <category>django</category>
      <category>python</category>
      <category>elasticsearch</category>
      <category>haystack</category>
    </item>
  </channel>
</rss>
