Use a custom filter to work around access to fields with spaces

This commit is contained in:
Trenton H 2024-10-03 11:44:05 -07:00
parent 6d8ffa3382
commit dc25d53af4
5 changed files with 54 additions and 24 deletions

View File

@ -438,6 +438,10 @@ with more complex logic.
- `{{ tag_name_list }}`: A list of tag names applied to the document, ordered by the tag name. Note this is a list, not a single string - `{{ tag_name_list }}`: A list of tag names applied to the document, ordered by the tag name. Note this is a list, not a single string
- `{{ custom_fields }}`: A mapping of custom field names to their type and value. A user can access the mapping by field name or check if a field is applied by checking its existence in the variable. - `{{ custom_fields }}`: A mapping of custom field names to their type and value. A user can access the mapping by field name or check if a field is applied by checking its existence in the variable.
!!! tip
To access a custom field which has a space in the name, use the `get_cf_value` filter. See the examples below.
#### Examples #### Examples
This example will construct a path based on the archive serial number range: This example will construct a path based on the archive serial number range:
@ -486,6 +490,12 @@ To use custom fields:
If the document has a custom field named "Invoice" with a value of 123, it would be filed into the `invoices/123.pdf`, but a document without the custom field If the document has a custom field named "Invoice" with a value of 123, it would be filed into the `invoices/123.pdf`, but a document without the custom field
would be filed to `not-invoices/Title.pdf` would be filed to `not-invoices/Title.pdf`
If the custom field is named "Invoice Number", you would access the value of it via the `get_cf_value` filter due to quirks of the Django Template Language:
```django
"invoices/{{ custom_fields|get_cf_value:'Invoice Number' }}"
```
## Automatic recovery of invalid PDFs {#pdf-recovery} ## Automatic recovery of invalid PDFs {#pdf-recovery}
Paperless will attempt to "clean" certain invalid PDFs with `qpdf` before processing if, for example, the mime_type Paperless will attempt to "clean" certain invalid PDFs with `qpdf` before processing if, for example, the mime_type

View File

@ -26,7 +26,10 @@ INVALID_VARIABLE_STR = "InvalidVarError"
filepath_engine = Engine( filepath_engine = Engine(
autoescape=False, autoescape=False,
string_if_invalid=f"{INVALID_VARIABLE_STR}: %s", string_if_invalid=f"{INVALID_VARIABLE_STR}: %s",
libraries={"filepath": "documents.templatetags.filepath"}, libraries={
"filepath": "documents.templatetags.filepath",
"get_cf_value": "documents.templatetags.get_cf_value",
},
) )
@ -330,7 +333,9 @@ def validate_template_and_render(
try: try:
# We load the custom tag used to remove spaces and newlines from the final string around the user string # We load the custom tag used to remove spaces and newlines from the final string around the user string
template = filepath_engine.from_string( template = filepath_engine.from_string(
"{% load filepath %}{% filepath %}" + template_string + "{% endfilepath %}", "{% load filepath %}{% load get_cf_value %}{% filepath %}"
+ template_string
+ "{% endfilepath %}",
) )
rendered_template = template.render(Context(context)) rendered_template = template.render(Context(context))

View File

@ -54,7 +54,7 @@ from documents.models import WorkflowTrigger
from documents.parsers import is_mime_type_supported from documents.parsers import is_mime_type_supported
from documents.permissions import get_groups_with_only_permission from documents.permissions import get_groups_with_only_permission
from documents.permissions import set_permissions_for_object from documents.permissions import set_permissions_for_object
from documents.templatetags import convert_to_django_template_format from documents.templatetags.filepath import convert_to_django_template_format
from documents.validators import uri_validator from documents.validators import uri_validator
logger = logging.getLogger("paperless.serializers") logger = logging.getLogger("paperless.serializers")

View File

@ -0,0 +1,12 @@
from django import template
register = template.Library()
@register.filter("get_cf_value")
def get_cf_value(custom_field_data: dict[str, dict[str, str]], name: str):
"""
See https://stackoverflow.com/questions/2970244/django-templates-value-of-dictionary-key-with-a-space-in-it/2970337#2970337
"""
data = custom_field_data[name]["value"]
return data

View File

@ -1245,17 +1245,6 @@ class TestFilenameGeneration(DirectoriesMixin, TestCase):
) )
def test_template_with_custom_fields(self): def test_template_with_custom_fields(self):
sp = StoragePath.objects.create(
name="sp1",
path="""
{% if "Invoice" in custom_fields %}
invoices/{{ custom_fields.Invoice.value }}
{% else %}
not-invoices/{{ title }}
{% endif %}
""",
)
doc_a = Document.objects.create( doc_a = Document.objects.create(
title="Some Title", title="Some Title",
created=timezone.make_aware(datetime.datetime(2020, 6, 25, 7, 36, 51, 153)), created=timezone.make_aware(datetime.datetime(2020, 6, 25, 7, 36, 51, 153)),
@ -1264,7 +1253,6 @@ class TestFilenameGeneration(DirectoriesMixin, TestCase):
pk=2, pk=2,
checksum="2", checksum="2",
archive_serial_number=25, archive_serial_number=25,
storage_path=sp,
) )
cf = CustomField.objects.create( cf = CustomField.objects.create(
@ -1278,16 +1266,31 @@ class TestFilenameGeneration(DirectoriesMixin, TestCase):
value_int=1234, value_int=1234,
) )
with override_settings(
FILENAME_FORMAT="""
{% if "Invoice" in custom_fields %}
invoices/{{ custom_fields|get_cf_value:'Invoice' }}
{% else %}
not-invoices/{{ title }}
{% endif %}
""",
):
self.assertEqual( self.assertEqual(
generate_filename(doc_a), generate_filename(doc_a),
"invoices/1234.pdf", "invoices/1234.pdf",
) )
cfi.delete() cf.name = "Invoice Number"
cfi.value_int = 4567
cfi.save()
cf.save()
with override_settings(
FILENAME_FORMAT="invoices/{{ custom_fields|get_cf_value:'Invoice Number' }}",
):
self.assertEqual( self.assertEqual(
generate_filename(doc_a), generate_filename(doc_a),
"not-invoices/Some Title.pdf", "invoices/4567.pdf",
) )
def test_using_other_filters(self): def test_using_other_filters(self):