paperap.models.abstract.model module

Define base model classes for Paperless-ngx API objects.

This module provides the foundation for all model classes in Paperap, implementing core functionality for serialization, validation, and API interactions. The models handle data mapping between Python objects and the Paperless-ngx API, with support for automatic saving, dirty tracking, and asynchronous operations.

The module contains two primary classes: - BaseModel: Abstract base class for all API objects - StandardModel: Extension of BaseModel for objects with ID fields

These classes are designed to be subclassed by specific resource models like Document, Tag, Correspondent, etc.

class paperap.models.abstract.model.ModelConfigType[source]

Bases: TypedDict

Define configuration options for Pydantic models.

This type definition specifies the configuration options used for all Pydantic models in the application, ensuring consistent behavior across all model classes.

populate_by_name

Allow population by field name as well as alias.

validate_assignment

Validate values when attributes are set.

validate_default

Validate default values during model initialization.

use_enum_values

Use enum values rather than enum instances.

extra

How to handle extra fields (ignore them).

arbitrary_types_allowed

Allow arbitrary types in model fields.

populate_by_name: bool
validate_assignment: bool
validate_default: bool
use_enum_values: bool
extra: Literal['ignore']
arbitrary_types_allowed: bool
class paperap.models.abstract.model.BaseModel(**data)[source]

Bases: BaseModel, ABC

Base model for all Paperless-ngx API objects.

Provide automatic serialization, deserialization, and API interactions with minimal configuration. This abstract class serves as the foundation for all models in the Paperap library, handling data validation, dirty tracking, and API communication.

_meta

Metadata for the model, including filtering and resource information.

_save_lock

Lock for saving operations to prevent race conditions.

_pending_save

Future object for pending save operations.

_save_executor

Executor for asynchronous save operations.

_status

Current status of the model (INITIALIZING, READY, UPDATING, SAVING).

_original_data

Original data from the server for dirty checking.

_saved_data

Data last sent to the database during save operations.

_resource

Associated resource for API interactions.

Raises:

ValueError – If resource is not provided during initialization.

Examples

Models are typically accessed through the client interface:

>>> document = client.documents.get(123)
>>> print(document.title)
>>> document.title = "New Title"
>>> document.save()
Parameters:

data (Any)

class Meta(model)[source]

Bases: Generic

Metadata for the Model.

Define model behavior, filtering capabilities, and API interaction rules. Each model class has its own Meta instance that controls how the model interacts with the Paperless-ngx API.

model

Reference to the model class this metadata belongs to.

name

The name of the model, used in API paths and error messages.

read_only_fields

Fields that should not be modified by the client.

filtering_disabled

Fields that cannot be used for filtering.

filtering_fields

Fields allowed for filtering operations.

supported_filtering_params

Specific filter parameters allowed during queryset filtering (e.g., “content__icontains”, “id__gt”).

blacklist_filtering_params

Filter parameters explicitly disallowed.

filtering_strategies

Strategies that determine how filtering is handled (ALLOW_ALL, ALLOW_NONE, WHITELIST, BLACKLIST).

field_map

Map of API field names to model attribute names.

save_on_write

If True, updating attributes triggers automatic save. If None, follows client.settings.save_on_write.

save_timeout

Timeout in seconds for save operations.

Raises:

ValueError – If both ALLOW_ALL and ALLOW_NONE filtering strategies are set, which would create contradictory behavior.

Examples

Defining a custom Meta for a model:

>>> class Document(StandardModel):
>>>     class Meta(StandardModel.Meta):
>>>         read_only_fields = {"content", "checksum"}
>>>         filtering_strategies = {FilteringStrategies.WHITELIST}
>>>         supported_filtering_params = {"title__icontains", "created__gt"}
Parameters:

model (type[_Self])

name: str
read_only_fields: ClassVar[set[str]] = {}
filtering_disabled: ClassVar[set[str]] = {}
filtering_fields: ClassVar[set[str]] = {}
supported_filtering_params: ClassVar[set[str]] = {'limit'}
blacklist_filtering_params: ClassVar[set[str]] = {}
filtering_strategies: ClassVar[set[FilteringStrategies]] = {FilteringStrategies.BLACKLIST}
field_map: dict[str, str] = {}
save_on_write: bool | None = None
save_timeout: int = ModelPrivateAttr(default=60)
__init__(model)[source]
Parameters:

