From 63d6213272f5241a1b6d0666913bd3517a88bc3f Mon Sep 17 00:00:00 2001 From: Trenton Holmes <797416+stumpylog@users.noreply.github.com> Date: Thu, 2 Nov 2023 08:26:17 -0700 Subject: [PATCH] Saving some more work on this, even though it will fail linting --- Pipfile | 1 + Pipfile.lock | 10 +- src/documents/models.py | 9 -- src/documents/serialisers.py | 75 +++++++----- src/documents/tests/test_api_custom_fields.py | 107 ++++++++++++++---- 5 files changed, 140 insertions(+), 62 deletions(-) diff --git a/Pipfile b/Pipfile index 1b8d3a94a..876ff481c 100644 --- a/Pipfile +++ b/Pipfile @@ -16,6 +16,7 @@ django-extensions = "*" django-filter = "~=23.3" djangorestframework = "~=3.14" djangorestframework-guardian = "*" +drf-writable-nested = "*" filelock = "*" gunicorn = "*" imap-tools = "*" diff --git a/Pipfile.lock b/Pipfile.lock index 920c3b0f0..93cd8fa24 100644 --- a/Pipfile.lock +++ b/Pipfile.lock @@ -1,7 +1,7 @@ { "_meta": { "hash": { - "sha256": "7b4272de2042a346f3252ae20e7bbeee60c375381f59526caa35511a706d4977" + "sha256": "3c380d590439f008ec85f1d5821ed96b4ebd56fcee3f287e6e0a6f5923262229" }, "pipfile-spec": 6, "requires": {}, @@ -522,6 +522,14 @@ "index": "pypi", "version": "==0.3.0" }, + "drf-writable-nested": { + "hashes": [ + "sha256:154c0381e8a3a477e0fd539d5e1caf8ff4c1097a9c0c0fe741d4858b11b0455b" + ], + "index": "pypi", + "markers": "python_version >= '3.7'", + "version": "==0.7.0" + }, "exceptiongroup": { "hashes": [ "sha256:097acd85d473d75af5bb98e41b61ff7fe35efe6675e4f9370ec6ec5126d160e9", diff --git a/src/documents/models.py b/src/documents/models.py index 47586e060..28ee8d73a 100644 --- a/src/documents/models.py +++ b/src/documents/models.py @@ -1008,15 +1008,6 @@ class CustomFieldInstance(models.Model): return CustomFieldInteger raise NotImplementedError(self.field.data_type) - def to_json(self) -> dict[str, str]: - return { - "id": self.id, - "created": self.created, - "type": self.field.data_type, - "name": self.field.name, - "data": self.value, - } - @staticmethod def from_json( document: Document, diff --git a/src/documents/serialisers.py b/src/documents/serialisers.py index ee5f8272a..c2ac364bb 100644 --- a/src/documents/serialisers.py +++ b/src/documents/serialisers.py @@ -396,6 +396,9 @@ class StoragePathField(serializers.PrimaryKeyRelatedField): return StoragePath.objects.all() +from drf_writable_nested.serializers import NestedUpdateMixin + + class CustomFieldSerializer(serializers.ModelSerializer): data_type = serializers.ChoiceField( choices=CustomField.FieldDataType, @@ -411,21 +414,54 @@ class CustomFieldSerializer(serializers.ModelSerializer): ] -class CustomFieldInstanceSerializer(serializers.Serializer): - field = CustomFieldSerializer(required=True) - value = SerializerMethodField() +class CustomFieldOnUpdateSerializer(serializers.ModelSerializer): + id = serializers.PrimaryKeyRelatedField(queryset=CustomField.objects.all()) + + class Meta: + model = CustomField + fields = [ + "id", + ] + + +class CustomFieldInstanceSerializer(serializers.ModelSerializer): + field = serializers.PrimaryKeyRelatedField(queryset=CustomField.objects.all()) + value = serializers.JSONField() + + def create(self, validated_data): + print("hello from create") + from pprint import pprint + + pprint(dict(validated_data)) + document: Document = validated_data["document"] + custom_field: CustomField = validated_data["field"] + instance, _ = CustomFieldInstance.objects.get_or_create( + document=document, field=custom_field + ) + instance_data_class = instance.field_type + _, _ = instance_data_class.objects.update_or_create( + parent=instance, defaults={"value": validated_data["value"]} + ) + return instance + + def update(self, instance: CustomFieldInstance, validated_data): + print("hello from update") + from pprint import pprint + + pprint(validated_data) + pprint(instance.id) def get_value(self, obj: CustomFieldInstance): return obj.value class Meta: - fields = [ - "field", - "value", - ] + model = CustomFieldInstance + fields = ["value", "field"] -class DocumentSerializer(OwnedObjectSerializer, DynamicFieldsModelSerializer): +class DocumentSerializer( + OwnedObjectSerializer, NestedUpdateMixin, DynamicFieldsModelSerializer +): correspondent = CorrespondentField(allow_null=True) tags = TagsField(many=True) document_type = DocumentTypeField(allow_null=True) @@ -459,29 +495,6 @@ class DocumentSerializer(OwnedObjectSerializer, DynamicFieldsModelSerializer): return doc def update(self, instance: Document, validated_data): - if "custom_fields" in validated_data: - custom_fields = validated_data.pop("custom_fields") - for index, field_data in enumerate(custom_fields): - # get field value from initial_data since its not on the model - initial_field_data = self.initial_data["custom_fields"][index] - CustomFieldInstance.from_json( - document=instance, - field=initial_field_data["field"], - value=initial_field_data["value"], - ) - existing_fields = CustomFieldInstance.objects.filter(document=instance) - for existing_field in existing_fields: - if ( - not len( - [ - f - for f in self.initial_data["custom_fields"] - if f["field"]["id"] == existing_field.field.id - ], - ) - > 0 - ): - existing_field.delete() if "created_date" in validated_data and "created" not in validated_data: new_datetime = datetime.datetime.combine( validated_data.get("created_date"), diff --git a/src/documents/tests/test_api_custom_fields.py b/src/documents/tests/test_api_custom_fields.py index 6e8206d76..fd7b7724c 100644 --- a/src/documents/tests/test_api_custom_fields.py +++ b/src/documents/tests/test_api_custom_fields.py @@ -2,9 +2,10 @@ from django.contrib.auth.models import User from rest_framework import status from rest_framework.test import APITestCase -from documents.models import CustomField +from documents.models import CustomField, CustomFieldInstance, CustomFieldShortText from documents.models import Document from documents.tests.utils import DirectoriesMixin +from pprint import pprint class TestCustomField(DirectoriesMixin, APITestCase): @@ -80,41 +81,105 @@ class TestCustomField(DirectoriesMixin, APITestCase): data={ "custom_fields": [ { - "field": { - "id": custom_field_string.id, - }, + "field": custom_field_string.id, "value": "test value", }, { - "field": { - "id": custom_field_date.id, - }, + "field": custom_field_date.id, "value": "2023-10-31", }, { - "field": { - "id": custom_field_int.id, - }, - "value": 3, + "field": custom_field_int.id, + "value": "3", }, { - "field": { - "id": custom_field_boolean.id, - }, - "value": True, + "field": custom_field_boolean.id, + "value": "True", }, { - "field": { - "id": custom_field_url.id, - }, + "field": custom_field_url.id, "value": "https://example.com", }, ], }, format="json", ) + from pprint import pprint + + print("Response data") + pprint(resp.json()) + self.assertEqual(resp.status_code, status.HTTP_200_OK) + + resp_data = resp.json()["custom_fields"] + + self.assertCountEqual( + resp_data, + [ + {"field": custom_field_string.id, "value": "test value"}, + {"field": custom_field_date.id, "value": "2023-10-31"}, + {"field": custom_field_int.id, "value": 3}, + {"field": custom_field_boolean.id, "value": True}, + {"field": custom_field_url.id, "value": "https://example.com"}, + ], + ) + + doc.refresh_from_db() + self.assertEqual(len(doc.custom_fields.all()), 5) + for custom_field in doc.custom_fields.all(): + print(custom_field.value, type(custom_field.value)) + + def test_change_custom_field_instance_value(self): + doc = Document.objects.create( + title="WOW", + content="the content", + checksum="123", + mime_type="application/pdf", + ) + custom_field_string = CustomField.objects.create( + name="Test Custom Field String", + data_type=CustomField.FieldDataType.STRING, + ) + + self.assertEqual(CustomFieldInstance.objects.count(), 0) + self.assertEqual(CustomFieldShortText.objects.count(), 0) + + resp = self.client.patch( + f"/api/documents/{doc.id}/", + data={ + "custom_fields": [ + { + "field": custom_field_string.id, + "value": "test value", + }, + ], + }, + format="json", + ) + + self.assertEqual(resp.status_code, status.HTTP_200_OK) + + pprint(resp.json()) + + self.assertEqual(doc.custom_fields.first().value, "test value") + self.assertEqual(CustomFieldInstance.objects.count(), 1) + self.assertEqual(CustomFieldShortText.objects.count(), 1) + + resp = self.client.patch( + f"/api/documents/{doc.id}/", + data={ + "custom_fields": [ + { + "field": custom_field_string.id, + "value": "a new test value", + }, + ], + }, + format="json", + ) self.assertEqual(resp.status_code, status.HTTP_200_OK) - doc.refresh_from_db() - self.assertEqual(len(doc.custom_fields.all()), 5) - self.assertEqual(doc.custom_fields.first().value, "test value") + pprint(resp.json()) + + self.assertEqual(doc.custom_fields.first().value, "a new test value") + self.assertEqual(CustomFieldInstance.objects.count(), 1) + self.assertEqual(CustomFieldShortText.objects.count(), 1)