@@ -12,29 +12,34 @@ class AlgoliaIndexError(Exception):
12
12
class AlgoliaIndex (object ):
13
13
'''An index in the Algolia backend.'''
14
14
15
- # Use to specify a custom field that will be used for the objectID.
15
+ # Used to specify a custom field that will be used for the objectID.
16
16
# This field should be unique.
17
17
custom_objectID = None
18
18
19
- # Use to specify the fields that should be included in the index.
19
+ # Used to specify the fields that should be included in the index.
20
20
fields = ()
21
21
22
- # Use to specify the geo-fields that should be used for location search.
22
+ # Used to specify the geo-fields that should be used for location search.
23
23
# The attribute should be a callable that returns a tuple.
24
24
geo_field = None
25
25
26
- # Use to specify the field that should be used for filtering by tag.
26
+ # Used to specify the field that should be used for filtering by tag.
27
27
tags = None
28
28
29
- # Use to specify the index to target on Algolia.
29
+ # Used to specify the index to target on Algolia.
30
30
index_name = None
31
31
32
- # Use to specify the settings of the index.
32
+ # Used to specify the settings of the index.
33
33
settings = {}
34
34
35
- # Use to specify a callable that say if the instance should be indexed.
36
- # The attribute should be a callable that returns a boolean.
35
+ # Used to specify if the instance should be indexed.
36
+ # The attribute should be either:
37
+ # - a callable that returns a boolean.
38
+ # - a BooleanField
39
+ # - a boolean property or attribute
37
40
should_index = None
41
+ # Name of the attribute to check on instances if should_index is not a callable
42
+ _should_index_is_method = False
38
43
39
44
# Instance of the index from algoliasearch client
40
45
__index = None
@@ -52,7 +57,7 @@ def __init__(self, model, client):
52
57
53
58
# Avoid error when there is only one field to index
54
59
if isinstance (self .fields , str ):
55
- self .fields = (self .fields , )
60
+ self .fields = (self .fields ,)
56
61
57
62
# Check fields
58
63
for field in self .fields :
@@ -63,7 +68,7 @@ def __init__(self, model, client):
63
68
# Check custom_objectID
64
69
if self .custom_objectID :
65
70
if not (hasattr (model , self .custom_objectID ) or
66
- (self .custom_objectID in all_fields )):
71
+ (self .custom_objectID in all_fields )):
67
72
raise AlgoliaIndexError ('{} is not an attribute of {}.' .format (
68
73
self .custom_objectID , model ))
69
74
@@ -91,18 +96,20 @@ def __init__(self, model, client):
91
96
raise AlgoliaIndexError ('{} is not an attribute of {}.' .format (
92
97
self .geo_field , model ))
93
98
94
- # Check should_index + get the callable
99
+ # Check should_index + get the callable or attribute/field name
95
100
if self .should_index :
96
101
if hasattr (model , self .should_index ):
97
102
attr = getattr (model , self .should_index )
98
- if callable (attr ):
103
+ if type (attr ) is not bool : # if attr is a bool, we keep attr=name to getattr on instance
99
104
self .should_index = attr
100
- else :
101
- raise AlgoliaIndexError ('{} should be a callable.' .format (
102
- self .should_index ))
105
+ if callable (self .should_index ):
106
+ self ._should_index_is_method = True
103
107
else :
104
- raise AlgoliaIndexError ('{} is not an attribute of {}.' .format (
105
- self .should_index , model ))
108
+ try :
109
+ model ._meta .get_field_by_name (self .should_index )
110
+ except :
111
+ raise AlgoliaIndexError ('{} is not an attribute nor a field of {}.' .format (
112
+ self .should_index , model ))
106
113
107
114
def __set_index (self , client ):
108
115
'''Get an instance of Algolia Index'''
@@ -188,9 +195,9 @@ def _build_object(self, instance):
188
195
return tmp
189
196
190
197
def update_obj_index (self , instance ):
191
- ''' Update the object.'''
192
- if self .should_index :
193
- if not self .should_index (instance ):
198
+ """ Update the object."""
199
+ if self ._has_should_index () :
200
+ if not self ._should_really_index (instance ):
194
201
# Should not index, but since we don't now the state of the
195
202
# instance, we need to send a DELETE request to ensure that if
196
203
# the instance was previously indexed, it will be removed.
@@ -201,6 +208,41 @@ def update_obj_index(self, instance):
201
208
self .__index .save_object (obj )
202
209
logger .debug ('UPDATE %s FROM %s' , obj ['objectID' ], self .model )
203
210
211
+ def _has_should_index (self ):
212
+ """Return True if this AlgoliaIndex has a should_index method or attribute"""
213
+ return self .should_index is not None
214
+
215
+ def _should_index (self , instance ):
216
+ """Return True if the object should be indexed (including when self.should_index is not set)."""
217
+ if self ._has_should_index ():
218
+ return self ._should_really_index (instance )
219
+ else :
220
+ return True
221
+
222
+ def _should_really_index (self , instance ):
223
+ """Return True if according to should_index the object should be indexed."""
224
+ if self ._should_index_is_method :
225
+ if hasattr (self .should_index , "__self__" ):
226
+ # bound method, call with instance
227
+ return self .should_index (instance )
228
+ else :
229
+ # unbound method, simply call without arguments
230
+ return self .should_index ()
231
+ else :
232
+ # property/attribute/Field, evaluate as bool
233
+ attr_type = type (self .should_index )
234
+ if attr_type is str :
235
+ attr_value = getattr (instance , self .should_index )
236
+ elif attr_type is property :
237
+ attr_value = self .should_index .__get__ (instance )
238
+ else :
239
+ raise AlgoliaIndexError ('{} should be a boolean attribute or a method that returns a boolean.' .format (
240
+ self .should_index ))
241
+ if type (attr_value ) is not bool :
242
+ raise AlgoliaIndexError ("%s's should_index (%s) should be a boolean" % (
243
+ instance .__class__ .__name__ , self .should_index ))
244
+ return attr_value
245
+
204
246
def delete_obj_index (self , instance ):
205
247
'''Delete the object.'''
206
248
objectID = self .__get_objectID (instance )
0 commit comments