model (type[_Self])

model: type[TypeVar(_Self, bound= BaseModel)]
filter_allowed(filter_param)[source]

Check if a filter parameter is allowed based on the filtering strategies.

Evaluate whether a given filter parameter can be used with this model based on the configured filtering strategies and rules. This method implements the filtering logic defined by the model’s filtering_strategies.

Parameters:

filter_param (str) – The filter parameter to check (e.g., “title__contains”).

Returns:

True if the filter is allowed, False otherwise.

Return type:

bool

Examples

>>> meta.filter_allowed("title__contains")
True
>>> meta.filter_allowed("content__exact")
False  # If content is in filtering_disabled
classmethod __init_subclass__(**kwargs)[source]

Initialize subclass and set up metadata.

Ensure that each subclass has its own Meta definition and properly inherits metadata attributes from parent classes. This method handles the automatic creation and configuration of model metadata.

Parameters:

**kwargs (Any) – Additional keyword arguments passed to parent __init_subclass__.

Raises:

ConfigurationError – If no Meta class is found in the class hierarchy.

Return type:

None

Notes

This method automatically: - Creates a Meta class for the subclass if not explicitly defined - Inherits and merges metadata from parent classes - Initializes the _meta instance for the subclass

model_config: ClassVar[ConfigDict] = {'arbitrary_types_allowed': True, 'extra': 'ignore', 'populate_by_name': True, 'use_enum_values': True, 'validate_assignment': True, 'validate_default': True}

Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].

__init__(**data)[source]

Initialize the model with resource and data.

Set up the model with the provided resource and initialize it with field values from the API response or user input.

Parameters:

**data (Any) – Field values to initialize the model with.

Raises:

ValueError – If resource is not provided or properly initialized.

Notes

Models should typically be created through their resource’s methods rather than directly instantiated.

property resource: BaseResource[Self]

Get the resource associated with this model.

Provide access to the resource instance that handles API interactions for this model type, such as retrieving, creating, updating, and deleting objects.

Returns:

The resource instance for this model type.

Return type:

BaseResource[Self]

property save_executor: ThreadPoolExecutor

Get the thread pool executor for asynchronous save operations.

Provide access to the thread pool that handles asynchronous save operations, creating a new executor if one doesn’t exist yet.

Returns:

The executor for handling

asynchronous save operations.

Return type:

concurrent.futures.ThreadPoolExecutor

cleanup()[source]

Clean up resources used by the model.

Shut down the save executor to release resources. Call this method when the model is no longer needed to prevent resource leaks.

Return type:

None

model_post_init(context: Any, /) None

We need to both initialize private attributes and call the user-defined model_post_init method.

Parameters:
Return type:

None

classmethod from_dict(data)[source]

Create a model instance from API response data.

Instantiate a model from a dictionary of API response data, handling field mapping and type conversion through the resource’s parse_to_model method.

Parameters:

data (dict[str, Any]) – Dictionary containing the API response data.

Returns:

A model instance initialized with the provided data.

Return type:

Self

Examples

>>> api_data = {"id": 123, "title": "Invoice", "created": "2023-01-01T00:00:00Z"}
>>> doc = Document.from_dict(api_data)
>>> print(doc.id, doc.title)
123 Invoice
to_dict(*, include_read_only=True, exclude_none=False, exclude_unset=True)[source]

Convert the model to a dictionary for API requests.

Prepare the model data for submission to the API, with options to control which fields are included based on their properties and values.

Parameters:
  • include_read_only (bool) – Whether to include read-only fields in the output. Set to False when preparing data for update operations.

  • exclude_none (bool) – Whether to exclude fields with None values.

  • exclude_unset (bool) – Whether to exclude fields that were not explicitly set. Useful for partial updates.

Returns:

A dictionary with model data ready for API submission.

Return type:

dict[str, Any]

