mirror of
https://github.com/paperless-ngx/paperless-ngx.git
synced 2025-05-01 11:19:32 -05:00
Automatically prune removed options from existing instances
This commit is contained in:
parent
233212d198
commit
e08fd79265
@ -34,7 +34,7 @@ from documents.settings import EXPORTER_ARCHIVE_NAME
|
||||
from documents.settings import EXPORTER_CRYPTO_SETTINGS_NAME
|
||||
from documents.settings import EXPORTER_FILE_NAME
|
||||
from documents.settings import EXPORTER_THUMBNAIL_NAME
|
||||
from documents.signals.handlers import update_cf_instance_documents
|
||||
from documents.signals.handlers import check_paths_and_prune_custom_fields
|
||||
from documents.signals.handlers import update_filename_and_move_files
|
||||
from documents.utils import copy_file_with_basic_stats
|
||||
from paperless import version
|
||||
@ -262,7 +262,7 @@ class Command(CryptMixin, BaseCommand):
|
||||
),
|
||||
disable_signal(
|
||||
post_save,
|
||||
receiver=update_cf_instance_documents,
|
||||
receiver=check_paths_and_prune_custom_fields,
|
||||
sender=CustomField,
|
||||
),
|
||||
):
|
||||
|
@ -367,21 +367,6 @@ class CannotMoveFilesException(Exception):
|
||||
pass
|
||||
|
||||
|
||||
# should be disabled in /src/documents/management/commands/document_importer.py handle
|
||||
@receiver(models.signals.post_save, sender=CustomField)
|
||||
def update_cf_instance_documents(sender, instance: CustomField, **kwargs):
|
||||
"""
|
||||
'Select' custom field instances get their end-user value (e.g. in file names) from the select_options in extra_data,
|
||||
which is contained in the custom field itself. So when the field is changed, we (may) need to update the file names
|
||||
of all documents that have this custom field.
|
||||
"""
|
||||
if (
|
||||
instance.data_type == CustomField.FieldDataType.SELECT
|
||||
): # Only select fields, for now
|
||||
for cf_instance in instance.fields.all():
|
||||
update_filename_and_move_files(sender, cf_instance)
|
||||
|
||||
|
||||
# should be disabled in /src/documents/management/commands/document_importer.py handle
|
||||
@receiver(models.signals.post_save, sender=CustomFieldInstance)
|
||||
@receiver(models.signals.m2m_changed, sender=Document.tags.through)
|
||||
@ -520,6 +505,34 @@ def update_filename_and_move_files(
|
||||
)
|
||||
|
||||
|
||||
# should be disabled in /src/documents/management/commands/document_importer.py handle
|
||||
@receiver(models.signals.post_save, sender=CustomField)
|
||||
def check_paths_and_prune_custom_fields(sender, instance: CustomField, **kwargs):
|
||||
"""
|
||||
When a custom field is updated:
|
||||
1. 'Select' custom field instances get their end-user value (e.g. in file names) from the select_options in extra_data,
|
||||
which is contained in the custom field itself. So when the field is changed, we (may) need to update the file names
|
||||
of all documents that have this custom field.
|
||||
2. If a 'Select' field option was removed, we need to nullify the custom field instances that have the option.
|
||||
"""
|
||||
if (
|
||||
instance.data_type == CustomField.FieldDataType.SELECT
|
||||
): # Only select fields, for now
|
||||
for cf_instance in instance.fields.all():
|
||||
options = instance.extra_data.get("select_options", [])
|
||||
try:
|
||||
next(
|
||||
option["label"]
|
||||
for option in options
|
||||
if option["id"] == cf_instance.value
|
||||
)
|
||||
except StopIteration:
|
||||
# The value of this custom field instance is not in the select options anymore
|
||||
cf_instance.value_select = None
|
||||
cf_instance.save()
|
||||
update_filename_and_move_files(sender, cf_instance)
|
||||
|
||||
|
||||
def set_log_entry(sender, document: Document, logging_group=None, **kwargs):
|
||||
ct = ContentType.objects.get(model="document")
|
||||
user = User.objects.get(username="consumer")
|
||||
|
@ -209,6 +209,69 @@ class TestCustomFieldsAPI(DirectoriesMixin, APITestCase):
|
||||
],
|
||||
)
|
||||
|
||||
def test_custom_field_select_options_pruned(self):
|
||||
"""
|
||||
GIVEN:
|
||||
- Select custom field exists and document instance with one of the options
|
||||
WHEN:
|
||||
- API request to remove an option from the select field
|
||||
THEN:
|
||||
- The option is removed from the field
|
||||
- The option is removed from the document instance
|
||||
"""
|
||||
custom_field_select = CustomField.objects.create(
|
||||
name="Select Field",
|
||||
data_type=CustomField.FieldDataType.SELECT,
|
||||
extra_data={
|
||||
"select_options": [
|
||||
{"label": "Option 1", "id": "abc-123"},
|
||||
{"label": "Option 2", "id": "def-456"},
|
||||
{"label": "Option 3", "id": "ghi-789"},
|
||||
],
|
||||
},
|
||||
)
|
||||
|
||||
doc = Document.objects.create(
|
||||
title="WOW",
|
||||
content="the content",
|
||||
checksum="123",
|
||||
mime_type="application/pdf",
|
||||
)
|
||||
CustomFieldInstance.objects.create(
|
||||
document=doc,
|
||||
field=custom_field_select,
|
||||
value_text="abc-123",
|
||||
)
|
||||
|
||||
resp = self.client.patch(
|
||||
f"{self.ENDPOINT}{custom_field_select.id}/",
|
||||
json.dumps(
|
||||
{
|
||||
"extra_data": {
|
||||
"select_options": [
|
||||
{"label": "Option 1", "id": "abc-123"},
|
||||
{"label": "Option 3", "id": "ghi-789"},
|
||||
],
|
||||
},
|
||||
},
|
||||
),
|
||||
content_type="application/json",
|
||||
)
|
||||
self.assertEqual(resp.status_code, status.HTTP_200_OK)
|
||||
|
||||
data = resp.json()
|
||||
|
||||
self.assertCountEqual(
|
||||
data["extra_data"]["select_options"],
|
||||
[
|
||||
{"label": "Option 1", "id": "abc-123"},
|
||||
{"label": "Option 3", "id": "ghi-789"},
|
||||
],
|
||||
)
|
||||
|
||||
doc.refresh_from_db()
|
||||
self.assertEqual(doc.custom_fields.first().value, None)
|
||||
|
||||
def test_create_custom_field_monetary_validation(self):
|
||||
"""
|
||||
GIVEN:
|
||||
|
Loading…
x
Reference in New Issue
Block a user