Skip to content

Commit c3bfa34

Browse files
author
Kenneth Yu
committed
Merge remote-tracking branch 'upstream/master'
2 parents 620307e + 1f01077 commit c3bfa34

37 files changed

+338
-276
lines changed

.travis.yml

Lines changed: 10 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ python:
55
- 2.7
66
- 3.4
77
- 3.5
8+
- 3.6
89
- pypy
910

1011
cache:
@@ -58,17 +59,19 @@ script:
5859

5960
env:
6061
matrix:
61-
- DJANGO_VERSION=">=1.8,<1.9" VERSION_ES=">=1.0.0,<2.0.0"
62-
- DJANGO_VERSION=">=1.9,<1.10" VERSION_ES=">=1.0.0,<2.0.0"
63-
- DJANGO_VERSION=">=1.10,<1.11" VERSION_ES=">=1.0.0,<2.0.0"
64-
- DJANGO_VERSION=">=1.8,<1.9" VERSION_ES=">=2.0.0,<3.0.0"
65-
- DJANGO_VERSION=">=1.9,<1.10" VERSION_ES=">=2.0.0,<3.0.0"
66-
- DJANGO_VERSION=">=1.10,<1.11" VERSION_ES=">=2.0.0,<3.0.0"
67-
- DJANGO_VERSION=">=1.11,<1.12" VERSION_ES=">=2.0.0,<3.0.0"
62+
- DJANGO_VERSION=">=1.11,<2.0" VERSION_ES=">=1.0.0,<2.0.0"
63+
- DJANGO_VERSION=">=2.0,<2.1" VERSION_ES=">=1.0.0,<2.0.0"
64+
- DJANGO_VERSION=">=1.11,<2.0" VERSION_ES=">=2.0.0,<3.0.0"
65+
- DJANGO_VERSION=">=2.0,<2.1" VERSION_ES=">=2.0.0,<3.0.0"
6866

6967
matrix:
7068
allow_failures:
7169
- python: 'pypy'
70+
exclude:
71+
- python: 2.7
72+
env: DJANGO_VERSION=">=2.0,<2.1" VERSION_ES=">=2.0.0,<3.0.0"
73+
- python: 2.7
74+
env: DJANGO_VERSION=">=2.0,<2.1" VERSION_ES=">=1.0.0,<2.0.0"
7275

7376
notifications:
7477
irc: "irc.freenode.org#haystack"

AUTHORS

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -117,3 +117,4 @@ Thanks to
117117
* Morgan Aubert (@ellmetha) for Django 1.10 support
118118
* João Junior (@joaojunior) and Bruno Marques (@ElSaico) for Elasticsearch 2.x support
119119
* Alex Tomkins (@tomkins) for various patches
120+
* Martin Pauly (@mpauly) for Django 2.0 support

haystack/__init__.py

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44

55
from django.conf import settings
66
from django.core.exceptions import ImproperlyConfigured
7-
from pkg_resources import DistributionNotFound, get_distribution
7+
from pkg_resources import DistributionNotFound, get_distribution, parse_version
88

99
from haystack import signals
1010
from haystack.constants import DEFAULT_ALIAS
@@ -13,9 +13,12 @@
1313
__author__ = 'Daniel Lindsley'
1414

1515
try:
16-
__version__ = get_distribution(__name__).version
16+
pkg_distribution = get_distribution(__name__)
17+
__version__ = pkg_distribution.version
18+
version_info = pkg_distribution.parsed_version
1719
except DistributionNotFound:
18-
__version__ = (0, 0, 'dev0')
20+
__version__ = '0.0.dev0'
21+
version_info = parse_version(__version__)
1922

2023
default_app_config = 'haystack.apps.HaystackConfig'
2124

