django-meilisearch-indexer


Namedjango-meilisearch-indexer JSON
Version 1.0.1 PyPI version JSON
download
home_pageNone
SummaryMeilisearch indexer for django models and related utilities
upload_time2024-11-02 14:14:41
maintainerNone
docs_urlNone
authorNone
requires_python>=3.9
licenseMIT License Copyright (c) 2024 Jordan Kowal Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
keywords django meilisearch indexer
VCS
bugtrack_url
requirements No requirements were recorded.
Travis-CI No Travis.
coveralls test coverage No coveralls.
            # ✨ Django Meilisearch Indexer ✨

![Code quality](https://github.com/Jordan-Kowal/django-meilisearch-indexer/actions/workflows/code_quality.yml/badge.svg?branch=main)
![Tests](https://github.com/Jordan-Kowal/django-meilisearch-indexer/actions/workflows/tests.yml/badge.svg?branch=main)
![Build](https://github.com/Jordan-Kowal/django-meilisearch-indexer/actions/workflows/publish_package.yml/badge.svg?event=release)
![Coverage](https://badgen.net/badge/coverage/%3E90%25/pink)
![Tag](https://badgen.net/badge/tag/1.0.1/orange)
![Python](https://badgen.net/badge/python/3.9%20|%203.10%20|%203.11%20|%203.12|%203.13)
![Licence](https://badgen.net/badge/licence/MIT)

- [✨ Django Meilisearch Indexer ✨](#-django-meilisearch-indexer-)
  - [💻 How to install](#-how-to-install)
  - [⚡ Quick start](#-quick-start)
  - [📕 Available modules](#-available-modules)
  - [🍜 Recipes](#-recipes)
    - [Create indexes on boot](#create-indexes-on-boot)
    - [Async actions with celery](#async-actions-with-celery)
    - [Mock for testing](#mock-for-testing)
    - [Admin actions](#admin-actions)
  - [🔗 Useful links](#-useful-links)

Provides a `MeilisearchModelIndexer` class to easily index django models in Meilisearch.

## 💻 How to install

The package is available on PyPi with the name `django_meilisearch_indexer`.
Simply run:

```shell
pip install django_meilisearch_indexer
```

## ⚡ Quick start

Here's a basic example:

```python
# Imports
from typing import Any, Dict
from django.db import models
from django_meilisearch_indexer.indexers import MeilisearchModelIndexer

# Model
class Tag(models.Model):
    name = models.CharField(max_length=100, unique=True)
    color = models.CharField(max_length=100)
    is_disabled = models.BooleanField(default=False)

# Indexer
class TagIndexer(MeilisearchModelIndexer[Tag]):
    MODEL_CLASS = Tag
    PRIMARY_KEY = "id"
    SETTINGS = {
        "filterableAttributes": ["is_disabled"],
        "searchableAttributes": ["name"],
        "sortableAttributes": ["name", "color"],
    }

    @classmethod
    def build_object(cls, instance: Tag) -> Dict[str, Any]:
        return {
            "id": instance.id,
            "name": instance.name,
            "color": instance.color,
            "is_disabled": instance.is_disabled,
        }

    @classmethod
    def index_name(cls) -> str:
        return "tags"

# Call
TagIndexer.maybe_create_index()
```

## 📕 Available modules

This library contains the following importable modules:

```python
# The main indexer
from django_meilisearch_indexer.indexers import MeilisearchModelIndexer

# Some serializers for your API
from django_meilisearch_indexer.serializers import (
    MeilisearchOnlyHitsResponseSerializer,
    MeilisearchSearchResultsSerializer,
    MeilisearchSimpleSearchSerializer,
)

# Lots of typing classes
from django_meilisearch_indexer.types import (
    Faceting,
    MeilisearchFilters,
    MeilisearchFilterValue,
    MeilisearchSearchHits,
    MeilisearchSearchParameters,
    MeilisearchSearchResults,
    MeilisearchSettings,
    MinWordSizeForTypos,
    Pagination,
    Precision,
    RankingRule,
    TypoTolerance,
)
```

## 🍜 Recipes

### Create indexes on boot

Generate your indexes on boot using `AppConfig.ready()`.

```python
class TagConfig(AppConfig):
    name = "tags"

    def ready(self) -> None:
        from django.conf import settings
        from tags.indexers import TagIndexer

        if settings.IS_RUNNING_MYPY or settings.ENVIRONMENT == "test":
            return

        TagIndexer.maybe_create_index()
```

### Async actions with celery

Make your indexation asynchronous using `celery` and `rabbitmq`.

```python
from typing import Dict, List
from celery import shared_task
from django.conf import settings
from django.db.models import Q

@shared_task(queue=settings.RABBITMQ_USER_QUEUE)
def index_tags(ids: List[int]) -> Dict[str, str]:
    from tags.indexers import TagIndexer

    TagIndexer.index_from_query(Q(pk__in=ids))
    return {"result": "ok"}

# ...
index_tags.s(ids).apply_async(countdown=5)
```

### Mock for testing

For testing, you'll need to mock the following tasks:

```python
from unittest import TestCase
from unittest.mock import patch

class TagTestCase(TestCase):
    def setUp(self) -> None:
        super().setUp()
        self._mock_indexers()
        self._mock_celery_tasks()

    def _mock_indexers(self) -> None:
        """
        Patches the `index_name` functions of all indexers.
        This allows running tests against a Meilisearch server
        without overwriting the actual index.
        """
        self.indexer_mocks = [
            patch(
                "tags.indexers.TagIndexer.index_name",
                return_value="test_tags",
            ).start(),
        ]

    # If you are using celery tasks
    def _mock_celery_tasks(self) -> None:
        """Patches the celery tasks in both forms: `delay` and `apply_async`."""
        names = [
            "tags.tasks.index_tags.delay",
            "tags.tasks.index_tags.apply_async",
        ]
        self.celery_task_mocks = {name: patch(name).start() for name in names}

    def test_something(self):
        # ...
        self.celery_task_mocks[
            "tags.tasks.index_tags.apply_async"
        ].assert_called_once_with(([recipe.id],), {}, countdown=5)
        # ...
```

### Admin actions

To trigger your indexations through the django admin interface,
you can add a custom action like so:

```python
from django.contrib import admin
from django.db.models import QuerySet
from django.http import HttpRequest, HttpResponseRedirect
from tags import tasks
from tags.models import Tag

@admin.action(description="[Meilisearch] Index selected item(s)")
def index_multiple(
    model_admin: admin.ModelAdmin,
    request: HttpRequest,
    queryset: QuerySet,
) -> HttpResponseRedirect:
    ids = list(queryset.values_list("id", flat=True))
    model_admin.index_task.s(ids).apply_async(countdown=5)
    model_admin.message_user(request, f"Indexing {len(ids)} items(s) on Meilisearch")
    return HttpResponseRedirect(request.get_full_path())


class TagAdmin(admin.ModelAdmin):
    index_task = tasks.index_tags
    extra_actions = [index_multiple]
    # ...
```

## 🔗 Useful links

- [Want to contribute?](CONTRIBUTING.md)
- [See what's new!](CHANGELOG.md)

            

Raw data

            {
    "_id": null,
    "home_page": null,
    "name": "django-meilisearch-indexer",
    "maintainer": null,
    "docs_url": null,
    "requires_python": ">=3.9",
    "maintainer_email": "Jordan Kowal <kowaljordan@gmail.com>",
    "keywords": "django, meilisearch, indexer",
    "author": null,
    "author_email": "Jordan Kowal <kowaljordan@gmail.com>",
    "download_url": "https://files.pythonhosted.org/packages/e5/b7/3708633a32d5642fa4f971b4eaa202534fa10cfdc60b24d3cfa39cc15dba/django_meilisearch_indexer-1.0.1.tar.gz",
    "platform": null,
    "description": "# \u2728 Django Meilisearch Indexer \u2728\n\n![Code quality](https://github.com/Jordan-Kowal/django-meilisearch-indexer/actions/workflows/code_quality.yml/badge.svg?branch=main)\n![Tests](https://github.com/Jordan-Kowal/django-meilisearch-indexer/actions/workflows/tests.yml/badge.svg?branch=main)\n![Build](https://github.com/Jordan-Kowal/django-meilisearch-indexer/actions/workflows/publish_package.yml/badge.svg?event=release)\n![Coverage](https://badgen.net/badge/coverage/%3E90%25/pink)\n![Tag](https://badgen.net/badge/tag/1.0.1/orange)\n![Python](https://badgen.net/badge/python/3.9%20|%203.10%20|%203.11%20|%203.12|%203.13)\n![Licence](https://badgen.net/badge/licence/MIT)\n\n- [\u2728 Django Meilisearch Indexer \u2728](#-django-meilisearch-indexer-)\n  - [\ud83d\udcbb How to install](#-how-to-install)\n  - [\u26a1 Quick start](#-quick-start)\n  - [\ud83d\udcd5 Available modules](#-available-modules)\n  - [\ud83c\udf5c Recipes](#-recipes)\n    - [Create indexes on boot](#create-indexes-on-boot)\n    - [Async actions with celery](#async-actions-with-celery)\n    - [Mock for testing](#mock-for-testing)\n    - [Admin actions](#admin-actions)\n  - [\ud83d\udd17 Useful links](#-useful-links)\n\nProvides a `MeilisearchModelIndexer` class to easily index django models in Meilisearch.\n\n## \ud83d\udcbb How to install\n\nThe package is available on PyPi with the name `django_meilisearch_indexer`.\nSimply run:\n\n```shell\npip install django_meilisearch_indexer\n```\n\n## \u26a1 Quick start\n\nHere's a basic example:\n\n```python\n# Imports\nfrom typing import Any, Dict\nfrom django.db import models\nfrom django_meilisearch_indexer.indexers import MeilisearchModelIndexer\n\n# Model\nclass Tag(models.Model):\n    name = models.CharField(max_length=100, unique=True)\n    color = models.CharField(max_length=100)\n    is_disabled = models.BooleanField(default=False)\n\n# Indexer\nclass TagIndexer(MeilisearchModelIndexer[Tag]):\n    MODEL_CLASS = Tag\n    PRIMARY_KEY = \"id\"\n    SETTINGS = {\n        \"filterableAttributes\": [\"is_disabled\"],\n        \"searchableAttributes\": [\"name\"],\n        \"sortableAttributes\": [\"name\", \"color\"],\n    }\n\n    @classmethod\n    def build_object(cls, instance: Tag) -> Dict[str, Any]:\n        return {\n            \"id\": instance.id,\n            \"name\": instance.name,\n            \"color\": instance.color,\n            \"is_disabled\": instance.is_disabled,\n        }\n\n    @classmethod\n    def index_name(cls) -> str:\n        return \"tags\"\n\n# Call\nTagIndexer.maybe_create_index()\n```\n\n## \ud83d\udcd5 Available modules\n\nThis library contains the following importable modules:\n\n```python\n# The main indexer\nfrom django_meilisearch_indexer.indexers import MeilisearchModelIndexer\n\n# Some serializers for your API\nfrom django_meilisearch_indexer.serializers import (\n    MeilisearchOnlyHitsResponseSerializer,\n    MeilisearchSearchResultsSerializer,\n    MeilisearchSimpleSearchSerializer,\n)\n\n# Lots of typing classes\nfrom django_meilisearch_indexer.types import (\n    Faceting,\n    MeilisearchFilters,\n    MeilisearchFilterValue,\n    MeilisearchSearchHits,\n    MeilisearchSearchParameters,\n    MeilisearchSearchResults,\n    MeilisearchSettings,\n    MinWordSizeForTypos,\n    Pagination,\n    Precision,\n    RankingRule,\n    TypoTolerance,\n)\n```\n\n## \ud83c\udf5c Recipes\n\n### Create indexes on boot\n\nGenerate your indexes on boot using `AppConfig.ready()`.\n\n```python\nclass TagConfig(AppConfig):\n    name = \"tags\"\n\n    def ready(self) -> None:\n        from django.conf import settings\n        from tags.indexers import TagIndexer\n\n        if settings.IS_RUNNING_MYPY or settings.ENVIRONMENT == \"test\":\n            return\n\n        TagIndexer.maybe_create_index()\n```\n\n### Async actions with celery\n\nMake your indexation asynchronous using `celery` and `rabbitmq`.\n\n```python\nfrom typing import Dict, List\nfrom celery import shared_task\nfrom django.conf import settings\nfrom django.db.models import Q\n\n@shared_task(queue=settings.RABBITMQ_USER_QUEUE)\ndef index_tags(ids: List[int]) -> Dict[str, str]:\n    from tags.indexers import TagIndexer\n\n    TagIndexer.index_from_query(Q(pk__in=ids))\n    return {\"result\": \"ok\"}\n\n# ...\nindex_tags.s(ids).apply_async(countdown=5)\n```\n\n### Mock for testing\n\nFor testing, you'll need to mock the following tasks:\n\n```python\nfrom unittest import TestCase\nfrom unittest.mock import patch\n\nclass TagTestCase(TestCase):\n    def setUp(self) -> None:\n        super().setUp()\n        self._mock_indexers()\n        self._mock_celery_tasks()\n\n    def _mock_indexers(self) -> None:\n        \"\"\"\n        Patches the `index_name` functions of all indexers.\n        This allows running tests against a Meilisearch server\n        without overwriting the actual index.\n        \"\"\"\n        self.indexer_mocks = [\n            patch(\n                \"tags.indexers.TagIndexer.index_name\",\n                return_value=\"test_tags\",\n            ).start(),\n        ]\n\n    # If you are using celery tasks\n    def _mock_celery_tasks(self) -> None:\n        \"\"\"Patches the celery tasks in both forms: `delay` and `apply_async`.\"\"\"\n        names = [\n            \"tags.tasks.index_tags.delay\",\n            \"tags.tasks.index_tags.apply_async\",\n        ]\n        self.celery_task_mocks = {name: patch(name).start() for name in names}\n\n    def test_something(self):\n        # ...\n        self.celery_task_mocks[\n            \"tags.tasks.index_tags.apply_async\"\n        ].assert_called_once_with(([recipe.id],), {}, countdown=5)\n        # ...\n```\n\n### Admin actions\n\nTo trigger your indexations through the django admin interface,\nyou can add a custom action like so:\n\n```python\nfrom django.contrib import admin\nfrom django.db.models import QuerySet\nfrom django.http import HttpRequest, HttpResponseRedirect\nfrom tags import tasks\nfrom tags.models import Tag\n\n@admin.action(description=\"[Meilisearch] Index selected item(s)\")\ndef index_multiple(\n    model_admin: admin.ModelAdmin,\n    request: HttpRequest,\n    queryset: QuerySet,\n) -> HttpResponseRedirect:\n    ids = list(queryset.values_list(\"id\", flat=True))\n    model_admin.index_task.s(ids).apply_async(countdown=5)\n    model_admin.message_user(request, f\"Indexing {len(ids)} items(s) on Meilisearch\")\n    return HttpResponseRedirect(request.get_full_path())\n\n\nclass TagAdmin(admin.ModelAdmin):\n    index_task = tasks.index_tags\n    extra_actions = [index_multiple]\n    # ...\n```\n\n## \ud83d\udd17 Useful links\n\n- [Want to contribute?](CONTRIBUTING.md)\n- [See what's new!](CHANGELOG.md)\n",
    "bugtrack_url": null,
    "license": "MIT License  Copyright (c) 2024 Jordan Kowal  Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the \"Software\"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:  The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.  THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ",
    "summary": "Meilisearch indexer for django models and related utilities",
    "version": "1.0.1",
    "project_urls": {
        "Changelog": "https://github.com/Jordan-Kowal/django-meilisearch-indexer/blob/main/CHANGELOG.md",
        "Issues": "https://github.com/Jordan-Kowal/django-meilisearch-indexer/issues",
        "Release notes": "https://github.com/Jordan-Kowal/django-meilisearch-indexer/releases",
        "Source": "https://github.com/Jordan-Kowal/django-meilisearch-indexer"
    },
    "split_keywords": [
        "django",
        " meilisearch",
        " indexer"
    ],
    "urls": [
        {
            "comment_text": "",
            "digests": {
                "blake2b_256": "3c2fbd468bf36c01ffb2df7de03baa855f265ed8968ed413687b7189eaa2c344",
                "md5": "7872c88e2a992e961edc3d9dc74c2b40",
                "sha256": "845e6d03dd9a42a0a708328e8e728093720df51ab1e41b5b144f6a65ab6db62c"
            },
            "downloads": -1,
            "filename": "django_meilisearch_indexer-1.0.1-py3-none-any.whl",
            "has_sig": false,
            "md5_digest": "7872c88e2a992e961edc3d9dc74c2b40",
            "packagetype": "bdist_wheel",
            "python_version": "py3",
            "requires_python": ">=3.9",
            "size": 9604,
            "upload_time": "2024-11-02T14:14:40",
            "upload_time_iso_8601": "2024-11-02T14:14:40.157258Z",
            "url": "https://files.pythonhosted.org/packages/3c/2f/bd468bf36c01ffb2df7de03baa855f265ed8968ed413687b7189eaa2c344/django_meilisearch_indexer-1.0.1-py3-none-any.whl",
            "yanked": false,
            "yanked_reason": null
        },
        {
            "comment_text": "",
            "digests": {
                "blake2b_256": "e5b73708633a32d5642fa4f971b4eaa202534fa10cfdc60b24d3cfa39cc15dba",
                "md5": "dd3a4a3454fb6b325f67ce0ad5c8a234",
                "sha256": "afca2aae987e8659dd167f11ec1a61757554cb208e5eee8526c7d8655ae234db"
            },
            "downloads": -1,
            "filename": "django_meilisearch_indexer-1.0.1.tar.gz",
            "has_sig": false,
            "md5_digest": "dd3a4a3454fb6b325f67ce0ad5c8a234",
            "packagetype": "sdist",
            "python_version": "source",
            "requires_python": ">=3.9",
            "size": 11352,
            "upload_time": "2024-11-02T14:14:41",
            "upload_time_iso_8601": "2024-11-02T14:14:41.767141Z",
            "url": "https://files.pythonhosted.org/packages/e5/b7/3708633a32d5642fa4f971b4eaa202534fa10cfdc60b24d3cfa39cc15dba/django_meilisearch_indexer-1.0.1.tar.gz",
            "yanked": false,
            "yanked_reason": null
        }
    ],
    "upload_time": "2024-11-02 14:14:41",
    "github": true,
    "gitlab": false,
    "bitbucket": false,
    "codeberg": false,
    "github_user": "Jordan-Kowal",
    "github_project": "django-meilisearch-indexer",
    "travis_ci": false,
    "coveralls": false,
    "github_actions": true,
    "lcname": "django-meilisearch-indexer"
}
        
Elapsed time: 1.38562s