drf-haystack 1.9__tar.gz → 1.9.3__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- drf_haystack-1.9.3/PKG-INFO +101 -0
- {drf_haystack-1.9 → drf_haystack-1.9.3}/README.md +8 -7
- {drf_haystack-1.9 → drf_haystack-1.9.3}/docs/01_intro.rst +0 -1
- {drf_haystack-1.9 → drf_haystack-1.9.3}/docs/02_autocomplete.rst +0 -1
- drf_haystack-1.9.3/docs/conf.py +102 -0
- {drf_haystack-1.9 → drf_haystack-1.9.3}/docs/index.rst +16 -11
- {drf_haystack-1.9 → drf_haystack-1.9.3}/drf_haystack/__init__.py +1 -5
- {drf_haystack-1.9 → drf_haystack-1.9.3}/drf_haystack/fields.py +10 -22
- {drf_haystack-1.9 → drf_haystack-1.9.3}/drf_haystack/filters.py +21 -26
- {drf_haystack-1.9 → drf_haystack-1.9.3}/drf_haystack/generics.py +14 -16
- {drf_haystack-1.9 → drf_haystack-1.9.3}/drf_haystack/mixins.py +7 -13
- {drf_haystack-1.9 → drf_haystack-1.9.3}/drf_haystack/query.py +82 -81
- {drf_haystack-1.9 → drf_haystack-1.9.3}/drf_haystack/serializers.py +71 -76
- {drf_haystack-1.9 → drf_haystack-1.9.3}/drf_haystack/utils.py +2 -7
- {drf_haystack-1.9 → drf_haystack-1.9.3}/drf_haystack/viewsets.py +0 -5
- drf_haystack-1.9.3/pyproject.toml +84 -0
- drf_haystack-1.9.3/pyproject.toml.orig +78 -0
- drf_haystack-1.9.3/tests/constants.py +13 -0
- {drf_haystack-1.9 → drf_haystack-1.9.3}/tests/mixins.py +1 -5
- drf_haystack-1.9.3/tests/mockapp/__init__.py +0 -0
- drf_haystack-1.9.3/tests/mockapp/admin.py +1 -0
- {drf_haystack-1.9/tests → drf_haystack-1.9.3/tests/mockapp}/apps.py +1 -4
- drf_haystack-1.9.3/tests/mockapp/migrations/0001_initial.py +23 -0
- drf_haystack-1.9.3/tests/mockapp/migrations/0002_mockperson.py +22 -0
- drf_haystack-1.9.3/tests/mockapp/migrations/0003_mockpet.py +23 -0
- {drf_haystack-1.9 → drf_haystack-1.9.3}/tests/mockapp/migrations/0004_load_fixtures.py +8 -13
- {drf_haystack-1.9 → drf_haystack-1.9.3}/tests/mockapp/migrations/0005_mockperson_birthdate.py +4 -6
- drf_haystack-1.9.3/tests/mockapp/migrations/0006_mockallfield.py +27 -0
- drf_haystack-1.9.3/tests/mockapp/migrations/__init__.py +0 -0
- {drf_haystack-1.9 → drf_haystack-1.9.3}/tests/mockapp/models.py +26 -19
- {drf_haystack-1.9 → drf_haystack-1.9.3}/tests/mockapp/search_indexes.py +10 -24
- {drf_haystack-1.9 → drf_haystack-1.9.3}/tests/mockapp/serializers.py +19 -24
- {drf_haystack-1.9 → drf_haystack-1.9.3}/tests/mockapp/templates/search/indexes/mockapp/mocklocation_text.txt +1 -1
- drf_haystack-1.9.3/tests/mockapp/templates/search/indexes/mockapp/mockpet_text.txt +1 -0
- {drf_haystack-1.9 → drf_haystack-1.9.3}/tests/mockapp/views.py +7 -12
- drf_haystack-1.9.3/tests/settings.py +100 -0
- {drf_haystack-1.9 → drf_haystack-1.9.3}/tests/test_filters.py +93 -82
- {drf_haystack-1.9 → drf_haystack-1.9.3}/tests/test_serializers.py +130 -128
- drf_haystack-1.9.3/tests/test_utils.py +43 -0
- {drf_haystack-1.9 → drf_haystack-1.9.3}/tests/test_viewsets.py +27 -72
- {drf_haystack-1.9 → drf_haystack-1.9.3}/tests/urls.py +1 -6
- {drf_haystack-1.9 → drf_haystack-1.9.3}/tests/wsgi.py +2 -0
- drf_haystack-1.9/MANIFEST.in +0 -6
- drf_haystack-1.9/PKG-INFO +0 -25
- drf_haystack-1.9/Pipfile +0 -24
- drf_haystack-1.9/docs/conf.py +0 -178
- drf_haystack-1.9/drf_haystack.egg-info/PKG-INFO +0 -25
- drf_haystack-1.9/drf_haystack.egg-info/SOURCES.txt +0 -71
- drf_haystack-1.9/drf_haystack.egg-info/dependency_links.txt +0 -1
- drf_haystack-1.9/drf_haystack.egg-info/not-zip-safe +0 -1
- drf_haystack-1.9/drf_haystack.egg-info/requires.txt +0 -4
- drf_haystack-1.9/drf_haystack.egg-info/top_level.txt +0 -1
- drf_haystack-1.9/setup.cfg +0 -22
- drf_haystack-1.9/setup.py +0 -57
- drf_haystack-1.9/tests/__init__.py +0 -54
- drf_haystack-1.9/tests/constants.py +0 -17
- drf_haystack-1.9/tests/mockapp/admin.py +0 -3
- drf_haystack-1.9/tests/mockapp/migrations/0001_initial.py +0 -29
- drf_haystack-1.9/tests/mockapp/migrations/0002_mockperson.py +0 -27
- drf_haystack-1.9/tests/mockapp/migrations/0003_mockpet.py +0 -28
- drf_haystack-1.9/tests/mockapp/migrations/0006_mockallfield.py +0 -29
- drf_haystack-1.9/tests/mockapp/templates/search/indexes/mockapp/mockpet_text.txt +0 -1
- drf_haystack-1.9/tests/run_tests.py +0 -29
- drf_haystack-1.9/tests/settings.py +0 -122
- drf_haystack-1.9/tests/test_utils.py +0 -55
- drf_haystack-1.9/tox.ini +0 -427
- {drf_haystack-1.9 → drf_haystack-1.9.3}/LICENSE.txt +0 -0
- {drf_haystack-1.9 → drf_haystack-1.9.3}/docs/03_geospatial.rst +0 -0
- {drf_haystack-1.9 → drf_haystack-1.9.3}/docs/04_highlighting.rst +0 -0
- {drf_haystack-1.9 → drf_haystack-1.9.3}/docs/05_more_like_this.rst +0 -0
- {drf_haystack-1.9 → drf_haystack-1.9.3}/docs/06_term_boost.rst +0 -0
- {drf_haystack-1.9 → drf_haystack-1.9.3}/docs/07_faceting.rst +0 -0
- {drf_haystack-1.9 → drf_haystack-1.9.3}/docs/08_permissions.rst +0 -0
- {drf_haystack-1.9 → drf_haystack-1.9.3}/docs/09_multiple_indexes.rst +0 -0
- {drf_haystack-1.9 → drf_haystack-1.9.3}/docs/10_tips_n_tricks.rst +0 -0
- {drf_haystack-1.9 → drf_haystack-1.9.3}/docs/Makefile +0 -0
- {drf_haystack-1.9 → drf_haystack-1.9.3}/docs/apidoc/drf_haystack.rst +0 -0
- {drf_haystack-1.9 → drf_haystack-1.9.3}/docs/apidoc/modules.rst +0 -0
- {drf_haystack-1.9 → drf_haystack-1.9.3}/docs/make.bat +0 -0
- {drf_haystack-1.9 → drf_haystack-1.9.3}/drf_haystack/constants.py +0 -0
- {drf_haystack-1.9/tests/mockapp → drf_haystack-1.9.3/tests}/__init__.py +0 -0
- /drf_haystack-1.9/tests/mockapp/migrations/__init__.py → /drf_haystack-1.9.3/tests/logs/.gitkeep +0 -0
- {drf_haystack-1.9 → drf_haystack-1.9.3}/tests/mockapp/fixtures/mockallfield.json +0 -0
- {drf_haystack-1.9 → drf_haystack-1.9.3}/tests/mockapp/fixtures/mocklocation.json +0 -0
- {drf_haystack-1.9 → drf_haystack-1.9.3}/tests/mockapp/fixtures/mockperson.json +0 -0
- {drf_haystack-1.9 → drf_haystack-1.9.3}/tests/mockapp/fixtures/mockpet.json +0 -0
- {drf_haystack-1.9 → drf_haystack-1.9.3}/tests/mockapp/templates/search/indexes/mockapp/mockperson_text.txt +0 -0
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: drf-haystack
|
|
3
|
+
Version: 1.9.3
|
|
4
|
+
Summary: Makes Haystack play nice with Django REST Framework
|
|
5
|
+
Author: Dhaval Gojiya, Rolf Håvard Blindheim, Ülgen Sarıkavak
|
|
6
|
+
Author-email: Dhaval Gojiya <dhavalgojiya10@gmail.com>, Rolf Håvard Blindheim <rhblind@gmail.com>, Ülgen Sarıkavak <foss@ulgenwanders.net>
|
|
7
|
+
License-Expression: MIT
|
|
8
|
+
Classifier: Development Status :: 3 - Alpha
|
|
9
|
+
Classifier: Environment :: Web Environment
|
|
10
|
+
Classifier: Framework :: Django
|
|
11
|
+
Classifier: Framework :: Django :: 5.2
|
|
12
|
+
Classifier: Framework :: Django :: 6.0
|
|
13
|
+
Classifier: Framework :: Django :: 6.1
|
|
14
|
+
Classifier: Intended Audience :: Developers
|
|
15
|
+
Classifier: Operating System :: OS Independent
|
|
16
|
+
Classifier: Programming Language :: Python :: 3 :: Only
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
18
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
19
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
20
|
+
Classifier: Programming Language :: Python :: 3.14
|
|
21
|
+
Classifier: Topic :: Internet :: WWW/HTTP :: Indexing/Search
|
|
22
|
+
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
|
23
|
+
Requires-Dist: django>=5.2,<6.2
|
|
24
|
+
Requires-Dist: django-haystack>=3.3.0,<4
|
|
25
|
+
Requires-Dist: djangorestframework>=3.16
|
|
26
|
+
Requires-Dist: python-dateutil
|
|
27
|
+
Requires-Python: >=3.11, <3.15
|
|
28
|
+
Project-URL: Documentation, https://drf-haystack.readthedocs.io
|
|
29
|
+
Project-URL: Homepage, https://github.com/django-commons/drf-haystack
|
|
30
|
+
Project-URL: Issues, https://github.com/django-commons/drf-haystack/issues
|
|
31
|
+
Project-URL: Repository, https://github.com/django-commons/drf-haystack.git
|
|
32
|
+
Description-Content-Type: text/markdown
|
|
33
|
+
|
|
34
|
+
Haystack for Django REST Framework
|
|
35
|
+
==================================
|
|
36
|
+
|
|
37
|
+
Build status
|
|
38
|
+
------------
|
|
39
|
+
|
|
40
|
+
[](https://coveralls.io/github/django-commons/drf-haystack?branch=main)
|
|
41
|
+
[](https://badge.fury.io/py/drf-haystack)
|
|
42
|
+
[](http://drf-haystack.readthedocs.io/en/latest/?badge=latest)
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
About
|
|
46
|
+
-----
|
|
47
|
+
|
|
48
|
+
Small library which tries to simplify integration of Haystack with Django REST Framework.
|
|
49
|
+
Fresh [documentation available](https://drf-haystack.readthedocs.io/en/latest/) on Read the docs!
|
|
50
|
+
|
|
51
|
+
Supported versions
|
|
52
|
+
------------------
|
|
53
|
+
|
|
54
|
+
- Python >=3.11, <3.15
|
|
55
|
+
- Django >=5.2, <6.2
|
|
56
|
+
- Haystack >=3.3.0, <4
|
|
57
|
+
- Django REST Framework >=3.16
|
|
58
|
+
- Elasticsearch >=7.0.0, <8
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
Installation
|
|
62
|
+
------------
|
|
63
|
+
|
|
64
|
+
$ pip install drf-haystack
|
|
65
|
+
|
|
66
|
+
Supported features
|
|
67
|
+
------------------
|
|
68
|
+
We aim to support most features Haystack does (or at least those which can be used in a REST API).
|
|
69
|
+
Currently, we support:
|
|
70
|
+
|
|
71
|
+
- Autocomplete
|
|
72
|
+
- Boost (Experimental)
|
|
73
|
+
- Faceting
|
|
74
|
+
- Geo Spatial Search
|
|
75
|
+
- Highlighting
|
|
76
|
+
- More Like This
|
|
77
|
+
|
|
78
|
+
Show me more!
|
|
79
|
+
-------------
|
|
80
|
+
|
|
81
|
+
```python
|
|
82
|
+
from drf_haystack.serializers import HaystackSerializer
|
|
83
|
+
from drf_haystack.viewsets import HaystackViewSet
|
|
84
|
+
|
|
85
|
+
from myapp.search_indexes import PersonIndex # You would define this Index normally as per Haystack's documentation
|
|
86
|
+
|
|
87
|
+
|
|
88
|
+
# Serializer
|
|
89
|
+
class PersonSearchSerializer(HaystackSerializer):
|
|
90
|
+
class Meta:
|
|
91
|
+
index_classes = [PersonIndex]
|
|
92
|
+
fields = ["firstname", "lastname", "full_name"]
|
|
93
|
+
|
|
94
|
+
|
|
95
|
+
# ViewSet
|
|
96
|
+
class PersonSearchViewSet(HaystackViewSet):
|
|
97
|
+
index_models = [Person]
|
|
98
|
+
serializer_class = PersonSerializer
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
That's it, you're good to go. Hook it up to a DRF router and happy searching!
|
|
@@ -4,8 +4,7 @@ Haystack for Django REST Framework
|
|
|
4
4
|
Build status
|
|
5
5
|
------------
|
|
6
6
|
|
|
7
|
-
[](https://coveralls.io/github/rhblind/drf-haystack?branch=master)
|
|
7
|
+
[](https://coveralls.io/github/django-commons/drf-haystack?branch=main)
|
|
9
8
|
[](https://badge.fury.io/py/drf-haystack)
|
|
10
9
|
[](http://drf-haystack.readthedocs.io/en/latest/?badge=latest)
|
|
11
10
|
|
|
@@ -19,11 +18,11 @@ Fresh [documentation available](https://drf-haystack.readthedocs.io/en/latest/)
|
|
|
19
18
|
Supported versions
|
|
20
19
|
------------------
|
|
21
20
|
|
|
22
|
-
- Python 3.
|
|
23
|
-
- Django >=
|
|
24
|
-
- Haystack >=
|
|
25
|
-
- Django REST Framework >=3.
|
|
26
|
-
-
|
|
21
|
+
- Python >=3.11, <3.15
|
|
22
|
+
- Django >=5.2, <6.2
|
|
23
|
+
- Haystack >=3.3.0, <4
|
|
24
|
+
- Django REST Framework >=3.16
|
|
25
|
+
- Elasticsearch >=7.0.0, <8
|
|
27
26
|
|
|
28
27
|
|
|
29
28
|
Installation
|
|
@@ -52,12 +51,14 @@ from drf_haystack.viewsets import HaystackViewSet
|
|
|
52
51
|
|
|
53
52
|
from myapp.search_indexes import PersonIndex # You would define this Index normally as per Haystack's documentation
|
|
54
53
|
|
|
54
|
+
|
|
55
55
|
# Serializer
|
|
56
56
|
class PersonSearchSerializer(HaystackSerializer):
|
|
57
57
|
class Meta:
|
|
58
58
|
index_classes = [PersonIndex]
|
|
59
59
|
fields = ["firstname", "lastname", "full_name"]
|
|
60
60
|
|
|
61
|
+
|
|
61
62
|
# ViewSet
|
|
62
63
|
class PersonSearchViewSet(HaystackViewSet):
|
|
63
64
|
index_models = [Person]
|
|
@@ -209,4 +209,3 @@ The default keyword is ``not``, but is configurable via settings using ``DRF_HAY
|
|
|
209
209
|
http://example.com/api/v1/location/search/?city__not=Oslo
|
|
210
210
|
http://example.com/api/v1/location/search/?city__not__contains=Los
|
|
211
211
|
http://example.com/api/v1/location/search/?city__contains=Los&city__not__contains=Angeles
|
|
212
|
-
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
import os
|
|
2
|
+
import sys
|
|
3
|
+
from datetime import UTC, datetime
|
|
4
|
+
from pathlib import Path
|
|
5
|
+
|
|
6
|
+
import django
|
|
7
|
+
|
|
8
|
+
from drf_haystack import __version__
|
|
9
|
+
|
|
10
|
+
# If extensions (or modules to document with autodoc) are in another directory,
|
|
11
|
+
# add these directories to sys.path here. If the directory is relative to the
|
|
12
|
+
# documentation root, use os.path.abspath to make it absolute, like shown here.
|
|
13
|
+
sys.path.insert(0, str(Path("..").resolve()))
|
|
14
|
+
os.environ["RUNTIME_ENV"] = "TESTSUITE"
|
|
15
|
+
os.environ["DJANGO_SETTINGS_MODULE"] = "tests.settings"
|
|
16
|
+
|
|
17
|
+
django.setup()
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
# -- General configuration ------------------------------------------------
|
|
21
|
+
|
|
22
|
+
# Add any Sphinx extension module names here, as strings. They can be
|
|
23
|
+
# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom
|
|
24
|
+
# ones.
|
|
25
|
+
extensions = [
|
|
26
|
+
"sphinx.ext.autodoc",
|
|
27
|
+
"sphinx.ext.intersphinx",
|
|
28
|
+
"sphinx.ext.extlinks",
|
|
29
|
+
# TODO: Is this still needed?
|
|
30
|
+
"sphinx.ext.todo",
|
|
31
|
+
"sphinx.ext.viewcode",
|
|
32
|
+
]
|
|
33
|
+
|
|
34
|
+
# Add any paths that contain templates here, relative to this directory.
|
|
35
|
+
templates_path = ["_templates"]
|
|
36
|
+
|
|
37
|
+
# The suffix(es) of source filenames.
|
|
38
|
+
# You can specify multiple suffix as a list of string:
|
|
39
|
+
#
|
|
40
|
+
# source_suffix = ['.rst', '.md']
|
|
41
|
+
source_suffix = ".rst"
|
|
42
|
+
|
|
43
|
+
# The master toctree document.
|
|
44
|
+
master_doc = "index"
|
|
45
|
+
|
|
46
|
+
# General information about the project.
|
|
47
|
+
project = "drf_haystack"
|
|
48
|
+
copyright = f"{datetime.now(tz=UTC).date().year}, Rolf Håvard Blindheim"
|
|
49
|
+
author = "Rolf Håvard Blindheim"
|
|
50
|
+
|
|
51
|
+
# The version info for the project you're documenting, acts as replacement for
|
|
52
|
+
# |version| and |release|, also used in various other places throughout the
|
|
53
|
+
# built documents.
|
|
54
|
+
#
|
|
55
|
+
# The full version, including alpha/beta/rc tags.
|
|
56
|
+
version = release = __version__
|
|
57
|
+
|
|
58
|
+
# The language for content autogenerated by Sphinx. Refer to documentation
|
|
59
|
+
# for a list of supported languages.
|
|
60
|
+
#
|
|
61
|
+
# This is also used if you do content translation via gettext catalogs.
|
|
62
|
+
# Usually you set "language" from the command line for these cases.
|
|
63
|
+
language = "en"
|
|
64
|
+
|
|
65
|
+
# List of patterns, relative to source directory, that match files and
|
|
66
|
+
# directories to ignore when looking for source files.
|
|
67
|
+
# This patterns also effect to html_static_path and html_extra_path
|
|
68
|
+
exclude_patterns = ["_build", "Thumbs.db", ".DS_Store"]
|
|
69
|
+
|
|
70
|
+
# The name of the Pygments (syntax highlighting) style to use.
|
|
71
|
+
pygments_style = "sphinx"
|
|
72
|
+
|
|
73
|
+
# If true, `todo` and `todoList` produce output, else they produce nothing.
|
|
74
|
+
todo_include_todos = True
|
|
75
|
+
|
|
76
|
+
|
|
77
|
+
# -- Options for HTML output ----------------------------------------------
|
|
78
|
+
|
|
79
|
+
# The theme to use for HTML and HTML Help pages. See the documentation for
|
|
80
|
+
# a list of builtin themes.
|
|
81
|
+
#
|
|
82
|
+
html_theme = "sphinx_rtd_theme"
|
|
83
|
+
|
|
84
|
+
|
|
85
|
+
# -- Options for HTMLHelp output ------------------------------------------
|
|
86
|
+
|
|
87
|
+
# Output file base name for HTML help builder.
|
|
88
|
+
htmlhelp_basename = "drfhaystackdoc"
|
|
89
|
+
|
|
90
|
+
# Example configuration for intersphinx: refer to the Python standard library.
|
|
91
|
+
intersphinx_mapping = {
|
|
92
|
+
"python": ("https://docs.python.org/", None),
|
|
93
|
+
"django": ("https://django.readthedocs.io/en/latest/", None),
|
|
94
|
+
"haystack": ("https://django-haystack.readthedocs.io/en/latest/", None),
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
# Configurations for extlinks
|
|
98
|
+
extlinks = {
|
|
99
|
+
"drf-pr": ("https://github.com/django-commons/drf-haystack/pull/%s", "PR#%s"),
|
|
100
|
+
"drf-issue": ("https://github.com/django-commons/drf-haystack/issues/%s", "#%s"),
|
|
101
|
+
"haystack-issue": ("https://github.com/django-haystack/django-haystack/issues/%s", "#%s"),
|
|
102
|
+
}
|
|
@@ -28,7 +28,7 @@ Features
|
|
|
28
28
|
|
|
29
29
|
Supported Python and Django versions:
|
|
30
30
|
|
|
31
|
-
- Python 3.
|
|
31
|
+
- Python >=3.11, <3.15
|
|
32
32
|
- `All supported versions of Django <https://www.djangoproject.com/download/#supported-versions>`_
|
|
33
33
|
|
|
34
34
|
|
|
@@ -44,8 +44,8 @@ It's in the cheese shop!
|
|
|
44
44
|
Requirements
|
|
45
45
|
============
|
|
46
46
|
- A Supported Django install
|
|
47
|
-
- Django REST Framework v3.
|
|
48
|
-
- Haystack
|
|
47
|
+
- Django REST Framework v3.16 and later
|
|
48
|
+
- Haystack v3.3.0 and later, below v4
|
|
49
49
|
- A supported search engine such as Solr, Elasticsearch, Whoosh, etc.
|
|
50
50
|
- Python bindings for the chosen backend (see below).
|
|
51
51
|
- (geopy and libgeos if you want to use geo spatial filtering)
|
|
@@ -58,18 +58,17 @@ You will also need to install python bindings for the search engine you'll use.
|
|
|
58
58
|
Elasticsearch
|
|
59
59
|
.............
|
|
60
60
|
|
|
61
|
-
See haystack `Elasticsearch <https://django-haystack.readthedocs.io/en/
|
|
61
|
+
See haystack `Elasticsearch <https://django-haystack.readthedocs.io/en/v3.3.0/installing_search_engines.html#elasticsearch>`_
|
|
62
62
|
docs for details
|
|
63
63
|
|
|
64
64
|
.. code-block:: none
|
|
65
65
|
|
|
66
|
-
$ pip install elasticsearch
|
|
67
|
-
$ pip install elasticsearch>=2.0.0,<3.0.0 # For Elasticsearch 2.x
|
|
66
|
+
$ pip install elasticsearch>=7.0.0,<8.0.0 # For Elasticsearch 7.x
|
|
68
67
|
|
|
69
68
|
Solr
|
|
70
69
|
....
|
|
71
70
|
|
|
72
|
-
See haystack `Solr <https://django-haystack.readthedocs.io/en/
|
|
71
|
+
See haystack `Solr <https://django-haystack.readthedocs.io/en/v3.3.0/installing_search_engines.html#solr>`_
|
|
73
72
|
docs for details.
|
|
74
73
|
|
|
75
74
|
.. code-block:: none
|
|
@@ -79,7 +78,7 @@ docs for details.
|
|
|
79
78
|
Whoosh
|
|
80
79
|
......
|
|
81
80
|
|
|
82
|
-
See haystack `Whoosh <https://django-haystack.readthedocs.io/en/
|
|
81
|
+
See haystack `Whoosh <https://django-haystack.readthedocs.io/en/v3.3.0/installing_search_engines.html#whoosh>`_
|
|
83
82
|
docs for details.
|
|
84
83
|
|
|
85
84
|
.. code-block:: none
|
|
@@ -89,7 +88,7 @@ docs for details.
|
|
|
89
88
|
Xapian
|
|
90
89
|
......
|
|
91
90
|
|
|
92
|
-
See haystack `Xapian <https://django-haystack.readthedocs.io/en/
|
|
91
|
+
See haystack `Xapian <https://django-haystack.readthedocs.io/en/v3.3.0/installing_search_engines.html#xapian>`_
|
|
93
92
|
docs for details.
|
|
94
93
|
|
|
95
94
|
|
|
@@ -100,14 +99,20 @@ This library has mainly been written by `me <https://github.com/rhblind>`_ while
|
|
|
100
99
|
at `Inonit <https://github.com/inonit>`_. I have also had some help from these amazing people!
|
|
101
100
|
Thanks guys!
|
|
102
101
|
|
|
103
|
-
- See the full list of `contributors <https://github.com/
|
|
102
|
+
- See the full list of `contributors <https://github.com/django-commons/drf-haystack/graphs/contributors>`_.
|
|
104
103
|
|
|
105
104
|
Changelog
|
|
106
105
|
=========
|
|
107
106
|
|
|
107
|
+
v1.9.1
|
|
108
|
+
------
|
|
109
|
+
*Release date: 2024-11-05*
|
|
110
|
+
|
|
111
|
+
- Add deprecation warning about potential future sunsetting of the project, due to lack of active maintainers.
|
|
112
|
+
|
|
108
113
|
v1.9
|
|
109
114
|
------
|
|
110
|
-
*Release date:
|
|
115
|
+
*Release date: 2024-11-04*
|
|
111
116
|
|
|
112
117
|
- Return an empty query object instead of an empty list when no filter is passed (:drf-issue:`202`).
|
|
113
118
|
- add "id" as an optional value of the serializer fields returning the haystack internal id (:drf-issue:`193`).
|
|
@@ -1,17 +1,12 @@
|
|
|
1
|
-
# -*- coding: utf-8 -*-
|
|
2
|
-
|
|
3
|
-
from __future__ import absolute_import, unicode_literals
|
|
4
|
-
|
|
5
|
-
import six
|
|
6
1
|
from rest_framework import fields
|
|
7
2
|
|
|
8
3
|
|
|
9
|
-
class DRFHaystackFieldMixin
|
|
4
|
+
class DRFHaystackFieldMixin:
|
|
10
5
|
prefix_field_names = False
|
|
11
6
|
|
|
12
7
|
def __init__(self, **kwargs):
|
|
13
|
-
self.prefix_field_names = kwargs.pop(
|
|
14
|
-
super(
|
|
8
|
+
self.prefix_field_names = kwargs.pop("prefix_field_names", False)
|
|
9
|
+
super().__init__(**kwargs)
|
|
15
10
|
|
|
16
11
|
def bind(self, field_name, parent):
|
|
17
12
|
"""
|
|
@@ -25,10 +20,9 @@ class DRFHaystackFieldMixin(object):
|
|
|
25
20
|
# 'source' argument has been used. For example:
|
|
26
21
|
# my_field = serializer.CharField(source='my_field')
|
|
27
22
|
assert self.source != field_name, (
|
|
28
|
-
"It is redundant to specify `source='
|
|
29
|
-
"serializer '
|
|
30
|
-
"Remove the `source` keyword argument."
|
|
31
|
-
(field_name, self.__class__.__name__, parent.__class__.__name__)
|
|
23
|
+
f"It is redundant to specify `source='{field_name}'` on field '{self.__class__.__name__}' in "
|
|
24
|
+
f"serializer '{parent.__class__.__name__}', because it is the same as the field name. "
|
|
25
|
+
"Remove the `source` keyword argument."
|
|
32
26
|
)
|
|
33
27
|
|
|
34
28
|
self.field_name = field_name
|
|
@@ -36,7 +30,7 @@ class DRFHaystackFieldMixin(object):
|
|
|
36
30
|
|
|
37
31
|
# `self.label` should default to being based on the field name.
|
|
38
32
|
if self.label is None:
|
|
39
|
-
self.label = field_name.replace(
|
|
33
|
+
self.label = field_name.replace("_", " ").capitalize()
|
|
40
34
|
|
|
41
35
|
# self.source should default to being the same as the field name.
|
|
42
36
|
if self.source is None:
|
|
@@ -44,10 +38,10 @@ class DRFHaystackFieldMixin(object):
|
|
|
44
38
|
|
|
45
39
|
# self.source_attrs is a list of attributes that need to be looked up
|
|
46
40
|
# when serializing the instance, or populating the validated data.
|
|
47
|
-
if self.source ==
|
|
41
|
+
if self.source == "*":
|
|
48
42
|
self.source_attrs = []
|
|
49
43
|
else:
|
|
50
|
-
self.source_attrs = self.source.split(
|
|
44
|
+
self.source_attrs = self.source.split(".")
|
|
51
45
|
|
|
52
46
|
def convert_field_name(self, field_name):
|
|
53
47
|
if not self.prefix_field_names:
|
|
@@ -95,11 +89,7 @@ class FacetDictField(fields.DictField):
|
|
|
95
89
|
"""
|
|
96
90
|
|
|
97
91
|
def to_representation(self, value):
|
|
98
|
-
return
|
|
99
|
-
[(six.text_type(key), self.child.to_representation(key, val))
|
|
100
|
-
for key, val in value.items()
|
|
101
|
-
]
|
|
102
|
-
)
|
|
92
|
+
return {str(key): self.child.to_representation(key, val) for key, val in value.items()}
|
|
103
93
|
|
|
104
94
|
|
|
105
95
|
class FacetListField(fields.ListField):
|
|
@@ -110,5 +100,3 @@ class FacetListField(fields.ListField):
|
|
|
110
100
|
|
|
111
101
|
def to_representation(self, key, data):
|
|
112
102
|
return [self.child.to_representation(key, item) for item in data]
|
|
113
|
-
|
|
114
|
-
|
|
@@ -1,16 +1,11 @@
|
|
|
1
|
-
# -*- coding: utf-8 -*-
|
|
2
|
-
|
|
3
|
-
from __future__ import absolute_import, unicode_literals
|
|
4
|
-
|
|
5
1
|
import operator
|
|
6
|
-
import six
|
|
7
2
|
from functools import reduce
|
|
8
3
|
|
|
9
4
|
from django.core.exceptions import ImproperlyConfigured
|
|
10
5
|
from haystack.query import SearchQuerySet
|
|
11
6
|
from rest_framework.filters import BaseFilterBackend, OrderingFilter
|
|
12
7
|
|
|
13
|
-
from drf_haystack.query import BoostQueryBuilder,
|
|
8
|
+
from drf_haystack.query import BoostQueryBuilder, FacetQueryBuilder, FilterQueryBuilder, SpatialQueryBuilder
|
|
14
9
|
|
|
15
10
|
|
|
16
11
|
class BaseHaystackFilterBackend(BaseFilterBackend):
|
|
@@ -44,7 +39,7 @@ class BaseHaystackFilterBackend(BaseFilterBackend):
|
|
|
44
39
|
Get the query builder instance and return constructed query filters.
|
|
45
40
|
"""
|
|
46
41
|
query_builder = self.get_query_builder(backend=self, view=view)
|
|
47
|
-
return query_builder.build_query(**(filters
|
|
42
|
+
return query_builder.build_query(**(filters or {}))
|
|
48
43
|
|
|
49
44
|
def process_filters(self, filters, queryset, view):
|
|
50
45
|
"""
|
|
@@ -61,7 +56,7 @@ class BaseHaystackFilterBackend(BaseFilterBackend):
|
|
|
61
56
|
return self.apply_filters(
|
|
62
57
|
queryset=queryset,
|
|
63
58
|
applicable_filters=self.process_filters(applicable_filters, queryset, view),
|
|
64
|
-
applicable_exclusions=self.process_filters(applicable_exclusions, queryset, view)
|
|
59
|
+
applicable_exclusions=self.process_filters(applicable_exclusions, queryset, view),
|
|
65
60
|
)
|
|
66
61
|
|
|
67
62
|
def get_query_builder(self, *args, **kwargs):
|
|
@@ -81,8 +76,8 @@ class BaseHaystackFilterBackend(BaseFilterBackend):
|
|
|
81
76
|
methods of building the query sent to the search engine backend.
|
|
82
77
|
"""
|
|
83
78
|
assert self.query_builder_class is not None, (
|
|
84
|
-
"'
|
|
85
|
-
"or override the `get_query_builder_class()` method."
|
|
79
|
+
f"'{self.__class__.__name__}' should either include a `query_builder_class` attribute, "
|
|
80
|
+
"or override the `get_query_builder_class()` method."
|
|
86
81
|
)
|
|
87
82
|
return self.query_builder_class
|
|
88
83
|
|
|
@@ -113,11 +108,9 @@ class HaystackAutocompleteFilter(HaystackFilter):
|
|
|
113
108
|
for field_name, query in filters.children:
|
|
114
109
|
for word in query.split(" "):
|
|
115
110
|
bit = queryset.query.clean(word.strip())
|
|
116
|
-
kwargs = {
|
|
117
|
-
field_name: bit
|
|
118
|
-
}
|
|
111
|
+
kwargs = {field_name: bit}
|
|
119
112
|
query_bits.append(view.query_object(**kwargs))
|
|
120
|
-
return
|
|
113
|
+
return reduce(operator.and_, filter(lambda x: x, query_bits))
|
|
121
114
|
|
|
122
115
|
|
|
123
116
|
class HaystackGEOSpatialFilter(BaseHaystackFilterBackend):
|
|
@@ -154,7 +147,7 @@ class HaystackHighlightFilter(HaystackFilter):
|
|
|
154
147
|
"""
|
|
155
148
|
|
|
156
149
|
def filter_queryset(self, request, queryset, view):
|
|
157
|
-
queryset = super(
|
|
150
|
+
queryset = super().filter_queryset(request, queryset, view)
|
|
158
151
|
if self.get_request_filters(request) and isinstance(queryset, SearchQuerySet):
|
|
159
152
|
queryset = queryset.highlight()
|
|
160
153
|
return queryset
|
|
@@ -224,8 +217,10 @@ class HaystackOrderingFilter(OrderingFilter):
|
|
|
224
217
|
Some docstring here!
|
|
225
218
|
"""
|
|
226
219
|
|
|
227
|
-
def get_default_valid_fields(self, queryset, view, context=
|
|
228
|
-
|
|
220
|
+
def get_default_valid_fields(self, queryset, view, context=None):
|
|
221
|
+
if context is None:
|
|
222
|
+
context = {}
|
|
223
|
+
valid_fields = super().get_default_valid_fields(queryset, view, context)
|
|
229
224
|
|
|
230
225
|
# Check if we need to support aggregate serializers
|
|
231
226
|
serializer_class = view.get_serializer_class()
|
|
@@ -234,7 +229,9 @@ class HaystackOrderingFilter(OrderingFilter):
|
|
|
234
229
|
|
|
235
230
|
return valid_fields
|
|
236
231
|
|
|
237
|
-
def get_valid_fields(self, queryset, view, context=
|
|
232
|
+
def get_valid_fields(self, queryset, view, context=None):
|
|
233
|
+
if context is None:
|
|
234
|
+
context = {}
|
|
238
235
|
valid_fields = getattr(view, "ordering_fields", self.ordering_fields)
|
|
239
236
|
|
|
240
237
|
if valid_fields is None:
|
|
@@ -244,19 +241,17 @@ class HaystackOrderingFilter(OrderingFilter):
|
|
|
244
241
|
# View explicitly allows filtering on all model fields.
|
|
245
242
|
if not queryset.query.models:
|
|
246
243
|
raise ImproperlyConfigured(
|
|
247
|
-
"Cannot use
|
|
244
|
+
f"Cannot use {self.__class__.__name__} with '__all__' as 'ordering_fields' attribute on a view "
|
|
248
245
|
"which has no 'index_models' set. Either specify some 'ordering_fields', "
|
|
249
246
|
"set the 'index_models' attribute or override the 'get_queryset' "
|
|
250
247
|
"method and pass some 'index_models'."
|
|
251
|
-
|
|
248
|
+
)
|
|
252
249
|
|
|
253
|
-
model_fields =
|
|
254
|
-
|
|
250
|
+
model_fields = (
|
|
251
|
+
[(field.name, field.verbose_name) for field in model._meta.fields] for model in queryset.query.models
|
|
252
|
+
)
|
|
255
253
|
valid_fields = list(set(reduce(operator.concat, model_fields)))
|
|
256
254
|
else:
|
|
257
|
-
valid_fields = [
|
|
258
|
-
(item, item) if isinstance(item, six.string_types) else item
|
|
259
|
-
for item in valid_fields
|
|
260
|
-
]
|
|
255
|
+
valid_fields = [(item, item) if isinstance(item, str) else item for item in valid_fields]
|
|
261
256
|
|
|
262
257
|
return valid_fields
|
|
@@ -1,12 +1,5 @@
|
|
|
1
|
-
# -*- coding: utf-8 -*-
|
|
2
|
-
|
|
3
|
-
from __future__ import absolute_import, unicode_literals
|
|
4
|
-
|
|
5
|
-
import six
|
|
6
|
-
|
|
7
1
|
from django.contrib.contenttypes.models import ContentType
|
|
8
2
|
from django.http import Http404
|
|
9
|
-
|
|
10
3
|
from haystack.backends import SQ
|
|
11
4
|
from haystack.query import SearchQuerySet
|
|
12
5
|
from rest_framework.generics import GenericAPIView
|
|
@@ -18,6 +11,7 @@ class HaystackGenericAPIView(GenericAPIView):
|
|
|
18
11
|
"""
|
|
19
12
|
Base class for all haystack generic views.
|
|
20
13
|
"""
|
|
14
|
+
|
|
21
15
|
# Use `index_models` to filter on which search index models we
|
|
22
16
|
# should include in the search result.
|
|
23
17
|
index_models = []
|
|
@@ -38,7 +32,7 @@ class HaystackGenericAPIView(GenericAPIView):
|
|
|
38
32
|
|
|
39
33
|
filter_backends = [HaystackFilter]
|
|
40
34
|
|
|
41
|
-
def get_queryset(self, index_models=
|
|
35
|
+
def get_queryset(self, index_models=None):
|
|
42
36
|
"""
|
|
43
37
|
Get the list of items for this view.
|
|
44
38
|
Returns ``self.queryset`` if defined and is a ``self.object_class``
|
|
@@ -46,6 +40,8 @@ class HaystackGenericAPIView(GenericAPIView):
|
|
|
46
40
|
|
|
47
41
|
@:param index_models: override `self.index_models`
|
|
48
42
|
"""
|
|
43
|
+
if index_models is None:
|
|
44
|
+
index_models = []
|
|
49
45
|
if self.queryset is not None and isinstance(self.queryset, self.object_class):
|
|
50
46
|
queryset = self.queryset.all()
|
|
51
47
|
else:
|
|
@@ -72,19 +68,21 @@ class HaystackGenericAPIView(GenericAPIView):
|
|
|
72
68
|
queryset = self.get_queryset()
|
|
73
69
|
if "model" in self.request.query_params:
|
|
74
70
|
try:
|
|
75
|
-
app_label, model = map(
|
|
71
|
+
app_label, model = map(str.lower, self.request.query_params["model"].split(".", 1))
|
|
76
72
|
ctype = ContentType.objects.get(app_label=app_label, model=model)
|
|
77
73
|
queryset = self.get_queryset(index_models=[ctype.model_class()])
|
|
78
74
|
except (ValueError, ContentType.DoesNotExist):
|
|
79
|
-
raise Http404(
|
|
80
|
-
|
|
75
|
+
raise Http404(
|
|
76
|
+
"Could not find any models matching '{}'. Make sure to use a valid "
|
|
77
|
+
"'app_label.model' name for the 'model' query parameter.".format(self.request.query_params["model"])
|
|
78
|
+
)
|
|
81
79
|
|
|
82
80
|
lookup_url_kwarg = self.lookup_url_kwarg or self.lookup_field
|
|
83
81
|
if lookup_url_kwarg not in self.kwargs:
|
|
84
82
|
raise AttributeError(
|
|
85
|
-
"Expected view
|
|
86
|
-
"named '
|
|
87
|
-
"attribute on the view correctly."
|
|
83
|
+
f"Expected view {self.__class__.__name__} to be called with a URL keyword argument "
|
|
84
|
+
f"named '{lookup_url_kwarg}'. Fix your URL conf, or set the `.lookup_field` "
|
|
85
|
+
"attribute on the view correctly."
|
|
88
86
|
)
|
|
89
87
|
queryset = queryset.filter(self.query_object((self.document_uid_field, self.kwargs[lookup_url_kwarg])))
|
|
90
88
|
count = queryset.count()
|
|
@@ -96,8 +94,8 @@ class HaystackGenericAPIView(GenericAPIView):
|
|
|
96
94
|
raise Http404("No result matches the given query.")
|
|
97
95
|
|
|
98
96
|
def filter_queryset(self, queryset):
|
|
99
|
-
queryset = super(
|
|
100
|
-
|
|
97
|
+
queryset = super().filter_queryset(queryset)
|
|
98
|
+
|
|
101
99
|
if self.load_all:
|
|
102
100
|
queryset = queryset.load_all()
|
|
103
101
|
|