Custom fields
Custom fields let you add structured, typed metadata to documents in Paperless-ngx. pypaperless provides a rich API for reading and writing them.
Custom field types
Each custom field has a data_type which determines the type of its value:
CustomFieldType |
Python value type | Description |
|---|---|---|
STRING |
str |
Short text |
LONGTEXT |
str |
Long text / multi-line |
URL |
str |
URL string |
DATE |
datetime.date |
Date value |
BOOLEAN |
bool |
True / False |
INTEGER |
int |
Integer number |
FLOAT |
float |
Floating point number |
MONETARY |
str |
Currency amount, e.g. "EUR12.50" |
SELECT |
`int | Selection from predefined options |
DOCUMENT_LINK |
list[int] |
Links to other documents by ID |
The cache
To get typed CustomFieldValue instances (instead of plain CustomFieldValue), pypaperless needs to know the type of each field. This information comes from the custom fields cache.
Providing a cache
Call update_cache() before working with documents that have custom fields:
async with Paperless("localhost:8000", "your-api-token") as paperless:
await paperless.cache.update_custom_fields()
doc = await paperless.documents(42)
for item in doc.custom_fields:
print(type(item).__name__, item.field, item.value)
With the cache populated, doc.custom_fields yields typed instances like CustomFieldStringValue, CustomFieldIntegerValue, etc.
Without cache
Without the cache, every CustomFieldValue is of the base type and data_type will be None:
doc = await paperless.documents(42)
for item in doc.custom_fields:
# item is always CustomFieldValue, value is Any
print(item.field, item.value)
Reading custom field values
Checking if a field is present
# by field ID
if 8 in document.custom_fields:
print("Field 8 is set")
# by CustomField object
cf = await paperless.custom_fields(8)
if cf in document.custom_fields:
print("Present")
Iterating over all values
Getting a specific value
Use get() to retrieve a value by field ID or CustomField object. Raises ItemNotFoundError if not present:
Use default() to return None instead of raising when the field is absent:
Typed access
Both get() and default() accept an optional expected_type parameter to assert and return a more specific subclass:
from pypaperless.models.custom_fields import CustomFieldIntegerValue
value = document.custom_fields.get(8, CustomFieldIntegerValue)
print(value.value + 1) # IDE knows it's an int
A TypeError is raised if the actual type does not match expected_type.
Typed value subclasses
When the cache is active, pypaperless instantiates the right subclass automatically:
| Class | data_type |
value type |
|---|---|---|
CustomFieldStringValue |
STRING, LONGTEXT |
`str |
CustomFieldURLValue |
URL |
`str |
CustomFieldDateValue |
DATE |
`datetime.date |
CustomFieldBooleanValue |
BOOLEAN |
`bool |
CustomFieldIntegerValue |
INTEGER |
`int |
CustomFieldFloatValue |
FLOAT |
`float |
CustomFieldMonetaryValue |
MONETARY |
`str |
CustomFieldSelectValue |
SELECT |
`int |
CustomFieldDocumentLinkValue |
DOCUMENT_LINK |
`list[int] |
CustomFieldMonetaryValue extras
Monetary values provide convenience properties:
item = document.custom_fields.get(8, CustomFieldMonetaryValue)
print(item.currency) # "EUR"
print(item.amount) # 12.5
item.currency = "USD"
item.amount = 9.99
# item.value is now "USD9.99"
CustomFieldSelectValue extras
item = document.custom_fields.get(5, CustomFieldSelectValue)
print(item.label) # human-readable label of selected option
print(item.labels) # all available options as list[CustomFieldSelectOptions]
Writing custom field values
Drafting a value from a CustomField
The recommended way is to call draft_value() on a CustomField instance. With the cache active, it returns the correct typed subclass:
await paperless.cache.update_custom_fields()
cf = await paperless.custom_fields(8)
new_value = cf.draft_value(42)
# new_value is CustomFieldIntegerValue(field=8, value=42)
Adding a value to a document
Use += or add() to append to a document's custom field list:
document = await paperless.documents(42)
cf = await paperless.custom_fields(8)
new_value = cf.draft_value(100)
document.custom_fields += new_value
# or:
document.custom_fields.add(new_value)
await paperless.documents.update(document)
Removing a value from a document
Use -= or remove(). You can pass a CustomFieldValue, a CustomField or a plain field ID:
document.custom_fields -= new_value
# or:
document.custom_fields.remove(8) # by field id
document.custom_fields.remove(cf) # by CustomField object
await paperless.documents.update(document)
Creating a custom field
Use draft() and save() on paperless.custom_fields:
from pypaperless.models.custom_fields import CustomFieldType
draft = paperless.custom_fields.draft(
name="Invoice amount",
data_type=CustomFieldType.MONETARY,
)
new_id = await paperless.custom_fields.save(draft)
For SELECT fields, provide options in extra_data:
from pypaperless.models.custom_fields import (
CustomFieldExtraData,
CustomFieldSelectOptions,
CustomFieldType,
)
draft = paperless.custom_fields.draft(
name="Status",
data_type=CustomFieldType.SELECT,
extra_data=CustomFieldExtraData(
select_options=[
CustomFieldSelectOptions(id="1", label="Open"),
CustomFieldSelectOptions(id="2", label="Closed"),
]
),
)