Examples

>>> # Full representation including all fields
>>> data = doc.to_dict()
>>>
>>> # Only include fields that can be modified
>>> update_data = doc.to_dict(include_read_only=False)
>>>
>>> # Only include fields that have been explicitly set
>>> partial_data = doc.to_dict(exclude_unset=True)
dirty_fields(comparison='both')[source]

Show which fields have changed since last update from the Paperless NGX database.

Compare the current model data with the last saved or retrieved data to identify changes. This method helps determine what will be sent to the server on the next save operation.

Parameters:

comparison (Literal['saved', 'db', 'both']) – Specify the data to compare against: - “saved”: Compare against the last data sent to Paperless NGX - “db”: Compare against the last data retrieved from Paperless NGX - “both”: Compare against both saved and db data (default)

Returns:

A dictionary mapping field names to tuples of

(original_value, current_value) for all fields that have changed.

Return type:

dict[str, tuple[Any, Any]]

Examples

>>> doc = client.documents.get(123)
>>> doc.title = "New Title"
>>> doc.dirty_fields()
{'title': ('Original Title', 'New Title')}
is_dirty(comparison='both')[source]

Check if any field has changed since last update from the Paperless NGX database.

Determine if the model has unsaved changes by comparing current data with the last saved or retrieved data. New models are always considered dirty.

Parameters:

comparison (Literal['saved', 'db', 'both']) – Specify the data to compare against: - “saved”: Compare against the last data sent to Paperless NGX - “db”: Compare against the last data retrieved from Paperless NGX - “both”: Compare against both saved and db data (default)

Returns:

True if any field has changed, False otherwise.

Return type:

bool

Examples

>>> doc = client.documents.get(123)
>>> doc.is_dirty()
False
>>> doc.title = "New Title"
>>> doc.is_dirty()
True
classmethod create(**kwargs)[source]

Create a new model instance and save it to the server.

Create a new instance of the model with the specified field values and immediately save it to the Paperless NGX server. This is a convenience method that delegates to the resource’s create method.

Parameters:

**kwargs (Any) – Field values to set on the new model instance.

Returns:

A new model instance that has been saved to the server.

Return type:

Self

Examples

>>> tag = Tag.create(name="Invoices", color="#ff0000")
>>> correspondent = Correspondent.create(name="Electric Company")
>>> doc_type = DocumentType.create(name="Bill")
delete()[source]

Delete this model from the Paperless NGX server.

Remove the model from the server. After calling this method, the model instance should not be used anymore as it no longer represents a valid server object.

Raises:
Return type:

None

update_locally(*, from_db=None, skip_changed_fields=False, **kwargs)[source]

Update model attributes without triggering automatic save.

Update the model’s attributes with the provided values without sending changes to the server, regardless of the save_on_write setting. This is useful for local modifications or when applying server updates.

Parameters:
  • from_db (bool | None) – Whether the update is from the database. If True, resets the dirty tracking to consider the model clean after the update.

  • skip_changed_fields (bool) – Whether to skip updating fields that have unsaved changes. Useful when merging updates from the server with local changes.

  • **kwargs (Any) – Field values to update.

Raises:

ReadOnlyFieldError – If attempting to change a read-only field when from_db is False.

Return type:

None

Examples

>>> doc = client.documents.get(123)
>>> # Update without saving to server
>>> doc.update_locally(title="New Title", correspondent_id=5)
>>> # Update from server data
>>> doc.update_locally(from_db=True, **server_data)
update(**kwargs)[source]

Update this model with new values.

Update the model with the provided field values. In BaseModel, this simply calls update_locally without saving. Subclasses (like StandardModel) may implement automatic saving.

Parameters:

**kwargs (Any) – New field values to set on the model.

Return type:

None

Examples

>>> model.update(name="New Name", description="Updated description")
abstractmethod is_new()[source]

Check if this model represents a new (unsaved) object.

Determine if the model has been saved to the server. Subclasses must implement this method, typically by checking if the model has a valid ID or other server-assigned identifier.

Returns:

True if the model is new (not yet saved), False otherwise.

Return type:

bool

Examples

>>> doc = Document.create(title="New Document")
>>> doc.is_new()  # Returns False after creation
>>>
>>> # When creating a model instance manually:
>>> doc = Document(title="Draft Document")
>>> doc.is_new()  # Returns True
should_save_on_write()[source]

Check if the model should save on attribute write.

Determine if changes to model attributes should trigger an automatic save operation based on configuration settings. This method considers both the model’s meta settings and the client settings, with the model’s setting taking precedence.

Returns:

True if the model should save on write, False otherwise.

Return type:

bool

enable_save_on_write()[source]

Enable automatic saving on attribute write.

Set the model’s meta configuration to allow automatic saving whenever an attribute is modified, overriding the client’s default setting. This affects only this specific model instance. :rtype: None

Examples

>>> doc = client.documents.get(123)
>>> doc.enable_save_on_write()
>>> doc.title = "New Title"  # This will trigger an automatic save
disable_save_on_write()[source]

Disable automatic saving on attribute write.

Set the model’s meta configuration to prevent automatic saving whenever an attribute is modified, overriding the client’s default setting. This affects only this specific model instance. :rtype: None

Examples

>>> doc = client.documents.get(123)
>>> doc.disable_save_on_write()
>>> doc.title = "New Title"  # This won't trigger an automatic save
>>> doc.save()  # Manual save required
matches_dict(data)[source]

Check if the model matches the provided data.

Compare the model’s current data with a given dictionary to determine if they are equivalent. This is useful for checking if a model needs to be updated based on new data from the server.

Parameters:

data (dict[str, Any]) – Dictionary containing the data to compare against.

Returns:

True if the model matches the data, False otherwise.

Return type:

bool

Examples

>>> doc = client.documents.get(123)
>>> new_data = {"id": 123, "title": "Invoice", "correspondent_id": 5}
>>> doc.matches_dict(new_data)
False  # If any values differ
__str__()[source]

Human-readable string representation.

Provide a string representation of the model that includes the model type and ID, typically used for logging and debugging purposes.

Returns:

