From 8efc9db39864a6b26dda69e67cba3387425ce528 Mon Sep 17 00:00:00 2001 From: Eric Ouma Date: Tue, 28 Nov 2023 16:59:50 +0300 Subject: [PATCH 1/3] make data a cached property. gracefully handle missing records --- django_typesense/collections.py | 53 +++++++++++++++++++++++---------- 1 file changed, 37 insertions(+), 16 deletions(-) diff --git a/django_typesense/collections.py b/django_typesense/collections.py index dbfee43..f4a3485 100644 --- a/django_typesense/collections.py +++ b/django_typesense/collections.py @@ -87,16 +87,34 @@ def __init__( self.fields = self.get_fields() self._synonyms = [synonym().data for synonym in self.synonyms] - # TODO: Make self.data a cached_property - if data: - self.data = data - elif obj: - if many: - self.data = list(map(self._get_object_data, obj)) - else: - self.data = [self._get_object_data(obj)] + if data and obj: + raise Exception("'data' and 'obj' are mutually exclusive") + + self._data = data + self.many = many + self.obj = obj + + @cached_property + def data(self): + return self.get_data() + + def get_data(self): + if self._data: + return self._data + + if not self.obj: + return [] + + data = [] + if self.many: + for _obj in self.obj: + if obj_data := self._get_object_data(_obj): + data.append(obj_data) else: - self.data = [] + if obj_data := self._get_object_data(self.obj): + data.append(obj_data) + + return data @classmethod def get_fields(cls) -> Dict[str, TypesenseField]: @@ -191,8 +209,11 @@ def _get_object_data(self, obj): if self.update_fields: # we need the id for updates and a user can leave it out update_fields = set(self.fields.keys()).intersection(set(self.update_fields)) - update_fields.add('id') - fields = [self.get_field(field_name) for field_name in update_fields] + if update_fields: + update_fields.add('id') + fields = [self.get_field(field_name) for field_name in update_fields] + else: + fields = [] else: fields = self.fields.values() @@ -304,9 +325,9 @@ def _update_single_document(self, document): try: return client.collections[self.schema_name].documents[document_id].update(document) except ObjectNotFound: - self.create_typesense_collection() - document['id'] = document_id - return client.collections[self.schema_name].documents.upsert(document) + self.update_fields = [] + # we don't want the cached data + return client.collections[self.schema_name].documents.upsert(self.get_data()[0] ) def _update_multiple_documents(self, action_mode): try: @@ -314,9 +335,9 @@ def _update_multiple_documents(self, action_mode): self.data, {"action": action_mode} ) except ObjectNotFound: - self.create_typesense_collection() + # we don't want the cached data return client.collections[self.schema_name].documents.import_( - self.data, {"action": action_mode} + self.get_data(), {"action": action_mode} ) def create_or_update_synonyms(self): From bfbb7480c4a46e78ba08cd70d8c6b8b30a9284b7 Mon Sep 17 00:00:00 2001 From: Eric Ouma Date: Tue, 28 Nov 2023 17:26:21 +0300 Subject: [PATCH 2/3] update save signal to make use of update fields --- django_typesense/signals.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/django_typesense/signals.py b/django_typesense/signals.py index 4e1f53e..409e2b6 100644 --- a/django_typesense/signals.py +++ b/django_typesense/signals.py @@ -9,7 +9,7 @@ def post_save_typesense_models(sender, instance, **kwargs): if not issubclass(sender, TypesenseModelMixin): return - sender.get_collection(instance).update() + sender.get_collection(instance, update_fields=kwargs.get('update_fields', [])).update() @receiver(pre_delete) From dd3b7c31d349c8b1a4dccda295262448e8792798 Mon Sep 17 00:00:00 2001 From: Eric Ouma Date: Tue, 28 Nov 2023 17:36:35 +0300 Subject: [PATCH 3/3] update collections before running tests --- django_typesense/collections.py | 20 ++++++++++++++------ runtests.py | 3 +++ 2 files changed, 17 insertions(+), 6 deletions(-) diff --git a/django_typesense/collections.py b/django_typesense/collections.py index f4a3485..a9a7aae 100644 --- a/django_typesense/collections.py +++ b/django_typesense/collections.py @@ -208,9 +208,11 @@ def schema_fields(self) -> list: def _get_object_data(self, obj): if self.update_fields: # we need the id for updates and a user can leave it out - update_fields = set(self.fields.keys()).intersection(set(self.update_fields)) + update_fields = set(self.fields.keys()).intersection( + set(self.update_fields) + ) if update_fields: - update_fields.add('id') + update_fields.add("id") fields = [self.get_field(field_name) for field_name in update_fields] else: fields = [] @@ -275,7 +277,7 @@ def update_typesense_collection(self): field_changes.append(field) else: if field != existing_fields[field["name"]]: - field_changes.append({"name": field['name'], "drop": True}) + field_changes.append({"name": field["name"], "drop": True}) field_changes.append(field) if field_changes: @@ -320,14 +322,20 @@ def update(self, action_mode: str = "emplace"): return self._update_multiple_documents(action_mode) def _update_single_document(self, document): - document_id = document.pop('id') + document_id = document.pop("id") try: - return client.collections[self.schema_name].documents[document_id].update(document) + return ( + client.collections[self.schema_name] + .documents[document_id] + .update(document) + ) except ObjectNotFound: self.update_fields = [] # we don't want the cached data - return client.collections[self.schema_name].documents.upsert(self.get_data()[0] ) + return client.collections[self.schema_name].documents.upsert( + self.get_data()[0] + ) def _update_multiple_documents(self, action_mode): try: diff --git a/runtests.py b/runtests.py index 0701d3e..411ef5e 100644 --- a/runtests.py +++ b/runtests.py @@ -4,11 +4,14 @@ import django from django.conf import settings from django.test.utils import get_runner +from django.core.management import call_command os.environ.setdefault("DJANGO_SETTINGS_MODULE", "tests.settings") django.setup() +call_command("updatecollections") + TestRunner = get_runner(settings) test_runner = TestRunner() failures = test_runner.run_tests(["tests"])