Skip to content

Add reverse fk handling in the default serializer. #38

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 11 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
52 changes: 46 additions & 6 deletions django_elasticsearch/managers.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,14 +28,30 @@

u'ForeignKey': 'object',
u'OneToOneField': 'object',
u'ManyToManyField': 'object'
u'ManyToManyField': 'object',

# reverse relationship
u'ManyToOneRel': 'object',
u'ManyToManyRel': 'object',

# dj <1.8
u'RelatedObject': 'object'
}


def needs_instance(f):
def wrapper(*args, **kwargs):
if args[0].instance is None:
raise AttributeError("This method requires an instance of the model.")
raise AttributeError(u"This method requires an instance of the model.")
return f(*args, **kwargs)
return wrapper


def no_abstract(f):
def wrapper(*args, **kwargs):
if args[0].model.Elasticsearch.abstract is True:
raise ValueError(u"This model {0} is abstract - not indexed.".format(
args[0].instance.__class__))
return f(*args, **kwargs)
return wrapper

Expand All @@ -60,13 +76,15 @@ def __init__(self, k):
self.serializer = None
self._mapping = None

@no_abstract
def get_index(self):
return self.model.Elasticsearch.index

@property
def index(self):
return self.get_index()

@no_abstract
def get_doc_type(self):
return (self.model.Elasticsearch.doc_type
or 'model-{0}'.format(self.model.__name__))
Expand Down Expand Up @@ -112,6 +130,7 @@ def deserialize(self, source):
else:
return serializer.deserialize(source)

@no_abstract
@needs_instance
def do_index(self):
body = self.serialize()
Expand All @@ -120,13 +139,15 @@ def do_index(self):
id=self.instance.id,
body=body)

@no_abstract
@needs_instance
def delete(self):
es_client.delete(index=self.index,
doc_type=self.doc_type,
id=self.instance.id,
ignore=404)

@no_abstract
def get(self, **kwargs):
if 'pk' in kwargs:
pk = kwargs.pop('pk')
Expand All @@ -140,6 +161,7 @@ def get(self, **kwargs):

return self.queryset.get(id=pk)

@no_abstract
@needs_instance
def mlt(self, **kwargs):
"""
Expand All @@ -158,13 +180,15 @@ def mlt(self, **kwargs):
"""
return self.queryset.mlt(id=self.instance.id, **kwargs)

@no_abstract
def count(self):
return self.queryset.count()

@property
def queryset(self):
return EsQueryset(self.model)

@no_abstract
def search(self, query,
facets=None, facets_limit=None, global_facets=True,
suggest_fields=None, suggest_limit=None,
Expand Down Expand Up @@ -203,18 +227,22 @@ def search(self, query,
return q.query(query)

# Convenience methods
@no_abstract
def all(self):
"""
proxy to an empty search.
"""
return self.search("")

@no_abstract
def filter(self, **kwargs):
return self.queryset.filter(**kwargs)

@no_abstract
def exclude(self, **kwargs):
return self.queryset.exclude(**kwargs)

@no_abstract
def complete(self, field_name, query):
"""
Returns a list of close values for auto-completion
Expand All @@ -227,6 +255,7 @@ def complete(self, field_name, query):
complete_name = "{0}_complete".format(field_name)
return self.queryset.complete(complete_name, query)

@no_abstract
def do_update(self):
"""
Hit this if you are in a hurry,
Expand All @@ -248,24 +277,29 @@ def make_mapping(self):

for field_name in self.get_fields():
try:
field = self.model._meta.get_field(field_name)
field, a, b, c = self.model._meta.get_field_by_name(field_name)
except FieldDoesNotExist:
# abstract field
mapping = {}
else:
mapping = {'type': ELASTICSEARCH_FIELD_MAP.get(
field.get_internal_type(), 'string')}
field.__class__.__name__, 'string')}
try:
# if an analyzer is set as default, use it.
# TODO: could be also tokenizer, filter, char_filter
if mapping['type'] == 'string':
analyzer = settings.ELASTICSEARCH_SETTINGS['analysis']['default']
mapping['analyzer'] = analyzer
except (ValueError, AttributeError, KeyError, TypeError):
except (AttributeError, KeyError):
# AttributeError - settings.ELASTICSEARCH_SETTINGS is not set
# KeyError - either 'analysis' or 'default' is not set
pass

try:
mapping.update(self.model.Elasticsearch.mappings[field_name])
except (AttributeError, KeyError, TypeError):
except (AttributeError, KeyError):
# AttributeError - Elasticsearch.mappings is not set
# KeyError - Elastisearch.mappings[field_name] is not set
pass
mappings[field_name] = mapping

Expand All @@ -281,6 +315,7 @@ def make_mapping(self):
}
}

@no_abstract
def get_mapping(self):
if self._mapping is None:
# TODO: could be done once for every index/doc_type ?
Expand All @@ -296,6 +331,7 @@ def get_settings(self):
"""
return es_client.indices.get_settings(index=self.index)

@no_abstract
@needs_instance
def diff(self, source=None):
"""
Expand All @@ -321,6 +357,7 @@ def diff(self, source=None):

return diff

@no_abstract
def create_index(self, ignore=True):
body = {}
if hasattr(settings, 'ELASTICSEARCH_SETTINGS'):
Expand All @@ -329,15 +366,18 @@ def create_index(self, ignore=True):
es_client.indices.create(self.index,
body=body,
ignore=ignore and 400)

es_client.indices.put_mapping(index=self.index,
doc_type=self.doc_type,
body=self.make_mapping())

@no_abstract
def reindex_all(self, queryset=None):
q = queryset or self.model.objects.all()
for instance in q:
instance.es.do_index()

@no_abstract
def flush(self):
es_client.indices.delete_mapping(index=self.index,
doc_type=self.doc_type,
Expand Down
7 changes: 4 additions & 3 deletions django_elasticsearch/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ class Meta:
abstract = True

class Elasticsearch:
abstract = False
index = getattr(settings, 'ELASTICSEARCH_DEFAULT_INDEX', 'django')
doc_type = None # defaults to 'model-{model.name}'
mapping = None
Expand Down Expand Up @@ -57,20 +58,20 @@ def add_es_manager(sender, **kwargs):

def es_save_callback(sender, instance, **kwargs):
# TODO: batch ?! @task ?!
if not issubclass(sender, EsIndexable):
if not issubclass(sender, EsIndexable) or sender.Elasticsearch.abstract:
return
instance.es.do_index()


def es_delete_callback(sender, instance, **kwargs):
if not issubclass(sender, EsIndexable):
if not issubclass(sender, EsIndexable) or sender.Elasticsearch.abstract:
return
instance.es.delete()


def es_syncdb_callback(sender, app, created_models, **kwargs):
for model in created_models:
if issubclass(model, EsIndexable):
if issubclass(model, EsIndexable) and not sender.Elasticsearch.abstract:
model.es.create_index()

if getattr(settings, 'ELASTICSEARCH_AUTO_INDEX', False):
Expand Down
Loading