A string representation of the model (e.g., “Document #123”).

Return type:

str

class paperap.models.abstract.model.StandardModel(**data)[source]

Bases: BaseModel, ABC

Standard model for Paperless-ngx API objects with an ID field.

Extend BaseModel to include a unique identifier and additional functionality for API objects that require an ID. Most Paperless-ngx resources are represented by StandardModel subclasses.

This class adds functionality for: - Tracking whether an object is new or existing - Automatic saving of changes to the server - Refreshing data from the server - Synchronous and asynchronous save operations

id

Unique identifier for the model from Paperless-ngx.

_resource

Associated resource for API interactions.

Examples

StandardModel subclasses are typically accessed through the client:

>>> doc = client.documents.get(123)
>>> tag = client.tags.create(name="Important")
>>> correspondent = client.correspondents.all()[0]
Parameters:

data (Any)

id: int
class Meta(model)[source]

Bases: Meta

Metadata for the StandardModel.

Define metadata specific to StandardModel, including read-only fields and filtering parameters common to all standard Paperless-ngx resources.

read_only_fields

Fields that should not be modified, including the ‘id’ field which is set by the server.

supported_filtering_params

Common filtering parameters supported for all standard models, including id-based lookups.

Parameters:

model (type[_Self])

read_only_fields: ClassVar[set[str]] = {'id'}
supported_filtering_params: ClassVar[set[str]] = {'id', 'id__in', 'limit'}
blacklist_filtering_params: ClassVar[set[str]] = {}
field_map: dict[str, str] = {}
filtering_disabled: ClassVar[set[str]] = {}
filtering_fields: ClassVar[set[str]] = {'_resource', 'id'}
model: type[_Self]
name: str
property resource: StandardResource[Self]

Get the resource associated with this model.

Provide access to the StandardResource instance that handles API interactions for this model type, with support for ID-based operations.

Returns:

The resource instance for this model type.

Return type:

StandardResource[Self]

update(**kwargs)[source]

Update this model with new values and save changes.

Update the model with the provided field values and automatically save the changes to the server if the model is not new. This method combines update_locally and save for convenience.

Parameters:

**kwargs (Any) – New field values to set on the model.

Return type:

None

Note

New (unsaved) instances will be updated locally but not saved automatically. Use create() to save new instances.

Examples

>>> doc = client.documents.get(123)
>>> doc.update(title="New Title", correspondent_id=5)
>>> # Changes are immediately saved to the server
refresh()[source]

Refresh the model with the latest data from the server.

Retrieve the latest data for the model from the server and update the model instance with any changes. This is useful when you suspect the server data may have changed due to actions by other users or automated processes.

Returns:

True if the model data changed, False if the data is identical

or the refresh failed.

Return type:

bool

Raises:

ResourceNotFoundError – If the model is not found on the server (e.g., it was deleted remotely).

Examples

>>> doc = client.documents.get(123)
>>> # After some time or operations by other users
>>> doc.refresh()  # Update with latest data from server
save(*, force=False)[source]

Save this model to the Paperless NGX server.

Send the current model state to the server, creating a new object or updating an existing one. This is a convenience method that calls save_sync.

Parameters:

force (bool) – Whether to force the save operation even if the model is not dirty or is already saving.

Returns:

True if the save was successful, False otherwise.

Return type:

bool

Examples

>>> doc = client.documents.get(123)
>>> doc.title = "New Title"
>>> doc.save()
>>>
>>> # Force save even if no changes
>>> doc.save(force=True)
save_sync(*, force=False)[source]

Save this model instance synchronously.

Send changes to the server immediately and update the model when the server responds. This method blocks until the save operation is complete.

Parameters:

force (bool) – Whether to force the save operation even if the model is not dirty or is already saving.

Returns:

True if the save was successful, False otherwise.

Return type:

bool

Raises:

Examples

>>> doc = client.documents.get(123)
>>> doc.title = "New Title"
>>> success = doc.save_sync()
>>> print(f"Save {'succeeded' if success else 'failed'}")
save_async(*, force=False)[source]

Save this model instance asynchronously.

Send changes to the server in a background thread, allowing other operations to continue while waiting for the server response. The model will be updated with the server’s response when the save completes.

Parameters:

force (bool) – Whether to force the save operation even if the model is not dirty or is already saving.

Returns:

True if the save was successfully submitted to the background

thread, False otherwise (e.g., if there are no changes to save).

Return type:

bool

Examples

>>> doc = client.documents.get(123)
>>> doc.title = "New Title"
>>> # Continue execution immediately while save happens in background
>>> doc.save_async()
>>> # Do other work...
is_new()[source]

Check if this model represents a new (unsaved) object.

Determine if the model has been saved to the server by checking if it has a valid ID (non-zero). StandardModel implements this method by checking the id attribute.

Returns:

True if the model is new (not yet saved), False otherwise.

Return type:

bool

Examples

>>> doc = Document(title="Draft")  # No ID yet
>>> doc.is_new()
True
>>> saved_doc = client.documents.get(123)
>>> saved_doc.is_new()
False
__setattr__(name, value)[source]

Override attribute setting to automatically trigger save.

Intercept attribute assignments and trigger an automatic save operation if appropriate based on the save_on_write setting. This enables the “save on write” functionality that makes the model automatically sync changes to the server.

Parameters:
  • name (str) – Attribute name to set

  • value (Any) – New attribute value

Return type:

None

Notes

  • Private attributes (starting with ‘_’) never trigger autosave

  • Autosave only happens when model status is READY

  • Autosave is skipped for new models or when save_on_write is False

__str__()[source]

Human-readable string representation.

This method returns a string representation of the model, typically used for logging and debugging purposes.

Returns:

A string representation of the model.

Return type:

str

model_config: ClassVar[ConfigDict] = {'arbitrary_types_allowed': True, 'extra': 'ignore', 'populate_by_name': True, 'use_enum_values': True, 'validate_assignment': True, 'validate_default': True}

Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].

model_post_init(context: Any, /) None

We need to both initialize private attributes and call the user-defined model_post_init method.

Parameters:
Return type:

None