├── .github ├── dependabot.yml └── workflows │ ├── codeql-analysis.yml │ └── codespell_and_ruff.yml ├── .gitignore ├── .pre-commit-config.yaml ├── AUTHORS ├── LICENSE ├── MANIFEST.in ├── README.rst ├── celery_haystack ├── __init__.py ├── conf.py ├── indexes.py ├── models.py ├── signals.py ├── tasks.py ├── test_settings.py ├── tests │ ├── __init__.py │ ├── models.py │ ├── search_indexes.py │ ├── search_sites.py │ └── tests.py └── utils.py ├── docs ├── Makefile ├── changelog.rst ├── conf.py ├── index.rst └── make.bat ├── requirements └── v2.txt ├── setup.cfg └── setup.py /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | # Keep GitHub Actions up to date with GitHub's Dependabot... 2 | # https://docs.github.com/en/code-security/dependabot/working-with-dependabot/keeping-your-actions-up-to-date-with-dependabot 3 | # https://docs.github.com/en/code-security/dependabot/dependabot-version-updates/configuration-options-for-the-dependabot.yml-file#package-ecosystem 4 | version: 2 5 | updates: 6 | - package-ecosystem: github-actions 7 | directory: / 8 | groups: 9 | github-actions: 10 | patterns: 11 | - "*" # Group all Actions updates into a single larger pull request 12 | schedule: 13 | interval: weekly 14 | -------------------------------------------------------------------------------- /.github/workflows/codeql-analysis.yml: -------------------------------------------------------------------------------- 1 | name: "CodeQL" 2 | 3 | on: 4 | push: 5 | branches: [develop, ] 6 | pull_request: 7 | # The branches below must be a subset of the branches above 8 | branches: [develop] 9 | schedule: 10 | - cron: '0 9 * * 3' 11 | 12 | jobs: 13 | analyze: 14 | name: Analyze 15 | runs-on: ubuntu-latest 16 | 17 | steps: 18 | - name: Checkout repository 19 | uses: actions/checkout@v4 20 | with: 21 | # We must fetch at least the immediate parents so that if this is 22 | # a pull request then we can checkout the head. 23 | fetch-depth: 2 24 | 25 | # If this run was triggered by a pull request event, then checkout 26 | # the head of the pull request instead of the merge commit. 27 | - run: git checkout HEAD^2 28 | if: ${{ github.event_name == 'pull_request' }} 29 | 30 | # Initializes the CodeQL tools for scanning. 31 | - name: Initialize CodeQL 32 | uses: github/codeql-action/init@v3 33 | 34 | - name: Perform CodeQL Analysis 35 | uses: github/codeql-action/analyze@v3 36 | -------------------------------------------------------------------------------- /.github/workflows/codespell_and_ruff.yml: -------------------------------------------------------------------------------- 1 | # This Action uses minimal steps to run in ~5 seconds to rapidly: 2 | # find typos in the codebase using codespell, and 3 | # lint Python code using ruff and provide intuitive GitHub Annotations to contributors. 4 | # https://github.com/codespell-project/codespell#readme 5 | # https://beta.ruff.rs 6 | name: codespell_and_ruff 7 | on: 8 | push: 9 | branches: [develop] 10 | pull_request: 11 | branches: [develop] 12 | jobs: 13 | codespell_and_ruff: 14 | runs-on: ubuntu-latest 15 | steps: 16 | - uses: actions/checkout@v4 17 | - run: pipx install codespell[toml] ruff 18 | - run: codespell # --ignore-words-list="" --skip="*.css,*.js,*.lock,*.po" 19 | - run: ruff check --output-format=github --target-version=py39 20 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | *.pyc 3 | celery_haystack/tests/whoosh_index 4 | *.egg 5 | *.egg-info 6 | .coverage 7 | docs/_build 8 | build/ 9 | dist/ 10 | .eggs/ 11 | MANIFEST 12 | htmlcov/ 13 | -------------------------------------------------------------------------------- /.pre-commit-config.yaml: -------------------------------------------------------------------------------- 1 | # To enable this pre-commit hook run: 2 | # `pip install pre-commit` or `brew install pre-commit` 3 | # Then run `pre-commit install` 4 | 5 | # Learn more about this config here: https://pre-commit.com/ 6 | default_language_version: 7 | python: python3.11 8 | 9 | ci: 10 | autoupdate_schedule: monthly 11 | submodules: true 12 | 13 | repos: 14 | - repo: https://github.com/astral-sh/ruff-pre-commit 15 | rev: v0.11.12 16 | hooks: 17 | - id: ruff # See pyproject.toml for args 18 | 19 | - repo: https://github.com/codespell-project/codespell 20 | rev: v2.4.1 21 | hooks: 22 | - id: codespell # See pyproject.toml for args 23 | additional_dependencies: 24 | - tomli 25 | -------------------------------------------------------------------------------- /AUTHORS: -------------------------------------------------------------------------------- 1 | Josh Bohde 2 | Germán M. Bravo 3 | Jannis Leidel 4 | Daniel Lindsley 5 | Stefan Wehrmeyer -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2011-2013, Jannis Leidel and contributors. 2 | All rights reserved. 3 | 4 | Redistribution and use in source and binary forms, with or without modification, 5 | are permitted provided that the following conditions are met: 6 | 7 | 1. Redistributions of source code must retain the above copyright notice, 8 | this list of conditions and the following disclaimer. 9 | 10 | 2. Redistributions in binary form must reproduce the above copyright 11 | notice, this list of conditions and the following disclaimer in the 12 | documentation and/or other materials provided with the distribution. 13 | 14 | 3. Neither the name of celery-haystack nor the names of its contributors may 15 | be used to endorse or promote products derived from this software without 16 | specific prior written permission. 17 | 18 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 19 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 20 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 21 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR 22 | ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 23 | (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 24 | LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON 25 | ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 26 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 27 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 28 | -------------------------------------------------------------------------------- /MANIFEST.in: -------------------------------------------------------------------------------- 1 | include AUTHORS 2 | include LICENSE 3 | include README.rst 4 | -------------------------------------------------------------------------------- /README.rst: -------------------------------------------------------------------------------- 1 | =============== 2 | celery-haystack 3 | =============== 4 | 5 | .. image:: https://secure.travis-ci.org/django-haystack/celery-haystack.png?branch=develop 6 | :alt: Build Status 7 | :target: http://travis-ci.org/django-haystack/celery-haystack 8 | 9 | This Django app allows you to utilize Celery for automatically updating and 10 | deleting objects in a Haystack_ search index. 11 | 12 | Requirements 13 | ------------ 14 | 15 | * Django 1.8+ 16 | * Haystack_ `2.X`_ 17 | * Celery_ 3.X 18 | 19 | You also need to install your choice of one of the supported search engines 20 | for Haystack and one of the supported backends for Celery. 21 | 22 | 23 | .. _Haystack: http://haystacksearch.org 24 | .. _Celery: http://www.celeryproject.org 25 | 26 | 27 | Installation 28 | ------------ 29 | 30 | Use your favorite Python package manager to install the app from PyPI, e.g.:: 31 | 32 | pip install celery-haystack 33 | 34 | 35 | For Django < 1.9 you need to install and configure `django-transaction-hooks`_ -- an app that 36 | brings transaction commit hooks to Django. 37 | 38 | .. _django-transaction-hooks: https://github.com/carljm/django-transaction-hooks 39 | 40 | 41 | Usage 42 | ----- 43 | 44 | 1. Add ``'celery_haystack'`` to the ``INSTALLED_APPS`` setting 45 | 46 | .. code:: python 47 | 48 | INSTALLED_APPS = [ 49 | # .. 50 | 'celery_haystack', 51 | ] 52 | 53 | 2. Enable the celery-haystack signal processor in the settings 54 | 55 | .. code:: python 56 | 57 | HAYSTACK_SIGNAL_PROCESSOR = 'celery_haystack.signals.CelerySignalProcessor' 58 | 59 | 3. Alter all of your ``SearchIndex`` subclasses to inherit from 60 | ``celery_haystack.indexes.CelerySearchIndex`` and 61 | ``haystack.indexes.Indexable`` 62 | 63 | .. code:: python 64 | 65 | from haystack import indexes 66 | from celery_haystack.indexes import CelerySearchIndex 67 | from myapp.models import Note 68 | 69 | class NoteIndex(CelerySearchIndex, indexes.Indexable): 70 | text = indexes.CharField(document=True, model_attr='content') 71 | 72 | def get_model(self): 73 | return Note 74 | 75 | 4. Ensure your Celery instance is running. 76 | 77 | Thanks 78 | ------ 79 | 80 | This app is a blatant rip-off of Daniel Lindsley's queued_search_ 81 | app but uses Ask Solem Hoel's Celery_ instead of the equally awesome 82 | queues_ library by Matt Croyden. 83 | 84 | .. _queued_search: https://github.com/toastdriven/queued_search/ 85 | .. _Celery: http://celeryproject.org/ 86 | .. _queues: http://code.google.com/p/queues/ 87 | 88 | Issues 89 | ------ 90 | 91 | Please use the `Github issue tracker`_ for any bug reports or feature 92 | requests. 93 | 94 | .. _`Github issue tracker`: https://github.com/django-haystack/celery-haystack/issues 95 | -------------------------------------------------------------------------------- /celery_haystack/__init__.py: -------------------------------------------------------------------------------- 1 | __version__ = '0.10' 2 | 3 | 4 | def version_hook(config): 5 | config['metadata']['version'] = __version__ 6 | -------------------------------------------------------------------------------- /celery_haystack/conf.py: -------------------------------------------------------------------------------- 1 | from django.conf import settings # noqa 2 | from django.core.exceptions import ImproperlyConfigured 3 | from haystack import constants 4 | from haystack.management.commands import update_index as cmd 5 | from appconf import AppConf 6 | 7 | 8 | class CeleryHaystack(AppConf): 9 | #: The default alias to 10 | DEFAULT_ALIAS = None 11 | #: The delay (in seconds) before task will be executed (Celery countdown) 12 | COUNTDOWN = 0 13 | #: The delay (in seconds) after which a failed index is retried 14 | RETRY_DELAY = 5 * 60 15 | #: The number of retries that are done 16 | MAX_RETRIES = 1 17 | #: The default Celery task class 18 | DEFAULT_TASK = 'celery_haystack.tasks.CeleryHaystackSignalHandler' 19 | #: The name of the celery queue to use, or None for default 20 | QUEUE = None 21 | #: Whether the task should be handled transaction safe 22 | TRANSACTION_SAFE = True 23 | 24 | #: The batch size used by the CeleryHaystackUpdateIndex task 25 | COMMAND_BATCH_SIZE = None 26 | #: The max age of items used by the CeleryHaystackUpdateIndex task 27 | COMMAND_AGE = None 28 | #: Whether to remove items from the index that aren't in the DB anymore 29 | COMMAND_REMOVE = False 30 | #: The number of multiprocessing workers used by the CeleryHaystackUpdateIndex task 31 | COMMAND_WORKERS = 0 32 | #: The names of apps to run update_index for 33 | COMMAND_APPS = [] 34 | #: The verbosity level of the update_index call 35 | COMMAND_VERBOSITY = 1 36 | 37 | def configure_default_alias(self, value): 38 | return value or getattr(constants, 'DEFAULT_ALIAS', None) 39 | 40 | def configure_command_batch_size(self, value): 41 | return value or getattr(cmd, 'DEFAULT_BATCH_SIZE', None) 42 | 43 | def configure_command_age(self, value): 44 | return value or getattr(cmd, 'DEFAULT_AGE', None) 45 | 46 | def configure(self): 47 | data = {} 48 | for name, value in self.configured_data.items(): 49 | if name in ('RETRY_DELAY', 'MAX_RETRIES', 50 | 'COMMAND_WORKERS', 'COMMAND_VERBOSITY'): 51 | value = int(value) 52 | data[name] = value 53 | return data 54 | 55 | 56 | signal_processor = getattr(settings, 'HAYSTACK_SIGNAL_PROCESSOR', None) 57 | 58 | 59 | if signal_processor is None: 60 | raise ImproperlyConfigured("When using celery-haystack with Haystack 2.X " 61 | "the HAYSTACK_SIGNAL_PROCESSOR setting must be " 62 | "set. Use 'celery_haystack.signals." 63 | "CelerySignalProcessor' as default.") 64 | -------------------------------------------------------------------------------- /celery_haystack/indexes.py: -------------------------------------------------------------------------------- 1 | from haystack import indexes 2 | 3 | 4 | class CelerySearchIndex(indexes.SearchIndex): 5 | pass 6 | -------------------------------------------------------------------------------- /celery_haystack/models.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/django-haystack/celery-haystack/2121307268ea4e9f9e5e9c7593d35155190650a2/celery_haystack/models.py -------------------------------------------------------------------------------- /celery_haystack/signals.py: -------------------------------------------------------------------------------- 1 | from django.db.models import signals 2 | 3 | from haystack.signals import BaseSignalProcessor 4 | from haystack.exceptions import NotHandled 5 | 6 | from .utils import enqueue_task 7 | from .indexes import CelerySearchIndex 8 | 9 | 10 | class CelerySignalProcessor(BaseSignalProcessor): 11 | 12 | def setup(self): 13 | signals.post_save.connect(self.enqueue_save) 14 | signals.post_delete.connect(self.enqueue_delete) 15 | 16 | def teardown(self): 17 | signals.post_save.disconnect(self.enqueue_save) 18 | signals.post_delete.disconnect(self.enqueue_delete) 19 | 20 | def enqueue_save(self, sender, instance, **kwargs): 21 | return self.enqueue('update', instance, sender, **kwargs) 22 | 23 | def enqueue_delete(self, sender, instance, **kwargs): 24 | return self.enqueue('delete', instance, sender, **kwargs) 25 | 26 | def enqueue(self, action, instance, sender, **kwargs): 27 | """ 28 | Given an individual model instance, determine if a backend 29 | handles the model, check if the index is Celery-enabled and 30 | enqueue task. 31 | """ 32 | using_backends = self.connection_router.for_write(instance=instance) 33 | 34 | for using in using_backends: 35 | try: 36 | connection = self.connections[using] 37 | index = connection.get_unified_index().get_index(sender) 38 | except NotHandled: 39 | continue # Check next backend 40 | 41 | if isinstance(index, CelerySearchIndex): 42 | if action == 'update' and not index.should_update(instance): 43 | continue 44 | enqueue_task(action, instance) 45 | -------------------------------------------------------------------------------- /celery_haystack/tasks.py: -------------------------------------------------------------------------------- 1 | from django.core.exceptions import ImproperlyConfigured 2 | from django.core.management import call_command 3 | from django.apps import apps 4 | 5 | from .conf import settings 6 | 7 | from haystack import connections, connection_router 8 | from haystack.exceptions import NotHandled as IndexNotFoundException 9 | 10 | from celery.task import Task # noqa 11 | from celery.utils.log import get_task_logger 12 | 13 | logger = get_task_logger(__name__) 14 | 15 | 16 | class CeleryHaystackSignalHandler(Task): 17 | using = settings.CELERY_HAYSTACK_DEFAULT_ALIAS 18 | max_retries = settings.CELERY_HAYSTACK_MAX_RETRIES 19 | default_retry_delay = settings.CELERY_HAYSTACK_RETRY_DELAY 20 | 21 | def split_identifier(self, identifier, **kwargs): 22 | """ 23 | Break down the identifier representing the instance. 24 | 25 | Converts 'notes.note.23' into ('notes.note', 23). 26 | """ 27 | bits = identifier.split('.') 28 | 29 | if len(bits) < 2: 30 | logger.error("Unable to parse object " 31 | "identifier '%s'. Moving on..." % identifier) 32 | return (None, None) 33 | 34 | pk = bits[-1] 35 | # In case Django ever handles full paths... 36 | object_path = '.'.join(bits[:-1]) 37 | return (object_path, pk) 38 | 39 | def get_model_class(self, object_path, **kwargs): 40 | """ 41 | Fetch the model's class in a standardized way. 42 | """ 43 | bits = object_path.split('.') 44 | app_name = '.'.join(bits[:-1]) 45 | classname = bits[-1] 46 | model_class = apps.get_model(app_name, classname) 47 | 48 | if model_class is None: 49 | raise ImproperlyConfigured("Could not load model '%s'." % 50 | object_path) 51 | return model_class 52 | 53 | def get_instance(self, model_class, pk, **kwargs): 54 | """ 55 | Fetch the instance in a standardized way. 56 | """ 57 | instance = None 58 | try: 59 | instance = model_class._default_manager.get(pk=pk) 60 | except model_class.DoesNotExist: 61 | logger.error("Couldn't load %s.%s.%s. Somehow it went missing?" % 62 | (model_class._meta.app_label.lower(), 63 | model_class._meta.object_name.lower(), pk)) 64 | except model_class.MultipleObjectsReturned: 65 | logger.error("More than one object with pk %s. Oops?" % pk) 66 | return instance 67 | 68 | def get_indexes(self, model_class, **kwargs): 69 | """ 70 | Fetch the model's registered ``SearchIndex`` in a standardized way. 71 | """ 72 | try: 73 | using_backends = connection_router.for_write(**{'models': [model_class]}) 74 | for using in using_backends: 75 | index_holder = connections[using].get_unified_index() 76 | yield index_holder.get_index(model_class), using 77 | except IndexNotFoundException: 78 | raise ImproperlyConfigured("Couldn't find a SearchIndex for %s." % 79 | model_class) 80 | 81 | def run(self, action, identifier, **kwargs): 82 | """ 83 | Trigger the actual index handler depending on the 84 | given action ('update' or 'delete'). 85 | """ 86 | # First get the object path and pk (e.g. ('notes.note', 23)) 87 | object_path, pk = self.split_identifier(identifier, **kwargs) 88 | if object_path is None or pk is None: 89 | msg = "Couldn't handle object with identifier %s" % identifier 90 | logger.error(msg) 91 | raise ValueError(msg) 92 | 93 | # Then get the model class for the object path 94 | model_class = self.get_model_class(object_path, **kwargs) 95 | for current_index, using in self.get_indexes(model_class, **kwargs): 96 | current_index_name = ".".join([current_index.__class__.__module__, 97 | current_index.__class__.__name__]) 98 | 99 | if action == 'delete': 100 | # If the object is gone, we'll use just the identifier 101 | # against the index. 102 | try: 103 | current_index.remove_object(identifier, using=using) 104 | except Exception as exc: 105 | logger.exception(exc) 106 | self.retry(exc=exc) 107 | else: 108 | msg = ("Deleted '%s' (with %s)" % 109 | (identifier, current_index_name)) 110 | logger.debug(msg) 111 | elif action == 'update': 112 | # and the instance of the model class with the pk 113 | instance = self.get_instance(model_class, pk, **kwargs) 114 | if instance is None: 115 | logger.debug("Failed updating '%s' (with %s)" % 116 | (identifier, current_index_name)) 117 | raise ValueError("Couldn't load object '%s'" % identifier) 118 | 119 | # Call the appropriate handler of the current index and 120 | # handle exception if necessary 121 | try: 122 | current_index.update_object(instance, using=using) 123 | except Exception as exc: 124 | logger.exception(exc) 125 | self.retry(exc=exc) 126 | else: 127 | msg = ("Updated '%s' (with %s)" % 128 | (identifier, current_index_name)) 129 | logger.debug(msg) 130 | else: 131 | logger.error("Unrecognized action '%s'. Moving on..." % action) 132 | raise ValueError("Unrecognized action %s" % action) 133 | 134 | 135 | class CeleryHaystackUpdateIndex(Task): 136 | """ 137 | A celery task class to be used to call the update_index management 138 | command from Celery. 139 | """ 140 | def run(self, apps=None, **kwargs): 141 | defaults = { 142 | 'batchsize': settings.CELERY_HAYSTACK_COMMAND_BATCH_SIZE, 143 | 'age': settings.CELERY_HAYSTACK_COMMAND_AGE, 144 | 'remove': settings.CELERY_HAYSTACK_COMMAND_REMOVE, 145 | 'using': [settings.CELERY_HAYSTACK_DEFAULT_ALIAS], 146 | 'workers': settings.CELERY_HAYSTACK_COMMAND_WORKERS, 147 | 'verbosity': settings.CELERY_HAYSTACK_COMMAND_VERBOSITY, 148 | } 149 | defaults.update(kwargs) 150 | if apps is None: 151 | apps = settings.CELERY_HAYSTACK_COMMAND_APPS 152 | # Run the update_index management command 153 | logger.info("Starting update index") 154 | call_command('update_index', *apps, **defaults) 155 | logger.info("Finishing update index") 156 | -------------------------------------------------------------------------------- /celery_haystack/test_settings.py: -------------------------------------------------------------------------------- 1 | import os 2 | 3 | from celery import Celery 4 | 5 | app = Celery('celery_haystack') 6 | app.config_from_object('django.conf:settings') 7 | 8 | 9 | DEBUG = True 10 | 11 | TEST_ROOT = os.path.abspath(os.path.join(os.path.dirname(__file__), 'tests')) 12 | 13 | INSTALLED_APPS = [ 14 | 'haystack', 15 | 'celery_haystack', 16 | 'celery_haystack.tests', 17 | ] 18 | 19 | DATABASES = { 20 | 'default': { 21 | 'ENGINE': 'django.db.backends.sqlite3', 22 | 'NAME': ':memory:', 23 | } 24 | } 25 | 26 | SECRET_KEY = 'really-not-secret' 27 | 28 | BROKER_TRANSPORT = "memory" 29 | CELERY_ALWAYS_EAGER = True 30 | CELERY_IGNORE_RESULT = True 31 | CELERYD_LOG_LEVEL = "DEBUG" 32 | CELERY_DEFAULT_QUEUE = "celery-haystack" 33 | 34 | HAYSTACK_CONNECTIONS = { 35 | 'default': { 36 | 'ENGINE': 'haystack.backends.whoosh_backend.WhooshEngine', 37 | 'PATH': os.path.join(TEST_ROOT, 'whoosh_index'), 38 | } 39 | } 40 | HAYSTACK_SIGNAL_PROCESSOR = 'celery_haystack.signals.CelerySignalProcessor' 41 | -------------------------------------------------------------------------------- /celery_haystack/tests/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/django-haystack/celery-haystack/2121307268ea4e9f9e5e9c7593d35155190650a2/celery_haystack/tests/__init__.py -------------------------------------------------------------------------------- /celery_haystack/tests/models.py: -------------------------------------------------------------------------------- 1 | from django.db import models 2 | 3 | 4 | class Note(models.Model): 5 | content = models.TextField() 6 | 7 | def __unicode__(self): 8 | return self.content 9 | -------------------------------------------------------------------------------- /celery_haystack/tests/search_indexes.py: -------------------------------------------------------------------------------- 1 | from haystack import indexes 2 | 3 | from .models import Note 4 | from ..indexes import CelerySearchIndex 5 | 6 | 7 | # Simplest possible subclass that could work. 8 | class NoteIndex(CelerySearchIndex, indexes.Indexable): 9 | text = indexes.CharField(document=True, model_attr='content') 10 | 11 | def get_model(self): 12 | return Note 13 | -------------------------------------------------------------------------------- /celery_haystack/tests/search_sites.py: -------------------------------------------------------------------------------- 1 | import haystack 2 | 3 | haystack.autodiscover() 4 | -------------------------------------------------------------------------------- /celery_haystack/tests/tests.py: -------------------------------------------------------------------------------- 1 | from django.core.management import call_command 2 | from django.test import TransactionTestCase 3 | 4 | from haystack.query import SearchQuerySet 5 | 6 | from .models import Note 7 | 8 | 9 | class QueuedSearchIndexTestCase(TransactionTestCase): 10 | 11 | def assertSearchResultLength(self, count): 12 | self.assertEqual(count, len(SearchQuerySet())) 13 | 14 | def assertSearchResultContains(self, pk, text): 15 | results = SearchQuerySet().filter(id='tests.note.%s' % pk) 16 | self.assertTrue(results) 17 | self.assertTrue(text in results[0].text) 18 | 19 | def setUp(self): 20 | # Nuke the index. 21 | call_command('clear_index', interactive=False, verbosity=0) 22 | 23 | # Throw away all Notes 24 | Note.objects.all().delete() 25 | 26 | def test_update(self): 27 | self.assertSearchResultLength(0) 28 | note1 = Note.objects.create(content='Because everyone loves tests.') 29 | self.assertSearchResultLength(1) 30 | self.assertSearchResultContains(note1.pk, 'loves') 31 | 32 | note2 = Note.objects.create(content='More test data.') 33 | self.assertSearchResultLength(2) 34 | self.assertSearchResultContains(note2.pk, 'More') 35 | 36 | note3 = Note.objects.create(content='The test data. All done.') 37 | self.assertSearchResultLength(3) 38 | self.assertSearchResultContains(note3.pk, 'All done') 39 | 40 | note3.content = 'Final test note FOR REAL' 41 | note3.save() 42 | self.assertSearchResultLength(3) 43 | self.assertSearchResultContains(note3.pk, 'FOR REAL') 44 | 45 | def test_delete(self): 46 | note1 = Note.objects.create(content='Because everyone loves tests.') 47 | note2 = Note.objects.create(content='More test data.') 48 | note3 = Note.objects.create(content='The test data. All done.') 49 | self.assertSearchResultLength(3) 50 | note1.delete() 51 | self.assertSearchResultLength(2) 52 | note2.delete() 53 | self.assertSearchResultLength(1) 54 | note3.delete() 55 | self.assertSearchResultLength(0) 56 | 57 | def test_complex(self): 58 | note1 = Note.objects.create(content='Because everyone loves test.') 59 | self.assertSearchResultLength(1) 60 | 61 | Note.objects.create(content='More test data.') 62 | self.assertSearchResultLength(2) 63 | note1.delete() 64 | self.assertSearchResultLength(1) 65 | 66 | note3 = Note.objects.create(content='The test data. All done.') 67 | self.assertSearchResultLength(2) 68 | 69 | note3.title = 'Final test note FOR REAL' 70 | note3.save() 71 | self.assertSearchResultLength(2) 72 | 73 | note3.delete() 74 | self.assertSearchResultLength(1) 75 | -------------------------------------------------------------------------------- /celery_haystack/utils.py: -------------------------------------------------------------------------------- 1 | from django.core.exceptions import ImproperlyConfigured 2 | try: 3 | from importlib import import_module 4 | except ImportError: 5 | from django.utils.importlib import import_module 6 | from django.db import connection, transaction 7 | 8 | from haystack.utils import get_identifier 9 | 10 | from .conf import settings 11 | 12 | 13 | def get_update_task(task_path=None): 14 | import_path = task_path or settings.CELERY_HAYSTACK_DEFAULT_TASK 15 | module, attr = import_path.rsplit('.', 1) 16 | try: 17 | mod = import_module(module) 18 | except ImportError as e: 19 | raise ImproperlyConfigured('Error importing module %s: "%s"' % 20 | (module, e)) 21 | try: 22 | Task = getattr(mod, attr) 23 | except AttributeError: 24 | raise ImproperlyConfigured('Module "%s" does not define a "%s" ' 25 | 'class.' % (module, attr)) 26 | return Task() 27 | 28 | 29 | def enqueue_task(action, instance, **kwargs): 30 | """ 31 | Common utility for enqueuing a task for the given action and 32 | model instance. 33 | """ 34 | identifier = get_identifier(instance) 35 | options = {} 36 | if settings.CELERY_HAYSTACK_QUEUE: 37 | options['queue'] = settings.CELERY_HAYSTACK_QUEUE 38 | if settings.CELERY_HAYSTACK_COUNTDOWN: 39 | options['countdown'] = settings.CELERY_HAYSTACK_COUNTDOWN 40 | 41 | task = get_update_task() 42 | task_func = lambda: task.apply_async( # noqa: E731 43 | (action, identifier), kwargs, **options 44 | ) 45 | 46 | if hasattr(transaction, 'on_commit'): 47 | # Django 1.9 on_commit hook 48 | transaction.on_commit( 49 | task_func 50 | ) 51 | elif hasattr(connection, 'on_commit'): 52 | # Django-transaction-hooks 53 | connection.on_commit( 54 | task_func 55 | ) 56 | else: 57 | task_func() 58 | -------------------------------------------------------------------------------- /docs/Makefile: -------------------------------------------------------------------------------- 1 | # Makefile for Sphinx documentation 2 | # 3 | 4 | # You can set these variables from the command line. 5 | SPHINXOPTS = 6 | SPHINXBUILD = sphinx-build 7 | PAPER = 8 | BUILDDIR = _build 9 | 10 | # Internal variables. 11 | PAPEROPT_a4 = -D latex_paper_size=a4 12 | PAPEROPT_letter = -D latex_paper_size=letter 13 | ALLSPHINXOPTS = -d $(BUILDDIR)/doctrees $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) . 14 | 15 | .PHONY: help clean html dirhtml singlehtml pickle json htmlhelp qthelp devhelp epub latex latexpdf text man changes linkcheck doctest 16 | 17 | help: 18 | @echo "Please use \`make ' where is one of" 19 | @echo " html to make standalone HTML files" 20 | @echo " dirhtml to make HTML files named index.html in directories" 21 | @echo " singlehtml to make a single large HTML file" 22 | @echo " pickle to make pickle files" 23 | @echo " json to make JSON files" 24 | @echo " htmlhelp to make HTML files and a HTML help project" 25 | @echo " qthelp to make HTML files and a qthelp project" 26 | @echo " devhelp to make HTML files and a Devhelp project" 27 | @echo " epub to make an epub" 28 | @echo " latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter" 29 | @echo " latexpdf to make LaTeX files and run them through pdflatex" 30 | @echo " text to make text files" 31 | @echo " man to make manual pages" 32 | @echo " changes to make an overview of all changed/added/deprecated items" 33 | @echo " linkcheck to check all external links for integrity" 34 | @echo " doctest to run all doctests embedded in the documentation (if enabled)" 35 | 36 | clean: 37 | -rm -rf $(BUILDDIR)/* 38 | 39 | html: 40 | $(SPHINXBUILD) -b html $(ALLSPHINXOPTS) $(BUILDDIR)/html 41 | @echo 42 | @echo "Build finished. The HTML pages are in $(BUILDDIR)/html." 43 | 44 | dirhtml: 45 | $(SPHINXBUILD) -b dirhtml $(ALLSPHINXOPTS) $(BUILDDIR)/dirhtml 46 | @echo 47 | @echo "Build finished. The HTML pages are in $(BUILDDIR)/dirhtml." 48 | 49 | singlehtml: 50 | $(SPHINXBUILD) -b singlehtml $(ALLSPHINXOPTS) $(BUILDDIR)/singlehtml 51 | @echo 52 | @echo "Build finished. The HTML page is in $(BUILDDIR)/singlehtml." 53 | 54 | pickle: 55 | $(SPHINXBUILD) -b pickle $(ALLSPHINXOPTS) $(BUILDDIR)/pickle 56 | @echo 57 | @echo "Build finished; now you can process the pickle files." 58 | 59 | json: 60 | $(SPHINXBUILD) -b json $(ALLSPHINXOPTS) $(BUILDDIR)/json 61 | @echo 62 | @echo "Build finished; now you can process the JSON files." 63 | 64 | htmlhelp: 65 | $(SPHINXBUILD) -b htmlhelp $(ALLSPHINXOPTS) $(BUILDDIR)/htmlhelp 66 | @echo 67 | @echo "Build finished; now you can run HTML Help Workshop with the" \ 68 | ".hhp project file in $(BUILDDIR)/htmlhelp." 69 | 70 | qthelp: 71 | $(SPHINXBUILD) -b qthelp $(ALLSPHINXOPTS) $(BUILDDIR)/qthelp 72 | @echo 73 | @echo "Build finished; now you can run "qcollectiongenerator" with the" \ 74 | ".qhcp project file in $(BUILDDIR)/qthelp, like this:" 75 | @echo "# qcollectiongenerator $(BUILDDIR)/qthelp/celery-haystack.qhcp" 76 | @echo "To view the help file:" 77 | @echo "# assistant -collectionFile $(BUILDDIR)/qthelp/celery-haystack.qhc" 78 | 79 | devhelp: 80 | $(SPHINXBUILD) -b devhelp $(ALLSPHINXOPTS) $(BUILDDIR)/devhelp 81 | @echo 82 | @echo "Build finished." 83 | @echo "To view the help file:" 84 | @echo "# mkdir -p $$HOME/.local/share/devhelp/celery-haystack" 85 | @echo "# ln -s $(BUILDDIR)/devhelp $$HOME/.local/share/devhelp/celery-haystack" 86 | @echo "# devhelp" 87 | 88 | epub: 89 | $(SPHINXBUILD) -b epub $(ALLSPHINXOPTS) $(BUILDDIR)/epub 90 | @echo 91 | @echo "Build finished. The epub file is in $(BUILDDIR)/epub." 92 | 93 | latex: 94 | $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex 95 | @echo 96 | @echo "Build finished; the LaTeX files are in $(BUILDDIR)/latex." 97 | @echo "Run \`make' in that directory to run these through (pdf)latex" \ 98 | "(use \`make latexpdf' here to do that automatically)." 99 | 100 | latexpdf: 101 | $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex 102 | @echo "Running LaTeX files through pdflatex..." 103 | make -C $(BUILDDIR)/latex all-pdf 104 | @echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex." 105 | 106 | text: 107 | $(SPHINXBUILD) -b text $(ALLSPHINXOPTS) $(BUILDDIR)/text 108 | @echo 109 | @echo "Build finished. The text files are in $(BUILDDIR)/text." 110 | 111 | man: 112 | $(SPHINXBUILD) -b man $(ALLSPHINXOPTS) $(BUILDDIR)/man 113 | @echo 114 | @echo "Build finished. The manual pages are in $(BUILDDIR)/man." 115 | 116 | changes: 117 | $(SPHINXBUILD) -b changes $(ALLSPHINXOPTS) $(BUILDDIR)/changes 118 | @echo 119 | @echo "The overview file is in $(BUILDDIR)/changes." 120 | 121 | linkcheck: 122 | $(SPHINXBUILD) -b linkcheck $(ALLSPHINXOPTS) $(BUILDDIR)/linkcheck 123 | @echo 124 | @echo "Link check complete; look for any errors in the above output " \ 125 | "or in $(BUILDDIR)/linkcheck/output.txt." 126 | 127 | doctest: 128 | $(SPHINXBUILD) -b doctest $(ALLSPHINXOPTS) $(BUILDDIR)/doctest 129 | @echo "Testing of doctests in the sources finished, look at the " \ 130 | "results in $(BUILDDIR)/doctest/output.txt." 131 | -------------------------------------------------------------------------------- /docs/changelog.rst: -------------------------------------------------------------------------------- 1 | Changelog 2 | ========= 3 | 4 | v0.9 (2015-06-13) 5 | ----------------- 6 | 7 | * Moved to Haystack GitHub org: https://github.com/django-haystack/celery-haystack 8 | 9 | * Fix handling the default Haystack backend alias, making it a list. 10 | 11 | * Added ``CELERY_HAYSTACK_QUEUE`` setting to define which Celery queue to use. 12 | 13 | * Added ``CELERY_HAYSTACK_COUNTDOWN`` setting to define when to start the 14 | indexing task after initially creating it. 15 | 16 | * Stop returning after enqueuing in the Haystack router to support 17 | multiple routers. 18 | 19 | * Optionally support using django-transaction-hooks for improved transaction 20 | handling. 21 | 22 | * Instantiate update task class correctly. 23 | 24 | * Use Celery's task logger utility function. 25 | 26 | v0.8 (2014-07-31) 27 | ----------------- 28 | 29 | * Fix bug when using multiple Haystack indices 30 | 31 | * Fixed merge bug where primary key of object was cast to int 32 | 33 | * Add compatibility for Python 3.3, 3.4, Celery 3.X 34 | 35 | v0.7.2 (2013-03-23) 36 | ------------------- 37 | 38 | * Fixed import time issue with Haystack 2.X. 39 | 40 | * Minor fixes to the README. 41 | 42 | * Made signal processor compatible for subclassing for easier extensibility. 43 | 44 | v0.7.1 (2013-03-09) 45 | ------------------- 46 | 47 | * Fixed installation issues with d2to1. 48 | 49 | v0.7 (2013-03-09) 50 | ----------------- 51 | 52 | * **Backwards incompatible** change to support the new signal processor API 53 | in Haystack 2.X. To upgrade simply add this to your settings:: 54 | 55 | HAYSTACK_SIGNAL_PROCESSOR = 'celery_haystack.signals.CelerySignalProcessor' 56 | 57 | Many thanks to Stefan Wehrmeyer for the help. 58 | 59 | * Simplified index class implementation. 60 | 61 | * Support multiple indexes in the task. Thanks, Stefan Wehrmeyer. 62 | 63 | * Use the exception handler of the task logger instead of the error handler 64 | when catching an exception. 65 | 66 | * Switched to d2to1_ for handling package metadata. 67 | 68 | .. _d2to1: http://pypi.python.org/pypi/d2to1 69 | 70 | v0.6.2 (2012-06-28) 71 | ------------------- 72 | 73 | * Fixed AttributeError in settings handling. 74 | 75 | v0.6.1 (2012-06-27) 76 | ------------------- 77 | 78 | * Fixed logging setup. 79 | 80 | v0.6 (2012-06-27) 81 | ----------------- 82 | 83 | * **backwards incompatible change** 84 | 85 | Added support for `django-celery-transactions`_ to make sure the tasks 86 | are respecting Django's transaction management. It holds on to Celery tasks 87 | until the current database transaction is committed, avoiding potential 88 | race conditions as described in `Celery's user guide`_. 89 | 90 | This is **enabled by default** but can be disabled in case you want 91 | to manually manage the transactions:: 92 | 93 | CELERY_HAYSTACK_TRANSACTION_SAFE = False 94 | 95 | * Refactored the error handling to always return a message about what 96 | happened in every step of the index interaction. Raises exception about 97 | misconfiguration and wrong parameters quicker. 98 | 99 | * Improved support for multiple search indexes as implemented by 100 | Haystack 2.X. Many thanks to Germán M. Bravo (Kronuz). 101 | 102 | .. _`django-celery-transactions`: https://github.com/chrisdoble/django-celery-transactions 103 | .. _`Celery's user guide`: http://celery.readthedocs.org/en/latest/userguide/tasks.html#database-transactions 104 | 105 | v0.5 (2012-05-23) 106 | ----------------- 107 | 108 | * Moved repository to personal account again: http://github.com/jezdez/celery-haystack 109 | 110 | * Removed versiontools dependency again. 111 | 112 | * Moved to using Travis-CI for tests: http://travis-ci.org/jezdez/celery-haystack 113 | 114 | v0.4 (2011-09-17) 115 | ----------------- 116 | 117 | * Fixed bug which caused the deletion of an item to not happen correctly. 118 | Please rebuild your Haystack indexes using the ``rebuild_index`` 119 | management command. 120 | 121 | * Added initial Sphinx documentation: http://celery-haystack.rtfd.org 122 | 123 | * Revamped the tets to test the search results, not only queuing. 124 | 125 | v0.3.1 (2011-08-22) 126 | ------------------- 127 | 128 | * Minor bugfix in new appconf support code. 129 | 130 | v0.3 (2011-08-22) 131 | ----------------- 132 | 133 | * Moved configuration defaults handling to django-appconf_. 134 | 135 | * Fixed issue that occurred when retrying a task. 136 | 137 | .. _django-appconf: http://pypi.python.org/pypi/django-appconf 138 | 139 | v0.2.1 (2011-08-05) 140 | ------------------- 141 | 142 | * Fixed typo in exception message handling. 143 | 144 | v0.2 (2011-08-04) 145 | ----------------- 146 | 147 | * Added support for Haystack 1.2.X. 148 | 149 | * Properly stop indexing if instance couldn't be found. 150 | 151 | * Forced Celery task config values to be of the correct type. 152 | 153 | v0.1.2 (2011-07-29) and v0.1.3 (2011-08-01) 154 | ------------------------------------------- 155 | 156 | * Removed stale print statement. 157 | 158 | v0.1.1 (2011-07-29) 159 | ------------------- 160 | 161 | * Fixed packaging issue (added manifest template). 162 | 163 | 164 | v0.1 (2011-07-29) 165 | ----------------- 166 | 167 | * Initial release. 168 | -------------------------------------------------------------------------------- /docs/conf.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # 3 | # celery-haystack documentation build configuration file, created by 4 | # sphinx-quickstart on Sat Sep 17 14:02:10 2011. 5 | # 6 | # This file is execfile()d with the current directory set to its containing dir. 7 | # 8 | # Note that not all possible configuration values are present in this 9 | # autogenerated file. 10 | # 11 | # All configuration values have a default; values that are commented out 12 | # serve to show the default. 13 | 14 | # import os 15 | # import sys 16 | 17 | # If extensions (or modules to document with autodoc) are in another directory, 18 | # add these directories to sys.path here. If the directory is relative to the 19 | # documentation root, use os.path.abspath to make it absolute, like shown here. 20 | #sys.path.insert(0, os.path.abspath('.')) 21 | 22 | # -- General configuration ----------------------------------------------------- 23 | 24 | # If your documentation needs a minimal Sphinx version, state it here. 25 | #needs_sphinx = '1.0' 26 | 27 | # Add any Sphinx extension module names here, as strings. They can be extensions 28 | # coming with Sphinx (named 'sphinx.ext.*') or your custom ones. 29 | extensions = ['sphinx.ext.autodoc', 'sphinx.ext.intersphinx'] 30 | 31 | # Add any paths that contain templates here, relative to this directory. 32 | templates_path = ['_templates'] 33 | 34 | # The suffix of source filenames. 35 | source_suffix = '.rst' 36 | 37 | # The encoding of source files. 38 | #source_encoding = 'utf-8-sig' 39 | 40 | # The master toctree document. 41 | master_doc = 'index' 42 | 43 | # General information about the project. 44 | project = u'celery-haystack' 45 | copyright = u'2011-2013, Jannis Leidel and contributors' 46 | 47 | # The version info for the project you're documenting, acts as replacement for 48 | # |version| and |release|, also used in various other places throughout the 49 | # built documents. 50 | # 51 | try: 52 | from celery_haystack import __version__ 53 | # The short X.Y version. 54 | version = '.'.join(__version__.split('.')[:2]) 55 | # The full version, including alpha/beta/rc tags. 56 | release = __version__ 57 | except ImportError: 58 | version = release = 'dev' 59 | 60 | # The language for content autogenerated by Sphinx. Refer to documentation 61 | # for a list of supported languages. 62 | #language = None 63 | 64 | # There are two options for replacing |today|: either, you set today to some 65 | # non-false value, then it is used: 66 | #today = '' 67 | # Else, today_fmt is used as the format for a strftime call. 68 | #today_fmt = '%B %d, %Y' 69 | 70 | # List of patterns, relative to source directory, that match files and 71 | # directories to ignore when looking for source files. 72 | exclude_patterns = ['_build'] 73 | 74 | # The reST default role (used for this markup: `text`) to use for all documents. 75 | #default_role = None 76 | 77 | # If true, '()' will be appended to :func: etc. cross-reference text. 78 | #add_function_parentheses = True 79 | 80 | # If true, the current module name will be prepended to all description 81 | # unit titles (such as .. function::). 82 | #add_module_names = True 83 | 84 | # If true, sectionauthor and moduleauthor directives will be shown in the 85 | # output. They are ignored by default. 86 | #show_authors = False 87 | 88 | # The name of the Pygments (syntax highlighting) style to use. 89 | pygments_style = 'sphinx' 90 | 91 | # A list of ignored prefixes for module index sorting. 92 | #modindex_common_prefix = [] 93 | 94 | 95 | # -- Options for HTML output --------------------------------------------------- 96 | 97 | # The theme to use for HTML and HTML Help pages. See the documentation for 98 | # a list of builtin themes. 99 | html_theme = 'default' 100 | 101 | # Theme options are theme-specific and customize the look and feel of a theme 102 | # further. For a list of options available for each theme, see the 103 | # documentation. 104 | #html_theme_options = {} 105 | 106 | # Add any paths that contain custom themes here, relative to this directory. 107 | #html_theme_path = [] 108 | 109 | # The name for this set of Sphinx documents. If None, it defaults to 110 | # " v documentation". 111 | #html_title = None 112 | 113 | # A shorter title for the navigation bar. Default is the same as html_title. 114 | #html_short_title = None 115 | 116 | # The name of an image file (relative to this directory) to place at the top 117 | # of the sidebar. 118 | #html_logo = None 119 | 120 | # The name of an image file (within the static path) to use as favicon of the 121 | # docs. This file should be a Windows icon file (.ico) being 16x16 or 32x32 122 | # pixels large. 123 | #html_favicon = None 124 | 125 | # Add any paths that contain custom static files (such as style sheets) here, 126 | # relative to this directory. They are copied after the builtin static files, 127 | # so a file named "default.css" will overwrite the builtin "default.css". 128 | # html_static_path = ['_static'] 129 | 130 | # If not '', a 'Last updated on:' timestamp is inserted at every page bottom, 131 | # using the given strftime format. 132 | #html_last_updated_fmt = '%b %d, %Y' 133 | 134 | # If true, SmartyPants will be used to convert quotes and dashes to 135 | # typographically correct entities. 136 | #html_use_smartypants = True 137 | 138 | # Custom sidebar templates, maps document names to template names. 139 | #html_sidebars = {} 140 | 141 | # Additional templates that should be rendered to pages, maps page names to 142 | # template names. 143 | #html_additional_pages = {} 144 | 145 | # If false, no module index is generated. 146 | #html_domain_indices = True 147 | 148 | # If false, no index is generated. 149 | #html_use_index = True 150 | 151 | # If true, the index is split into individual pages for each letter. 152 | #html_split_index = False 153 | 154 | # If true, links to the reST sources are added to the pages. 155 | #html_show_sourcelink = True 156 | 157 | # If true, "Created using Sphinx" is shown in the HTML footer. Default is True. 158 | #html_show_sphinx = True 159 | 160 | # If true, "(C) Copyright ..." is shown in the HTML footer. Default is True. 161 | #html_show_copyright = True 162 | 163 | # If true, an OpenSearch description file will be output, and all pages will 164 | # contain a tag referring to it. The value of this option must be the 165 | # base URL from which the finished HTML is served. 166 | #html_use_opensearch = '' 167 | 168 | # This is the file name suffix for HTML files (e.g. ".xhtml"). 169 | #html_file_suffix = None 170 | 171 | # Output file base name for HTML help builder. 172 | htmlhelp_basename = 'celery-haystackdoc' 173 | 174 | 175 | # -- Options for LaTeX output -------------------------------------------------- 176 | 177 | # The paper size ('letter' or 'a4'). 178 | #latex_paper_size = 'letter' 179 | 180 | # The font size ('10pt', '11pt' or '12pt'). 181 | #latex_font_size = '10pt' 182 | 183 | # Grouping the document tree into LaTeX files. List of tuples 184 | # (source start file, target name, title, author, documentclass [howto/manual]). 185 | latex_documents = [ 186 | ('index', 'celery-haystack.tex', u'celery-haystack Documentation', 187 | u'Jannis Leidel', 'manual'), 188 | ] 189 | 190 | # The name of an image file (relative to this directory) to place at the top of 191 | # the title page. 192 | #latex_logo = None 193 | 194 | # For "manual" documents, if this is true, then toplevel headings are parts, 195 | # not chapters. 196 | #latex_use_parts = False 197 | 198 | # If true, show page references after internal links. 199 | #latex_show_pagerefs = False 200 | 201 | # If true, show URL addresses after external links. 202 | #latex_show_urls = False 203 | 204 | # Additional stuff for the LaTeX preamble. 205 | #latex_preamble = '' 206 | 207 | # Documents to append as an appendix to all manuals. 208 | #latex_appendices = [] 209 | 210 | # If false, no module index is generated. 211 | #latex_domain_indices = True 212 | 213 | 214 | # -- Options for manual page output -------------------------------------------- 215 | 216 | # One entry per manual page. List of tuples 217 | # (source start file, name, description, authors, manual section). 218 | man_pages = [ 219 | ('index', 'celery-haystack', u'celery-haystack Documentation', 220 | [u'Jannis Leidel'], 1) 221 | ] 222 | 223 | 224 | # Example configuration for intersphinx: refer to the Python standard library. 225 | intersphinx_mapping = { 226 | 'python': ('http://docs.python.org/2.7', None), 227 | 'sphinx': ('http://sphinx.pocoo.org/', None), 228 | 'django': ('http://django.readthedocs.org/en/latest/', None), 229 | } 230 | -------------------------------------------------------------------------------- /docs/index.rst: -------------------------------------------------------------------------------- 1 | .. include:: ../README.rst 2 | 3 | Contents: 4 | 5 | .. toctree:: 6 | :maxdepth: 2 7 | 8 | changelog 9 | -------------------------------------------------------------------------------- /docs/make.bat: -------------------------------------------------------------------------------- 1 | @ECHO OFF 2 | 3 | REM Command file for Sphinx documentation 4 | 5 | if "%SPHINXBUILD%" == "" ( 6 | set SPHINXBUILD=sphinx-build 7 | ) 8 | set BUILDDIR=_build 9 | set ALLSPHINXOPTS=-d %BUILDDIR%/doctrees %SPHINXOPTS% . 10 | if NOT "%PAPER%" == "" ( 11 | set ALLSPHINXOPTS=-D latex_paper_size=%PAPER% %ALLSPHINXOPTS% 12 | ) 13 | 14 | if "%1" == "" goto help 15 | 16 | if "%1" == "help" ( 17 | :help 18 | echo.Please use `make ^` where ^ is one of 19 | echo. html to make standalone HTML files 20 | echo. dirhtml to make HTML files named index.html in directories 21 | echo. singlehtml to make a single large HTML file 22 | echo. pickle to make pickle files 23 | echo. json to make JSON files 24 | echo. htmlhelp to make HTML files and a HTML help project 25 | echo. qthelp to make HTML files and a qthelp project 26 | echo. devhelp to make HTML files and a Devhelp project 27 | echo. epub to make an epub 28 | echo. latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter 29 | echo. text to make text files 30 | echo. man to make manual pages 31 | echo. changes to make an overview over all changed/added/deprecated items 32 | echo. linkcheck to check all external links for integrity 33 | echo. doctest to run all doctests embedded in the documentation if enabled 34 | goto end 35 | ) 36 | 37 | if "%1" == "clean" ( 38 | for /d %%i in (%BUILDDIR%\*) do rmdir /q /s %%i 39 | del /q /s %BUILDDIR%\* 40 | goto end 41 | ) 42 | 43 | if "%1" == "html" ( 44 | %SPHINXBUILD% -b html %ALLSPHINXOPTS% %BUILDDIR%/html 45 | if errorlevel 1 exit /b 1 46 | echo. 47 | echo.Build finished. The HTML pages are in %BUILDDIR%/html. 48 | goto end 49 | ) 50 | 51 | if "%1" == "dirhtml" ( 52 | %SPHINXBUILD% -b dirhtml %ALLSPHINXOPTS% %BUILDDIR%/dirhtml 53 | if errorlevel 1 exit /b 1 54 | echo. 55 | echo.Build finished. The HTML pages are in %BUILDDIR%/dirhtml. 56 | goto end 57 | ) 58 | 59 | if "%1" == "singlehtml" ( 60 | %SPHINXBUILD% -b singlehtml %ALLSPHINXOPTS% %BUILDDIR%/singlehtml 61 | if errorlevel 1 exit /b 1 62 | echo. 63 | echo.Build finished. The HTML pages are in %BUILDDIR%/singlehtml. 64 | goto end 65 | ) 66 | 67 | if "%1" == "pickle" ( 68 | %SPHINXBUILD% -b pickle %ALLSPHINXOPTS% %BUILDDIR%/pickle 69 | if errorlevel 1 exit /b 1 70 | echo. 71 | echo.Build finished; now you can process the pickle files. 72 | goto end 73 | ) 74 | 75 | if "%1" == "json" ( 76 | %SPHINXBUILD% -b json %ALLSPHINXOPTS% %BUILDDIR%/json 77 | if errorlevel 1 exit /b 1 78 | echo. 79 | echo.Build finished; now you can process the JSON files. 80 | goto end 81 | ) 82 | 83 | if "%1" == "htmlhelp" ( 84 | %SPHINXBUILD% -b htmlhelp %ALLSPHINXOPTS% %BUILDDIR%/htmlhelp 85 | if errorlevel 1 exit /b 1 86 | echo. 87 | echo.Build finished; now you can run HTML Help Workshop with the ^ 88 | .hhp project file in %BUILDDIR%/htmlhelp. 89 | goto end 90 | ) 91 | 92 | if "%1" == "qthelp" ( 93 | %SPHINXBUILD% -b qthelp %ALLSPHINXOPTS% %BUILDDIR%/qthelp 94 | if errorlevel 1 exit /b 1 95 | echo. 96 | echo.Build finished; now you can run "qcollectiongenerator" with the ^ 97 | .qhcp project file in %BUILDDIR%/qthelp, like this: 98 | echo.^> qcollectiongenerator %BUILDDIR%\qthelp\celery-haystack.qhcp 99 | echo.To view the help file: 100 | echo.^> assistant -collectionFile %BUILDDIR%\qthelp\celery-haystack.ghc 101 | goto end 102 | ) 103 | 104 | if "%1" == "devhelp" ( 105 | %SPHINXBUILD% -b devhelp %ALLSPHINXOPTS% %BUILDDIR%/devhelp 106 | if errorlevel 1 exit /b 1 107 | echo. 108 | echo.Build finished. 109 | goto end 110 | ) 111 | 112 | if "%1" == "epub" ( 113 | %SPHINXBUILD% -b epub %ALLSPHINXOPTS% %BUILDDIR%/epub 114 | if errorlevel 1 exit /b 1 115 | echo. 116 | echo.Build finished. The epub file is in %BUILDDIR%/epub. 117 | goto end 118 | ) 119 | 120 | if "%1" == "latex" ( 121 | %SPHINXBUILD% -b latex %ALLSPHINXOPTS% %BUILDDIR%/latex 122 | if errorlevel 1 exit /b 1 123 | echo. 124 | echo.Build finished; the LaTeX files are in %BUILDDIR%/latex. 125 | goto end 126 | ) 127 | 128 | if "%1" == "text" ( 129 | %SPHINXBUILD% -b text %ALLSPHINXOPTS% %BUILDDIR%/text 130 | if errorlevel 1 exit /b 1 131 | echo. 132 | echo.Build finished. The text files are in %BUILDDIR%/text. 133 | goto end 134 | ) 135 | 136 | if "%1" == "man" ( 137 | %SPHINXBUILD% -b man %ALLSPHINXOPTS% %BUILDDIR%/man 138 | if errorlevel 1 exit /b 1 139 | echo. 140 | echo.Build finished. The manual pages are in %BUILDDIR%/man. 141 | goto end 142 | ) 143 | 144 | if "%1" == "changes" ( 145 | %SPHINXBUILD% -b changes %ALLSPHINXOPTS% %BUILDDIR%/changes 146 | if errorlevel 1 exit /b 1 147 | echo. 148 | echo.The overview file is in %BUILDDIR%/changes. 149 | goto end 150 | ) 151 | 152 | if "%1" == "linkcheck" ( 153 | %SPHINXBUILD% -b linkcheck %ALLSPHINXOPTS% %BUILDDIR%/linkcheck 154 | if errorlevel 1 exit /b 1 155 | echo. 156 | echo.Link check complete; look for any errors in the above output ^ 157 | or in %BUILDDIR%/linkcheck/output.txt. 158 | goto end 159 | ) 160 | 161 | if "%1" == "doctest" ( 162 | %SPHINXBUILD% -b doctest %ALLSPHINXOPTS% %BUILDDIR%/doctest 163 | if errorlevel 1 exit /b 1 164 | echo. 165 | echo.Testing of doctests in the sources finished, look at the ^ 166 | results in %BUILDDIR%/doctest/output.txt. 167 | goto end 168 | ) 169 | 170 | :end 171 | -------------------------------------------------------------------------------- /requirements/v2.txt: -------------------------------------------------------------------------------- 1 | django-discover-runner 2 | django-haystack 3 | celery 4 | Whoosh 5 | flake8 6 | coverage 7 | -------------------------------------------------------------------------------- /setup.cfg: -------------------------------------------------------------------------------- 1 | [global] 2 | setup-hooks = 3 | celery_haystack.version_hook 4 | 5 | [metadata] 6 | name = celery-haystack 7 | author = Jannis Leidel 8 | author-email = jannis@leidel.info 9 | summary = An app for integrating Celery with Haystack. 10 | description-file = README.rst 11 | home-page = http://celery-haystack.rtfd.org/ 12 | project-url = 13 | Github, https://github.com/django-haystack/celery-haystack/ 14 | classifier = 15 | Development Status :: 4 - Beta 16 | Environment :: Web Environment 17 | Framework :: Django 18 | Intended Audience :: Developers 19 | License :: OSI Approved :: BSD License 20 | Operating System :: OS Independent 21 | Programming Language :: Python :: 3 22 | Topic :: Utilities 23 | requires-dist = 24 | django-appconf>=0.4.1 25 | 26 | 27 | [files] 28 | packages = 29 | celery_haystack 30 | celery_haystack.tests 31 | extra_files = 32 | README.rst 33 | AUTHORS 34 | 35 | [backwards_compat] 36 | zip_safe = False 37 | 38 | [wheel] 39 | universal = 1 40 | -------------------------------------------------------------------------------- /setup.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | from setuptools import setup 3 | 4 | setup(setup_requires=['d2to1'], d2to1=True) 5 | --------------------------------------------------------------------------------