haystack/backends/elasticsearch_backend.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -296,7 +296,7 @@ def build_search_kwargs(self, query_string, sort_by=None, start_offset=0, end_of
296296
for field, direction in sort_by:
297297
if field == 'distance' and distance_point:
298298
# Do the geo-enabled sort.
299-
lng, lat = distance_point['point'].get_coords()
299+
lng, lat = distance_point['point'].coords
300300
sort_kwargs = {
301301
"_geo_distance": {
302302
distance_point['field']: [lng, lat],
@@ -455,7 +455,7 @@ def build_search_kwargs(self, query_string, sort_by=None, start_offset=0, end_of
455455
filters.append(within_filter)
456456

457457
if dwithin is not None:
458-
lng, lat = dwithin['point'].get_coords()
458+
lng, lat = dwithin['point'].coords
459459

460460
# NB: the 1.0.0 release of elasticsearch introduce an
461461
# incompatible change on the distance filter formating
@@ -625,8 +625,8 @@ def from_timestamp(tm):
625625
model = haystack_get_model(app_label, model_name)
626626

627627
if model and model in indexed_models:
628+
index = source and unified_index.get_index(model)
628629
for key, value in source.items():
629-
index = unified_index.get_index(model)
630630
string_key = str(key)
631631

632632
if string_key in index.fields and hasattr(index.fields[string_key], 'convert'):

haystack/backends/solr_backend.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -173,7 +173,7 @@ def build_search_kwargs(self, query_string, sort_by=None, start_offset=0, end_of
173173
if sort_by is not None:
174174
if sort_by in ['distance asc', 'distance desc'] and distance_point:
175175
# Do the geo-enabled sort.
176-
lng, lat = distance_point['point'].get_coords()
176+
lng, lat = distance_point['point'].coords
177177
kwargs['sfield'] = distance_point['field']
178178
kwargs['pt'] = '%s,%s' % (lat, lng)
179179

@@ -290,7 +290,7 @@ def build_search_kwargs(self, query_string, sort_by=None, start_offset=0, end_of
290290

291291
if dwithin is not None:
292292
kwargs.setdefault('fq', [])
293-
lng, lat = dwithin['point'].get_coords()
293+
lng, lat = dwithin['point'].coords
294294
geofilt = '{!geofilt pt=%s,%s sfield=%s d=%s}' % (lat, lng, dwithin['field'], dwithin['distance'].km)
295295
kwargs['fq'].append(geofilt)
296296

haystack/backends/whoosh_backend.py

Lines changed: 8 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -277,7 +277,7 @@ def optimize(self):
277277
def calculate_page(self, start_offset=0, end_offset=None):
278278
# Prevent against Whoosh throwing an error. Requires an end_offset
279279
# greater than 0.
280-
if not end_offset is None and end_offset <= 0:
280+
if end_offset is not None and end_offset <= 0:
281281
end_offset = 1
282282

283283
# Determine the page.
@@ -353,7 +353,7 @@ def search(self, query_string, sort_by=None, start_offset=0, end_offset=None,
353353
if len(sort_by_list) == 1:
354354
reverse = False
355355

356-
sort_by = sort_by_list[0]
356+
sort_by = sort_by_list
357357

358358
if facets is not None:
359359
warnings.warn("Whoosh does not handle faceting.", Warning, stacklevel=2)
@@ -404,7 +404,7 @@ def search(self, query_string, sort_by=None, start_offset=0, end_offset=None,
404404
if narrowed_results:
405405
narrowed_results.filter(recent_narrowed_results)
406406
else:
407-
narrowed_results = recent_narrowed_results
407+
narrowed_results = recent_narrowed_results
408408

409409
self.index = self.index.refresh()
410410

@@ -484,10 +484,6 @@ def more_like_this(self, model_instance, additional_query_string=None,
484484
if not self.setup_complete:
485485
self.setup()
486486

487-
# Deferred models will have a different class ("RealClass_Deferred_fieldname")
488-
# which won't be in our registry:
489-
model_klass = model_instance._meta.concrete_model
490-
491487
field_name = self.content_field_name
492488
narrow_queries = set()
493489
narrowed_results = None
@@ -533,13 +529,14 @@ def more_like_this(self, model_instance, additional_query_string=None,
533529
if narrowed_results:
534530
narrowed_results.filter(recent_narrowed_results)
535531
else:
536-
narrowed_results = recent_narrowed_results
532+
narrowed_results = recent_narrowed_results
537533

538534
page_num, page_length = self.calculate_page(start_offset, end_offset)
539535

540536
self.index = self.index.refresh()
541537
raw_results = EmptyResults()
542538

539+
searcher = None
543540
if self.index.doc_count():
544541
query = "%s:%s" % (ID, get_identifier(model_instance))
545542
searcher = self.index.searcher()
@@ -575,7 +572,9 @@ def more_like_this(self, model_instance, additional_query_string=None,
575572
}
576573

577574
results = self._process_results(raw_page, result_class=result_class)
578-
searcher.close()
575+
576+
if searcher:
577+
searcher.close()
579578

580579
if hasattr(narrow_searcher, 'close'):
581580
narrow_searcher.close()
@@ -900,13 +899,6 @@ def build_query_fragment(self, field, filter_type, value):
900899
return u"%s%s" % (index_fieldname, query_frag)
901900

902901

903-
# if not filter_type in ('in', 'range'):
904-
# # 'in' is a bit of a special case, as we don't want to
905-
# # convert a valid list/tuple to string. Defer handling it
906-
# # until later...
907-
# value = self.backend._from_python(value)
908-
909-
910902
class WhooshEngine(BaseEngine):
911903
backend = WhooshSearchBackend
912904
query = WhooshSearchQuery

haystack/fields.py

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33

44
import re
55

6-
from django.template import Context, loader
6+
from django.template import loader
77
from django.utils import datetime_safe, six
88

99
from haystack.exceptions import SearchFieldError
@@ -112,7 +112,8 @@ def resolve_attributes_lookup(self, current_objects, attributes):
112112

113113
if len(attributes) > 1:
114114
current_objects_in_attr = self.get_iterable_objects(getattr(current_object, attributes[0]))
115-
return self.resolve_attributes_lookup(current_objects_in_attr, attributes[1:])
115+
values.extend(self.resolve_attributes_lookup(current_objects_in_attr, attributes[1:]))
116+
continue
116117

117118
current_object = getattr(current_object, attributes[0])
118119

@@ -244,6 +245,8 @@ def convert(self, value):
244245
elif isinstance(value, dict):
245246
lat = value.get('lat', 0)
246247
lng = value.get('lon', 0)
248+
else:
249+
raise TypeError('Unable to extract coordinates from %r' % value)
247250

248251
value = Point(float(lng), float(lat))
249252
return value

haystack/generic_views.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,7 @@ def get_form_kwargs(self):
5858
Returns the keyword arguments for instantiating the form.
5959
"""
6060
kwargs = {'initial': self.get_initial()}
61-
if self.request.method == 'GET' and self.request.GET:
61+
if self.request.method == 'GET':
6262
kwargs.update({
6363
'data': self.request.GET,
6464
})

haystack/indexes.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -443,7 +443,7 @@ def should_skip_field(self, field):
443443
return True
444444

445445
# Ignore certain fields (AutoField, related fields).
446-
if field.primary_key or getattr(field, 'rel'):
446+
if field.primary_key or field.is_relation:
447447
return True
448448

449449
return False

haystack/inputs.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ def __init__(self, query_string, **kwargs):
2121
self.kwargs = kwargs
2222

2323
def __repr__(self):
24-
return u"<%s '%s'>" % (self.__class__.__name__, self.__unicode__().encode('utf8'))
24+
return u"<%s '%s'>" % (self.__class__.__name__, self)
2525

2626
def __str__(self):
2727
return force_text(self.query_string)

haystack/management/commands/rebuild_index.py

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
11
# encoding: utf-8
2-
32
from __future__ import absolute_import, division, print_function, unicode_literals
43

54
from django.core.management import call_command
@@ -33,5 +32,11 @@ def add_arguments(self, parser):
3332
)
3433

3534
def handle(self, **options):
36-
call_command('clear_index', **options)
37-
call_command('update_index', **options)
35+
clear_options = options.copy()
36+
update_options = options.copy()
37+
for key in ('batchsize', 'workers'):
38+
del clear_options[key]
39+
for key in ('interactive', ):
40+
del update_options[key]
41+
call_command('clear_index', **clear_options)
42+
call_command('update_index', **update_options)

haystack/management/commands/update_index.py

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -131,6 +131,7 @@ def do_update(backend, index, qs, start, end, total, verbosity=1, commit=True,
131131
reset_queries()
132132
return max_pk
133133

134+
134135
class Command(BaseCommand):
135136
help = "Freshens the index for the given app(s)."
136137

@@ -145,11 +146,13 @@ def add_arguments(self, parser):
145146
)
146147
parser.add_argument(
147148
'-s', '--start', dest='start_date',
148-
help='The start date for indexing within. Can be any dateutil-parsable string, recommended to be YYYY-MM-DDTHH:MM:SS.'
149+
help='The start date for indexing. Can be any dateutil-parsable string;'
150+
' YYYY-MM-DDTHH:MM:SS is recommended to avoid confusion'
149151
)
150152
parser.add_argument(
151153
'-e', '--end', dest='end_date',
152-
help='The end date for indexing within. Can be any dateutil-parsable string, recommended to be YYYY-MM-DDTHH:MM:SS.'
154+
help='The end date for indexing. Can be any dateutil-parsable string;'
155+
' YYYY-MM-DDTHH:MM:SS is recommended to avoid confusion'
153156
)
154157
parser.add_argument(
155158
'-b', '--batch-size', dest='batchsize', type=int,
@@ -268,9 +271,9 @@ def update_backend(self, label, using):
268271

269272
if self.workers == 0:
270273
max_pk = do_update(backend, index, qs, start, end, total,
271-
verbosity=self.verbosity,
272-
commit=self.commit, max_retries=self.max_retries,
273-
last_max_pk=max_pk)
274+
verbosity=self.verbosity,
275+
commit=self.commit, max_retries=self.max_retries,
276+
last_max_pk=max_pk)
274277
else:
275278
ghetto_queue.append((model, start, end, total, using, self.start_date, self.end_date,
276279
self.verbosity, self.commit, self.max_retries))
@@ -296,8 +299,6 @@ def update_backend(self, label, using):
296299
# all pks. Rebuild the list with everything.
297300
qs = index.index_queryset().values_list('pk', flat=True)
298301
database_pks = set(smart_bytes(pk) for pk in qs)
299-
300-
total = len(database_pks)
301302
else:
302303
database_pks = set(smart_bytes(pk) for pk in qs.values_list('pk', flat=True))
303304

haystack/models.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -133,7 +133,7 @@ def _get_distance(self):
133133
if location_field is None:
134134
return None
135135

136-
lf_lng, lf_lat = location_field.get_coords()
136+
lf_lng, lf_lat = location_field.coords
137137
self._distance = Distance(km=geopy_distance.distance((po_lat, po_lng), (lf_lat, lf_lng)).km)
138138

139139
# We've either already calculated it or the backend returned it, so

haystack/query.py

Lines changed: 26 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -147,12 +147,12 @@ def _manual_iter(self):
147147
current_position += 1
148148

149149
if self._cache_is_full():
150-
raise StopIteration
150+
return
151151

152152
# We've run out of results and haven't hit our limit.
153153
# Fill more of the cache.
154154
if not self._fill_cache(current_position, current_position + ITERATOR_LOAD_PER_QUERY):
155-
raise StopIteration
155+
return
156156

157157
def post_process_results(self, results):
158158
to_cache = []
@@ -172,23 +172,32 @@ def post_process_results(self, results):
172172

173173
for result in results:
174174
if self._load_all:
175-
# We have to deal with integer keys being cast from strings
175+
176176
model_objects = loaded_objects.get(result.model, {})
177-
if result.pk not in model_objects:
177+
# Try to coerce a primary key object that matches the models pk
178+
# We have to deal with semi-arbitrary keys being cast from strings (UUID, int, etc)
179+
if model_objects:
180+
result_klass = type(next(iter(model_objects)))
181+
result.pk = result_klass(result.pk)
182+
178183
try:
179-
result.pk = int(result.pk)
180-
except ValueError:
181-
pass
182-
try:
183-
result._object = model_objects[result.pk]
184-
except KeyError:
185-
# The object was either deleted since we indexed or should
186-
# be ignored; fail silently.
187-
self._ignored_result_count += 1
188-
189-
# avoid an unfilled None at the end of the result cache
190-
self._result_cache.pop()
191-
continue
184+
result._object = model_objects[result.pk]
185+
except KeyError:
186+
# The object was either deleted since we indexed or should
187+
# be ignored for other reasons such as an overriden 'load_all_queryset';
188+
# fail silently.
189+
self._ignored_result_count += 1
190+
191+
# avoid an unfilled None at the end of the result cache
192+
self._result_cache.pop()
193+
continue
194+
else:
195+
# No objects were returned -- possible due to SQS nesting such as
196+
# XYZ.objects.filter(id__gt=10) where the amount ignored are
197+
# exactly equal to the ITERATOR_LOAD_PER_QUERY
198+
del self._result_cache[:len(results)]
199+
self._ignored_result_count += len(results)
200+
break
192201

193202
to_cache.append(result)
194203

0 commit comments

Comments
 (0)