Repository: django-haystack/django-haystack

Issue:
ModuleNotFoundError: No module named 'whoosh'
## Problem
I want to be able to use django-haystack + elasticsearch backend without needing to install the `whoosh` package, since I'm not using the Whoosh backend. I currently get an `ModuleNotFoundError: No module named 'whoosh'` error when attempting to use haystack. Once the `whoosh` package is installed, the error goes away, but I don't want to have to do that since I'm not using the Whoosh backend.

* [ ] Tested with the latest Haystack release (no, this only happens on the current master branch)
* [X] Tested with the current Haystack master branch

## Expected behaviour
The ability to use `django-haystack` with a non-Whoosh backend without needing to install the `Whoosh` package

## Actual behaviour
Using haystack throws `ModuleNotFoundError: No module named 'whoosh'` when I don't have the `whoosh` package installed

## Steps to reproduce the behaviour

1. Put `git+https://github.com/django-haystack/django-haystack.git@8ab14a273d8f438fe3542d988954e41bddc13fc8#egg=django-haystack` in your requirements.txt file, then run `pip install -r requirements.txt`
2. Start a Django shell with `python3 manage.py shell`
3. Try to use `haystack`:
```python
[1]: from haystack.query import SearchQuerySet

[2]: SearchQuerySet().all().count()
```
4. See the error:
```
/usr/local/lib/python3.6/dist-packages/haystack/fields.py in <module>                                                                                                                
      4 from django.template import loader                                                
      5 from django.utils import datetime_safe                                                                                                                                       
----> 6 from whoosh import analysis                                                       
      7                                                                                   
      8 from haystack.exceptions import SearchFieldError                                  
                                                                                          
ModuleNotFoundError: No module named 'whoosh'
```

## Configuration

* Operating system version: Ubuntu 18.04.5 LTS (in a container)
* Search engine version: Elasticsearch 5.6
* Python version: 3.6
* Django version: 2.2
* Haystack version: django-haystack-3.1.dev68+g8ab14a2


Discussion:
Ouch, sorry about that – it looks like an oversight in #1789. We need to make that conditional.
@acdha If you could describe what a fix would look like, I could work on a PR that addresses this issue.

Resolving patch:
diff --git a/haystack/backends/whoosh_backend.py b/haystack/backends/whoosh_backend.py
--- a/haystack/backends/whoosh_backend.py
+++ b/haystack/backends/whoosh_backend.py
@@ -226,7 +226,7 @@ def build_schema(self, fields):
             else:
                 schema_fields[field_class.index_fieldname] = TEXT(
                     stored=True,
-                    analyzer=field_class.analyzer,
+                    analyzer=field_class.analyzer or StemmingAnalyzer(),
                     field_boost=field_class.boost,
                     sortable=True,
                 )
diff --git a/haystack/fields.py b/haystack/fields.py
--- a/haystack/fields.py
+++ b/haystack/fields.py
@@ -3,7 +3,6 @@
 
 from django.template import loader
 from django.utils import datetime_safe
-from whoosh import analysis
 
 from haystack.exceptions import SearchFieldError
 from haystack.utils import get_model_ct_tuple
@@ -46,7 +45,7 @@ def __init__(
         facet_class=None,
         boost=1.0,
         weight=None,
-        analyzer=NOT_PROVIDED,
+        analyzer=None,
     ):
         # Track what the index thinks this field is called.
         self.instance_name = None
@@ -61,7 +60,7 @@ def __init__(
         self.null = null
         self.index_fieldname = index_fieldname
         self.boost = weight or boost
-        self._analyzer = analyzer
+        self.analyzer = analyzer
         self.is_multivalued = False
 
         # We supply the facet_class for making it easy to create a faceted
@@ -73,12 +72,6 @@ def __init__(
 
         self.set_instance_name(None)
 
-    @property
-    def analyzer(self):
-        if self._analyzer is NOT_PROVIDED:
-            return None
-        return self._analyzer
-
     def set_instance_name(self, instance_name):
         self.instance_name = instance_name
 
@@ -233,15 +226,10 @@ def convert(self, value):
 class CharField(SearchField):
     field_type = "string"
 
-    def __init__(self, analyzer=NOT_PROVIDED, **kwargs):
+    def __init__(self, **kwargs):
         if kwargs.get("facet_class") is None:
             kwargs["facet_class"] = FacetCharField
 
-        # use StemmingAnalyzer by default
-        kwargs["analyzer"] = (
-            analysis.StemmingAnalyzer() if analyzer is NOT_PROVIDED else analyzer
-        )
-
         super().__init__(**kwargs)
 
     def prepare(self, obj):


Test patch: